6 min read

Security headers in Next.js — HSTS, CSP, frame-ancestors, complete config

Vercel doesn't set security headers by default. HSTS, CSP, X-Frame-Options, Permissions-Policy all need explicit config. Here's the canonical next.config.mjs.

Default Next.js + Vercel = no security headers. Add HSTS + CSP + frame-ancestors + permissions-policy via next.config.mjs.

What it is

Security headers are HTTP response headers that instruct browsers to enforce protections (HTTPS-only, no framing, no Flash, etc.). Without them, browser defaults are permissive.

Vulnerable example

// next.config.mjs (default — no security headers)
const nextConfig = { reactStrictMode: true };
export default nextConfig;

Fixed example

// next.config.mjs
const securityHeaders = [
  { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
  { key: "X-Content-Type-Options", value: "nosniff" },
  { key: "X-Frame-Options", value: "DENY" },
  { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
  { key: "Permissions-Policy", value: "accelerometer=(), camera=(), geolocation=(), microphone=()" },
  { key: "Cross-Origin-Opener-Policy", value: "same-origin" },
];
const nextConfig = {
  reactStrictMode: true,
  poweredByHeader: false,
  async headers() { return [{ source: "/:path*", headers: securityHeaders }]; },
};
export default nextConfig;

How Securie catches it

Securie findingmedium
apps/web/app/api/route.ts:22

Security headers in Next.js

Securie's static-rules pre-filter detects missing security headers in next.config.mjs at PR time; flags as a high-severity finding for shipped-without-headers PRs.

Suggested fix — ready as a PR
// next.config.mjs
const securityHeaders = [
  { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
  { key: "X-Content-Type-Options", value: "nosniff" },
  { key: "X-Frame-Options", value: "DENY" },
  { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
  { key: "Permissions-Policy", value: "accelerometer=(), camera=(), geolocation=(), microphone=()" },
  { key: "Cross-Origin-Opener-Policy", value: "same-origin" },
];
const nextConfig = {
  reactStrictMode: true,
  poweredByHeader: false,
  async headers() { return [{ source: "/:path*", headers: securityHeaders }]; },
};
export default nextConfig;
Catch this in my repo →Securie reviews every PR · proves the issue · ships a verified fix PR

Checklist

  • HSTS with max-age >= 1 year + preload
  • X-Frame-Options DENY (matches CSP frame-ancestors 'none')
  • X-Content-Type-Options nosniff
  • Referrer-Policy strict-origin-when-cross-origin
  • Permissions-Policy lock down accelerometer/camera/geo/etc
  • CSP with nonce via middleware (advanced)

FAQ

What about CSP?

CSP requires nonce-wiring through middleware; see Securie's apps/web/middleware.ts for the canonical pattern.

Should I submit to HSTS preload list?

Yes — at hstspreload.org, after testing.

Related guides