Every example sets the same starting values. Hosting platforms may already send Strict-Transport-Security, so the Vercel, Netlify and Next.js examples leave it out; check the live response and add it only if it is missing.
nginx
Put the lines in the server block that serves HTTPS. The always parameter adds them to error responses too. A location block with add_header lines of its own drops every add_header inherited from the server block, so repeat the set there or keep it in one include file.
# In the server block that serves HTTPS (listen 443 ssl)
add_header Strict-Transport-Security "max-age=300" always;
add_header Content-Security-Policy-Report-Only "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
Apache
Enable mod_headers and add the lines to the virtual host for port 443, or to .htaccess where AllowOverride FileInfo is set. Header always set also covers error responses and redirects.
# <VirtualHost *:443> or .htaccess; needs mod_headers (Debian/Ubuntu: a2enmod headers)
Header always set Strict-Transport-Security "max-age=300"
Header always set Content-Security-Policy-Report-Only "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Cloudflare
In the dashboard, create a Response Header Transform Rule for your hostname and add each header with the Set static operation, which replaces a header of the same name from your origin. HSTS has its own setting under SSL/TLS, Edge Certificates; use it or the rule, not both. Menu names can differ between dashboard versions.
Rule type Response Header Transform Rule
When Hostname equals example.com
Then Set static, one row per header:
X-Content-Type-Options nosniff
Referrer-Policy strict-origin-when-cross-origin
X-Frame-Options SAMEORIGIN
Permissions-Policy camera=(), microphone=(), geolocation=()
Content-Security-Policy-Report-Only default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
HSTS SSL/TLS > Edge Certificates > HTTP Strict Transport Security (HSTS)
Vercel
Add a headers entry to vercel.json in the project root. The source pattern /(.*) matches every path; narrow it if some routes need a different policy.
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "Content-Security-Policy-Report-Only", "value": "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" },
{ "key": "X-Frame-Options", "value": "SAMEORIGIN" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" }
]
}
]
}
Netlify
Create a file named _headers in the publish directory, the folder Netlify deploys. Each path line, such as /*, is followed by indented Name: value lines. The same rules can also go in netlify.toml under [[headers]].
/*
Content-Security-Policy-Report-Only: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Next.js
Return the headers from the async headers() function in next.config.js; the source '/:path*' covers every route. headers() does not run for a static export (output: 'export'), so set the headers on the host that serves the exported files instead.
// next.config.js
const securityHeaders = [
{ key: 'Content-Security-Policy-Report-Only', value: "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" },
{ key: 'X-Frame-Options', value: 'SAMEORIGIN' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ 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 }];
},
};
After you deploy, run the checker again: it reads the final response, so it shows what your CDN or platform really sends. Then open an error page and a static file in your browser's developer tools, because headers are often set for some responses only.