Guides / Missing security headers on Vercel or Netlify: how to check and add them
Missing security headers on Vercel or Netlify: how to check and add them
Security headers are a few lines of config that tell the browser to refuse things your app never meant to allow, like being framed by another site or loading scripts from anywhere. Vercel and Netlify won't add most of them for you.
The short answer
Run curl -sI https://your-app.com and look for Strict-Transport-Security, Content-Security-Policy, X-Frame-Options (or CSP frame-ancestors), X-Content-Type-Options: nosniff, Referrer-Policy and Permissions-Policy. Add the missing ones with a headers block in vercel.json or next.config.js on Vercel, or a _headers file on Netlify. Roll out CSP in report-only mode first so you don't break your own app.
Headers won't fix an open database or a leaked key. They're a cheap extra layer, and scanners and security reviewers check for them, so they're worth ten minutes.
How to check
curl -sI https://your-app.com | grep -iE \ "strict-transport|content-security|x-frame|x-content-type|referrer-policy|permissions-policy"
Check a deep page too (for example /dashboard), not just the homepage, because header rules are matched by path. You can also see the response headers in DevTools → Network, by clicking the document request. Our free Leak Checker reports missing security headers as part of its passive check.
On Vercel you'll usually see strict-transport-security already there, since Vercel lists HSTS among its default response headers (max-age=63072000). The rest are up to you.
What each header does
Strict-Transport-Securitytells browsers to use HTTPS only for your domain from now on. Only addincludeSubDomainsorpreloadif every subdomain serves HTTPS.Content-Security-Policysets where scripts, styles, images and connections may load from. It's the strongest of these headers against XSS, and the one most likely to break things if you rush it.X-Frame-Options: DENY, or CSPframe-ancestors 'none', stops other sites from putting your app in an iframe, which blocks clickjacking.X-Content-Type-Options: nosniffstops the browser from guessing file types, which matters if users can upload files.Referrer-Policy: strict-origin-when-cross-originkeeps full URLs, which may include tokens or IDs, from leaking to other sites.Permissions-Policyturns off browser features you don't use, like camera, microphone and geolocation.
How to fix it on Vercel
Any framework: vercel.json
Put this in the project root. source: "/(.*)" applies it to every path:
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"headers": [
{
"source": "/(.*)",
"headers": [
{ "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": "Strict-Transport-Security", "value": "max-age=63072000" },
{ "key": "Content-Security-Policy-Report-Only",
"value": "default-src 'self'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" }
]
}
]
}
Next.js: next.config.js
This is the same idea, set by the framework, so it also applies when you run the app somewhere other than Vercel:
// next.config.js
const securityHeaders = [
{ 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 }]
},
}
If you need a strict CSP with nonces for inline scripts, Next.js has a dedicated Content Security Policy guide that uses middleware. That's worth following instead of hand-rolling one.
How to fix it on Netlify
Option A: a _headers file
The file has to end up in your publish directory, the folder Netlify deploys. That's usually dist/, build/ or out/. With Vite, putting it in public/ gets it copied there at build time.
/* X-Content-Type-Options: nosniff X-Frame-Options: DENY Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=(), microphone=(), geolocation=() Strict-Transport-Security: max-age=31536000 Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
Option B: netlify.toml
[[headers]]
for = "/*"
[headers.values]
X-Content-Type-Options = "nosniff"
X-Frame-Options = "DENY"
Referrer-Policy = "strict-origin-when-cross-origin"
Permissions-Policy = "camera=(), microphone=(), geolocation=()"
Netlify's custom headers only apply to files Netlify serves itself. Responses from functions, edge functions, SSR pages or proxied URLs don't get them, so set the headers in that code instead. Check an SSR route with curl after deploying.
Rolling out CSP without breaking your app
- Ship it as
Content-Security-Policy-Report-Onlyfirst, as in the examples above. The browser logs violations in the console but blocks nothing. - Click through every page, including login, checkout and anything with embeds, and add the domains you actually use. Typical ones are your Supabase project URL in
connect-srcand your payment provider inscript-srcandframe-src. - When the console is quiet, rename the header to
Content-Security-Policyto enforce it.
Then run the curl command again on the live site to confirm everything shows up.
next step
Check your live app for free
Our free Leak Checker looks at your deployed app the way any visitor's browser does and flags exposed secret keys, public source maps and missing security headers. Paste your app's URL to see which security headers it's missing, along with any exposed secret keys or public source maps.
If you'd rather have the whole app looked at, the $99 Scan + Report covers your repo and live app, including Supabase RLS, leaked keys and auth checks.
Sources
Every product step above was checked against these official pages. Last checked Oct 2026. Products change; if a menu moved, the docs below are the source of truth.