GUIDE · SECURITY
Setting security headers on Vercel and Netlify
How to set security headers in vercel.json or in _headers or netlify.toml, and why Content-Security-Policy runs in report-only mode first.
3 min readLast checked
Which headers
The sources give X-Frame-Options (DENY) and X-Content-Type-Options (nosniff) as examples, plus the Content-Security-Policy, which defines allowed sources for scripts, images and styles and makes attacks through injected code harder. According to Vercel’s documentation it is a second line of defense, not a replacement for clean input validation.
Vercel: headers in vercel.json
vercel.json takes a headers list. Each entry has a source (the path, for example /(.*)) and a headers list with key and value, for example X-Frame-Options with DENY.
Netlify: _headers or netlify.toml
On Netlify you create a _headers file in the publish directory (a path, with the headers indented below it) or use [[headers]] in netlify.toml with for = "/*" and a headers.values block.
The documentation names one limit: custom headers apply only to content Netlify serves itself, not to proxy targets, serverless functions, edge functions or SSR pages. There the service or function has to return the headers itself.
Content-Security-Policy: report-only first
Vercel recommends starting with the Content-Security-Policy-Report-Only header. You see possible violations without anything being blocked, and switch to enforcing only when nothing breaks.
The documentation also advises avoiding unsafe-inline and unsafe-eval, using nonces or hashes for inline scripts and styles, naming sources as precisely as possible and keeping the policy up to date with the app.
Check afterwards
Test the page in several browsers. The free check looks at your home page from the outside and shows whether important security headers are missing.
Common mistakes
These mistakes follow from the Vercel and Netlify docs:
- Enforcing the Content-Security-Policy right away. Vercel advises setting
Content-Security-Policy-Report-Onlyfirst and enforcing only once nothing breaks. - Adding
unsafe-inlineandunsafe-evalas a shortcut. The docs advise against it and recommend nonces or hashes. - Allowing sources too broadly, for example with
*or all subdomains. Name the specific sources. - Not maintaining the policy. When the sources your app loads from change, the policy has to follow.
- On Netlify, expecting custom headers to apply to functions, edge functions, SSR pages and proxy targets as well. Per the docs, they do not.
- On Netlify, setting headers such as
LocationorSet-Cookiein_headers. The docs list headers that Netlify sets itself and that are therefore typically ignored; redirects have their own rules. - On Netlify, forgetting that headers from
_headersornetlify.tomlapply to all builds and branches.
Example: vercel.json
Following the pattern in the Vercel docs, here with two headers for all paths:
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" }
]
}
]
}Example: _headers and netlify.toml
The _headers file lives in the publish folder. The path stands alone on a line, the headers are indented below it:
The same in netlify.toml:
# _headers
/*
X-Frame-Options: DENY
# netlify.toml
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"Example: Content-Security-Policy in test mode
The example policy from the Vercel docs allows content from your own origin by default, scripts additionally from cdn.example.com and images from img.example.com. To test it, set it under the name Content-Security-Policy-Report-Only so nothing is blocked:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' cdn.example.com; img-src 'self' img.example.com; style-src 'self';Frequently asked questions
Which headers should I set first?
The sources give X-Frame-Options, X-Content-Type-Options and the Content-Security-Policy as examples. Start with the first two; the policy needs a test run.
Why Report-Only first?
You see possible violations without anything being blocked. Enforce only once the policy does not break anything important.
Do my Netlify headers apply to functions too?
No. Per the docs they do not apply to functions, edge functions, SSR pages and proxy targets. There the service or function sets the headers itself.
Does the Content-Security-Policy alone protect against XSS?
No. Vercel describes it as a second line of defense. Clean input validation and secure code remain necessary.
Sources
Check your app
The free check loads your app like a visitor and shows what is visible from the outside. You confirm beforehand that you are responsible for the app or authorised to test it.