Security headers

Six security headers applied to every response via proxy.ts.

Where they are set

All security headers are set in proxy.ts on every response using NextResponse.next() with custom headers. This ensures every page, API route, and static file response includes the headers.

proxy.ts
export function proxy(request: NextRequest) {
  const response = NextResponse.next()

  response.headers.set('X-Frame-Options', 'DENY')
  response.headers.set('X-Content-Type-Options', 'nosniff')
  response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')
  response.headers.set(
    'Permissions-Policy',
    'camera=(), microphone=(), geolocation=()'
  )
  response.headers.set(
    'Content-Security-Policy',
    "default-src 'self'; script-src 'self' 'unsafe-inline'; " +
    "style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; " +
    "font-src 'self' data:"
  )

  if (process.env.NODE_ENV === 'production') {
    response.headers.set(
      'Strict-Transport-Security',
      'max-age=31536000; includeSubDomains'
    )
  }

  return response
}

Full header list

HeaderValuePurpose
X-Frame-OptionsDENYPrevents clickjacking - disallows the page being embedded in iframes
X-Content-Type-OptionsnosniffPrevents MIME type sniffing - browser uses declared content-type only
Referrer-Policystrict-origin-when-cross-originLimits referrer info sent to third-party sites
Permissions-Policycamera=(), microphone=(), geolocation=()Disables sensitive browser APIs
Content-Security-Policysee belowRestricts what resources the page can load
Strict-Transport-Securitymax-age=31536000; includeSubDomainsForces HTTPS for 1 year (production only)

Content-Security-Policy

The CSP header tells the browser which sources are allowed for each resource type. The NextForge default:

  • default-src 'self' - all resource types default to same-origin only
  • script-src 'self' 'unsafe-inline' - inline scripts allowed (needed for Next.js)
  • style-src 'self' 'unsafe-inline' - inline styles allowed (needed for Tailwind)
  • img-src 'self' data: https: - images from same origin, data URIs, and any HTTPS source
  • font-src 'self' data: - fonts from same origin and data URIs

HSTS

Strict-Transport-Security is only set in production (process.env.NODE_ENV === 'production'). Setting HSTS in development causes browsers to refuse HTTP connections to localhost permanently - which breaks local development.

The max-age=31536000 value sets the policy for 1 year. includeSubDomains extends it to all subdomains.

Customising

Add, remove, or tighten headers in proxy.ts:

proxy.ts
// Remove Permissions-Policy if you need geolocation:
// response.headers.set('Permissions-Policy', 'geolocation=(self)')

// Tighten CSP to disallow all inline scripts (requires nonce implementation):
// response.headers.set(
//   'Content-Security-Policy',
//   "default-src 'self'; script-src 'self'; style-src 'self'"
// )