Security Headers Guide
Paste a set of response headers for a graded review, then read what each header actually does and how to deploy a Content-Security-Policy without breaking things.
Get them with curl -sSI https://yourdomain.com, or copy the response headers from your browser's network tab. This page cannot fetch the site for you: there is no server here to make the request, which is the same reason nothing you paste can leave your browser.
What each header actually does
Ordered by how much difference it makes in practice, not alphabetically. The first two are worth real engineering time; the rest are configuration.
| Header | What it does | Why it matters |
|---|---|---|
| Content-Security-Policy | Declares which origins may supply script, style, frames and other content, and what inline code is allowed to run. | The only header that limits what an injection bug can actually do. Also the only one that can break your site, which is why it is so often missing. |
| Strict-Transport-Security | Tells the browser to reach this host over HTTPS only, for a stated period. | Closes the window where a first request over http:// can be intercepted and downgraded before your redirect runs. |
| X-Content-Type-Options | nosniff stops the browser second-guessing your Content-Type. | Prevents an uploaded file or a JSON endpoint being re-interpreted as script. One value, no compatibility risk. |
| CSP frame-ancestors | Controls who may put your pages in a frame. Replaces X-Frame-Options. | Without it, clickjacking is available against any state-changing UI you have: transfers, permission grants, deletions. |
| Referrer-Policy | Decides how much of the current URL is sent when a user navigates away. | Password-reset and invitation URLs end up in third-party logs when this is left permissive. |
| Permissions-Policy | Switches off browser features (camera, microphone, geolocation) for your pages and anything embedded in them. | Mostly a constraint on third-party frames you embed. Cheap to set, small payoff. |
| Cross-Origin-Opener-Policy | Severs the window reference between your page and cross-origin windows. | Blocks a class of cross-window attacks and enables cross-origin isolation. Check any OAuth popup flows before enabling. |
Deploying a CSP without breaking the application
Most teams that have no CSP tried once, broke something in production, and reverted. The report-only phase is what makes this safe, and it is the step usually skipped.
- Start in report-only. Ship Content-Security-Policy-Report-Only with the policy you eventually want. It blocks nothing and reports everything, so production traffic tells you what would have broken.
- Collect reports for a full business cycle. A fortnight catches the weekly batch job, the monthly invoice run, and the marketing tag someone adds on a Friday. A day catches your own testing.
- Fix the violations, do not widen the policy. Every report is either a dependency you forgot about or inline code that should move to a file. Adding the offending origin to the allow-list is how policies decay into permitting everything.
- Use nonces rather than 'unsafe-inline'. Generate a random nonce per response, put it on your own script tags and in the policy. Inline script injected by an attacker will not carry it.
- Set base-uri and object-src. Without base-uri 'self' an injected <base> tag re-points every relative script URL at the attacker. It is the most commonly missed directive in an otherwise decent policy.
- Enforce, then keep the report endpoint.Move the header to the enforcing name and leave reporting on. It is how you find out that last week's deploy is now silently blocked.
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self' 'nonce-{RANDOM_PER_RESPONSE}';
style-src 'self' 'nonce-{RANDOM_PER_RESPONSE}';
img-src 'self' data:;
connect-src 'self';
frame-ancestors 'none';
base-uri 'self';
object-src 'none';
form-action 'self';
report-uri /csp-reportTighten from here rather than loosening towards here. If a directive has to be relaxed, relax that one directive instead of falling back on default-src.
Headers still widely recommended that do nothing
These appear in most hardening checklists and a good number of compliance templates. They are no longer doing anything for you.
X-XSS-Protection
The browser auditor it configured has been removed from Chrome and Edge. While it existed it introduced its own bugs. Setting it to 1; mode=block achieves nothing today.
Expect-CT
Deprecated. Certificate Transparency is enforced unconditionally by browsers now.
Public-Key-Pins
Dead and ignored. When it worked, a pinning mistake took your domain offline until the pin expired, with no way to recover.
Feature-Policy
Renamed to Permissions-Policy with slightly different syntax. Migrate the directives rather than shipping both.