HTTP Security Headers Explained (With Grades and Fixes)
What each HTTP security header does, the risk of leaving it out, and the exact value to add. Includes HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, cookie flags, and how a header grader scores a site from A+ to F.
A response header is a one-line instruction from your server to the browser. A handful of them decide whether your site can be framed by an attacker, whether your cookies leak over plain HTTP, and whether a content injection becomes a full account takeover. They cost nothing to send and are invisible to users. Paste any URL into the analyzer at /#tool and you get the exact response headers, a per-header pass or fail, and a letter grade.
What a security header does
Security headers are defense-in-depth. They do not replace input validation or authentication. They constrain what the browser is willing to do with your content, which is exactly the layer that traditional server-side validation cannot reach. When a page fails to specify a policy, the browser falls back to a permissive default, and the attacker gets to decide how the page behaves.
Strict-Transport-Security (HSTS)
What it does: tells the browser to only ever contact this host over HTTPS for a set period, and to refuse certificate errors for it.
The risk if missing: a user on a hostile network can intercept the first plain-HTTP request and strip the redirect to HTTPS. This is the SSL-strip attack. Without HSTS, every visit is a fresh chance for it to succeed.
The fix: send it over HTTPS only, on every response:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Start with a short max-age, confirm every subdomain works over HTTPS, then raise it. Add preload only once you are certain you will never need plain HTTP for that domain.
Content-Security-Policy (CSP)
What it does: declares which origins may supply scripts, stylesheets, images, fonts, and frames, and blocks inline script unless explicitly allowed.
The risk if missing: an injected <script> tag from a comment field or a compromised third-party script executes with full access to the page. CSP is the strongest mitigation against cross-site scripting because it stops the malicious script from running at all.
The fix: roll it out in report-only mode first so you see violations without breaking the page:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
Flip the header name to Content-Security-Policy once the reports are clean. Avoid 'unsafe-inline' in script-src; if you must use it for a legacy site, pair it with a nonce or hash as you migrate.
X-Frame-Options
What it does: controls whether the page may be embedded in a frame.
The risk if missing: clickjacking. An attacker loads your site in a transparent iframe over a decoy, and the victim clicks your “delete account” or “confirm payment” button without knowing.
The fix: X-Frame-Options: SAMEORIGIN, or better, the CSP frame-ancestors directive, which supersedes it:
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'
Use DENY if nothing should ever frame the page. If you embed your own app across subdomains, frame-ancestors lets you name those origins precisely.
X-Content-Type-Options
What it does: disables MIME sniffing. The browser trusts your declared Content-Type instead of guessing.
The risk if missing: a browser may interpret an uploaded file as HTML or script. A user-uploaded image with embedded markup can run in your origin.
The fix: one value, no parameters:
X-Content-Type-Options: nosniff
This is the cheapest header on the list. Add it everywhere, including API responses.
Referrer-Policy
What it does: limits how much of the current URL is sent in the Referer header on outbound requests.
The risk if missing: full URLs leak to third parties. If a URL contains a session token, an email address, or a private document name, the destination and every tracking script on it learns that value. Note the misspelling of Referer is intentional; it predates the standard.
The fix: for most sites:
Referrer-Policy: strict-origin-when-cross-origin
This sends the full URL for same-origin requests and only the origin cross-origin, and nothing when downgrading to HTTP. Use no-referrer on sensitive paths.
Cookie Flags
Cookies are not headers you add; they are attributes on Set-Cookie. Three matter most.
| Flag | What it prevents | Example |
|---|---|---|
Secure |
Cookie sent over plain HTTP, where it can be sniffed | Set-Cookie: sid=abc; Secure |
HttpOnly |
JavaScript reading the cookie, which defeats XSS token theft | Set-Cookie: sid=abc; HttpOnly |
SameSite |
The cookie riding along on cross-site requests (CSRF) | Set-Cookie: sid=abc; SameSite=Lax |
SameSite=Lax is a sensible default for most session cookies and is now the browser default, but do not rely on the default; state it explicitly. Use SameSite=Strict for cookies that should never travel from an external link, and SameSite=None; Secure for genuinely cross-site embeds, which requires Secure.
For session cookies, the __Host- prefix is the strongest option: the browser refuses to accept a __Host- cookie unless it is Secure, has no Domain, and is set with Path=/. That blocks cookie-tossing from a subdomain.
How a grader scores A+ to F
A header grader fetches the response and checks a fixed list of controls. A representative scheme, and the one used by the analyzer on this site, scores eight things: HTTPS, HSTS, CSP, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, and Cross-Origin-Opener-Policy. Each satisfied control is worth one point out of eight, and the percentage maps to a letter:
| Score | Grade | Meaning |
|---|---|---|
| 95-100% | A+ | Every control present |
| 85-94% | A | One lower-priority header missing |
| 75-84% | B | A couple of controls missing |
| 65-74% | C | Several gaps |
| 50-64% | D | Most controls missing |
| below 50% | F | Barely any protections |
With eight controls, only a perfect set reaches A+. Missing exactly one yields 87.5% and an A. Missing two yields 75% and a B. Because the list is small, each missing header costs a lot, which is why nosniff and a referrer policy are worth adding immediately: they are trivial to configure and recover two full points.
Cookie flags and version-disclosure headers usually appear in the report as findings rather than score deductions. Leaking Server or X-Powered-By values like nginx/1.18.0 or PHP/7.4.3 tells an attacker which known vulnerabilities to try first. Remove the version numbers where your stack allows it.
A rollout order that does not break production
- Add
X-Content-Type-Options: nosniffandReferrer-Policy: strict-origin-when-cross-origin. These almost never break anything. - Enable HSTS with a short
max-agesuch as 300, confirm HTTPS across subdomains, then raise it to a year. - Add
X-Frame-Optionsor a CSPframe-ancestorsdirective; test any legitimate embed. - Deploy CSP as
Content-Security-Policy-Report-Only, watch the violation reports, then promote it. - Audit cookies and add
Secure,HttpOnly, and an explicitSameSitevalue.
Re-run the grader after each step and confirm the grade moved. Then commit the header configuration to your repo or infrastructure as code, so the next deploy cannot silently drop it. The blog covers related diagnostics if you want to go deeper on response inspection.
Takeaway
Open your browser’s network panel, reload your homepage, and find the Set-Cookie and response header block. If X-Content-Type-Options is absent, add nosniff today; it is a single line that closes a whole class of content-sniffing attacks and immediately improves your grade.