Strict-Transport-Security
Without it, a visitor typing doors.test makes one plain-http request that can be intercepted before your redirect ever arrives. With it, the browser rewrites http to https itself for the next year — and the redirect round trip disappears too.
includeSubDomains covers everything under the name, so check that every subdomain has a certificate first. Start with a short max-age, confirm nothing broke, then raise it.
Strict-Transport-Security: max-age=31536000; includeSubDomains
A browser that has seen the header honours it until it expires, and there is no way to tell it otherwise. A year of https with a certificate you forgot to renew on a subdomain is a year of an unreachable site.
Content-Security-Policy
CSP is the second line of defence against cross-site scripting: even if an attacker injects a <script>, the browser refuses to run it because it is not from an allowed source and carries no nonce.
It is only worth having if it is strict. script-src 'self' 'unsafe-inline' allows exactly the injected inline script you were trying to stop — and unsafe-inline is what most first attempts end up with, because the page has inline handlers. Removing them is the work; the header is the easy part.
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-r4nd0m';
style-src 'self';
img-src 'self' data: https://cdn.doors.test;
connect-src 'self' https://api.doors.test;
frame-ancestors 'none';
base-uri 'self';
form-action 'self'
Content-Security-Policy-Report-Only with a report-to endpoint tells you what would have been blocked, on real traffic, without breaking anything. Run it for a week before enforcing.
The short ones
frame-ancestors in CSP supersedes X-Frame-Options, but sending both costs nothing and covers older browsers.
| Header | Stops |
|---|---|
X-Content-Type-Options: nosniff | the browser guessing that your JSON is HTML and running it |
Referrer-Policy: strict-origin-when-cross-origin | leaking a full URL — with its query — to other sites |
X-Frame-Options: DENY / frame-ancestors 'none' | your page being framed invisibly over a button (clickjacking) |
Permissions-Policy: camera=(), geolocation=() | embedded content asking for hardware in your name |
Cross-Origin-Opener-Policy: same-origin | another window keeping a handle on yours |
Where the defaults are wrong
- Serving user uploads from your own origin. A file called
avatar.htmlis then a script on your domain, with your cookies. Serve uploads from a separate origin, withnosniffandContent-Disposition: attachment. Referrer-Policy: unsafe-url— the whole URL, including a password-reset token in the query, goes to every site the user clicks through to.- A CSP with
unsafe-evalbecause one library needs it. That library is runningevalon something; find out what.
Headers are not the whole answer
Escaping output prevents XSS; CSP limits what a missed escape can do. Validating input prevents injection; a prepared statement makes it impossible. Headers are the layer that turns a mistake into a blocked console message instead of an incident — they are worth an afternoon, and they are not a substitute for the other layers.