← Guides

Vercel and Next.js Security Headers: vercel.json vs next.config.js, With Copy-Paste Configs

Apps on Vercel, including ones built with v0, Bolt, Lovable, or Cursor and deployed there, usually ship with HTTPS but few security headers. A headers checker will flag missing Content-Security-Policy, X-Frame-Options, Referrer-Policy, and Permissions-Policy. Here's how to add them without breaking your app.

Pick one place for your headers

Don't define the same header in two places. Both get sent, and two Content-Security-Policy headers are enforced together, which commonly breaks scripts and styles.

Starter config: vercel.json

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains" },
        { "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": "camera=(), microphone=(), geolocation=()" },
        { "key": "Content-Security-Policy-Report-Only", "value": "default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src 'self' https:; frame-ancestors 'none'" }
      ]
    }
  ]
}

Same thing in next.config.js

const securityHeaders = [
  { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains' },
  { 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: 'camera=(), microphone=(), geolocation=()' },
];
module.exports = {
  async headers() {
    return [{ source: '/:path*', headers: securityHeaders }];
  },
};

Roll out CSP in two steps

CSP is the header most likely to break something, so the starter config uses Content-Security-Policy-Report-Only, which only logs violations. Deploy it, click through your app with the browser console open, and add every legitimate source you see blocked: your Supabase or Firebase URL in connect-src, Stripe in script-src and frame-src, analytics domains, font CDNs. When the console is clean, rename the key to Content-Security-Policy. Next.js in development needs 'unsafe-eval', but production builds don't, so test against a preview deployment. If you need inline scripts, Next.js's nonce setup via middleware is safer than 'unsafe-inline' in script-src.

Things to know

Check it the fast way

Want a second opinion in under a minute? Scan your site free with SiteShield. It checks which security headers your deployed app actually sends (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy), plus SSL, SPF, DMARC, and privacy basics and shows your score and every issue found. If you'd rather skip the trial and error, the $14 fix bundle gives you copy-paste fixes written for your exact site.

Check your site free

SiteShield by Telekeen · Telekeen Communications