HTTP Security Headers That Actually Matter in 2025: CSP, HSTS, Permissions-Policy, and Friends
Headers are levers. Set the right ones and you slash XSS risk, lock browsers to HTTPS, tame third-party scripts, and protect users—without touching app code. These are the ones I deploy on every build.
For more details, see our Terms of Service.
Copy-Paste Starter Policy (Then Tweak)
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' https://cdn.example.com;
img-src 'self' data: https://images.example.com;
font-src 'self' https://fonts.example.com;
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
base-uri 'self';
object-src 'none';
upgrade-insecure-requests;
Strict-Transport-Security: max-age=15552000; includeSubDomains; preload
Permissions-Policy: geolocation=(), camera=(), microphone=(), usb=(), payment=()
Referrer-Policy: strict-origin-when-cross-origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Notes: Start in Report-Only
for CSP while you fix violations. Replace example origins with yours.
What Each Header Does (Plain English)
Content-Security-Policy (CSP)
Controls where scripts, styles, images, and connections can load from—and whether inline code is allowed.
Strict-Transport-Security (HSTS)
Forces HTTPS at the browser level and disables protocol downgrades. Include preload
if you’ll submit to the preload list.
Docs: MDN, hstspreload.org.
Permissions-Policy
Turns off powerful browser features (geolocation, camera, mic, payment) by default.
Docs: MDN.
Referrer-Policy
Controls how much URL info gets sent when users click links. strict-origin-when-cross-origin
is a safe default.
X-Content-Type-Options: nosniff
Prevents MIME sniffing—browsers won’t misinterpret files as executable if headers say otherwise.
X-Frame-Options
Legacy but still useful alongside CSP frame-ancestors
to block clickjacking.
Safe CSP Patterns That Don’t Break Everything
Goal: no 'unsafe-inline'
, no wildcards, minimal CDNs, and SRI on every third-party asset.
# Report-Only for rollout (copy header name exactly)
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self' https://cdn.example.com 'nonce-RANDOM';
style-src 'self' https://cdn.example.com;
img-src 'self' data:;
connect-src 'self' https://api.example.com;
report-to default-endpoint;
Rotate nonces per request. For static sites, prefer external JS with SRI over inline. Pair with Subresource Integrity for tamper resistance.
Add Headers at the Edge (Netlify, Nginx, Cloudflare)
Netlify _headers
/*
Strict-Transport-Security: max-age=15552000; includeSubDomains; preload
Referrer-Policy: strict-origin-when-cross-origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Nginx
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
Cloudflare: use Transform Rules → Response Header Modification for the same effect.
Test, Report, Iterate
- DevTools → Network → Headers to verify everything is set.
- Roll out CSP in
Report-Only
; send violation reports to an endpoint you control. - Use high-signal scanners: SecurityHeaders.com, Mozilla Observatory.
Common Header Pitfalls (and Fixes)
- Mixed content: Add
upgrade-insecure-requests
, fix legacy HTTP assets. - Third-party soup: Collapse CDNs. The fewer origins in CSP, the safer and faster.
- Inline scripts: Move to external JS + SRI, or use per-request nonces. Avoid
'unsafe-inline'
. - iFrames needed? Replace
X-Frame-Options
alone with CSPframe-ancestors
and name allowed parents.
Authoritative References
Where to Go Next
Add tamper resistance with Subresource Integrity, and remember the ops reality in Nothing Deletes. Security is a stack—policies, headers, and process.