LookMyIP
Blog/HTTP Security Headers: What Each One Does and Safe Values to Start With
Security5 min read

HTTP Security Headers: What Each One Does and Safe Values to Start With

By Avinash Mehra

HSTS, CSP, X-Content-Type-Options, Referrer-Policy, Permissions-Policy and clickjacking protection explained, with safe values to deploy first.

Just need to check? Use the HTTP Headers Checker to see which security headers a site is sending.

Key takeaways

  • HSTS, Content-Security-Policy, X-Content-Type-Options, Referrer-Policy, Permissions-Policy and frame-ancestors are the headers that matter today.
  • Roll out CSP in report-only mode first; adding frame-ancestors and upgrade-insecure-requests rarely breaks anything.
  • X-XSS-Protection, Expect-CT and HPKP are obsolete and can be dropped.

Why Response Headers Matter for Security

Every HTTP response carries headers before the page itself. Most are about caching and content type, but a handful tell the browser to switch on protections it wouldn't otherwise apply: always use HTTPS, refuse to run scripts from unknown origins, don't let other sites put this page in a frame.

These headers are cheap — a few lines of server or CDN configuration — and they defend against whole classes of attack: protocol downgrades, cross-site scripting, clickjacking, and data leaking through the Referer header. They don't fix vulnerable code, but they limit how much damage a bug can do.

You can see any site's headers with curl:

$ curl -sI https://github.com | grep -iE 'strict|x-frame|x-content|referrer'
strict-transport-security: max-age=31536000; includeSubdomains; preload
x-frame-options: deny
x-content-type-options: nosniff
referrer-policy: origin-when-cross-origin, strict-origin-when-cross-origin

The HTTP Headers Checker shows the full header list and checks six protections: HSTS, Content-Security-Policy, clickjacking protection, X-Content-Type-Options, Referrer-Policy and Permissions-Policy. The sections below explain each one.

Strict-Transport-Security (HSTS)

HSTS tells the browser: for the next N seconds, only ever contact this host over HTTPS. After the first visit, typing http://example.com or following an old http:// link never touches the network in plain text — the browser upgrades it internally.

This closes the gap a plain HTTP-to-HTTPS redirect leaves open. Without HSTS the very first request goes out unencrypted, and an attacker on the same network can intercept it and keep the victim on HTTP (an "SSL stripping" attack).

Strict-Transport-Security: max-age=31536000; includeSubDomains
  • max-age is in seconds; 31536000 is one year.
  • includeSubDomains applies the rule to every subdomain. Only add it once you're sure every subdomain serves HTTPS — including internal names like intranet.example.com that happen to resolve publicly.
  • preload asks to have the domain built into browsers' HSTS preload list, which protects even the very first visit. Removal from that list takes months, so treat it as permanent.

Safe rollout: start with max-age=300, confirm nothing breaks, then raise it to a week, then a year. Browsers ignore HSTS sent over plain HTTP, so it must be on the HTTPS responses.

Content-Security-Policy (CSP)

CSP is the most powerful header on this list and the hardest to deploy. It tells the browser which sources of scripts, styles, images, frames and connections a page may use. If an attacker injects a <script> tag through a cross-site scripting bug, a good CSP stops the browser from running it.

A strict policy based on nonces looks like this:

Content-Security-Policy: script-src 'nonce-R4nd0m' 'strict-dynamic'; object-src 'none'; base-uri 'none'

Every legitimate <script> tag carries nonce="R4nd0m", with a fresh random value on every response. Injected scripts don't know the nonce, so they're blocked.

Most sites can't adopt that overnight: inline scripts, third-party widgets, ad tags and analytics all need accounting for. The safe path is report-only mode:

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

The browser enforces nothing but reports every violation. Collect reports for a week or two, adjust the policy until real traffic produces no unexpected violations, then rename the header to Content-Security-Policy.

Two directives are worth adding even if you never reach a full policy, because they rarely break anything:

  • frame-ancestors 'self' — clickjacking protection (next section).
  • upgrade-insecure-requests — rewrites http:// subresource URLs to https://, fixing mixed-content warnings on older pages.

Avoid 'unsafe-inline' in script-src: it allows exactly the injected inline scripts CSP is meant to stop.

Clickjacking: X-Frame-Options and frame-ancestors

Clickjacking loads your page in an invisible frame on an attacker's site and tricks the user into clicking buttons they can't see — "delete account", "confirm transfer", "allow camera".

The defence is to forbid framing. The modern way is the CSP frame-ancestors directive:

Content-Security-Policy: frame-ancestors 'self'

The older header still works and is still widely sent for compatibility:

X-Frame-Options: SAMEORIGIN

DENY forbids all framing; SAMEORIGIN lets your own pages frame each other. When both are present, browsers that support frame-ancestors use it and ignore X-Frame-Options. The Headers Checker counts either as protection.

If a page is legitimately meant to be embedded — a video player, a booking widget — list the allowed parent origins explicitly in frame-ancestors rather than dropping protection altogether.

X-Content-Type-Options, Referrer-Policy and Permissions-Policy

X-Content-Type-Options: nosniff stops the browser guessing a file's type when the Content-Type header says something else. Without it, a user-uploaded "image" containing HTML or JavaScript could be sniffed and executed. It has one valid value and almost never breaks anything:

X-Content-Type-Options: nosniff

Referrer-Policy controls how much of the current URL goes into the Referer header when a user follows a link or the page loads a resource. Full URLs can contain password-reset tokens, search terms or internal paths. Modern browsers already default to strict-origin-when-cross-origin — only the origin is sent to other sites, and nothing on an HTTPS-to-HTTP step — but setting it explicitly protects users of older browsers:

Referrer-Policy: strict-origin-when-cross-origin

Permissions-Policy switches off browser features the page doesn't use, so a compromised script or embedded frame can't request them:

Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()

An empty list () disables the feature for everyone, including your own page. Only list features you're sure you don't need.

Headers You Can Drop

Some headers still appear in older checklists but no longer help:

X-XSS-Protection. It controlled the "XSS auditor" in old versions of Chrome, Edge and Safari. Every major browser has removed the auditor, and in some configurations it could be abused to selectively block legitimate scripts. MDN and OWASP now recommend sending X-XSS-Protection: 0 or omitting it and relying on CSP. The Headers Checker lists it in the raw headers but doesn't score it.

Expect-CT. It made Certificate Transparency enforcement opt-in. CT is now enforced by default in Chrome and Safari and the header is deprecated. See what Certificate Transparency is.

Public-Key-Pins (HPKP). It let sites pin certificate keys. One mistake could lock visitors out for months, and browsers removed support around 2018.

Server and X-Powered-By. Not security headers, but they advertise your software and version (nginx/1.18.0, PHP/7.4). Hiding them isn't real protection, but there's no reason to tell automated scanners which exploits to try.

A Starter Set and How to Roll It Out

For a typical site, this set is safe to deploy with little testing:

Strict-Transport-Security: max-age=31536000
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: frame-ancestors 'self'; upgrade-insecure-requests
Permissions-Policy: camera=(), microphone=(), geolocation=()

Then work toward a full CSP in report-only mode.

Where you set them depends on the stack: add_header ... always in nginx (the always makes them appear on error responses too), Header always set in Apache, a headers block in next.config.js or vercel.json, or a response-header rule at your CDN. The CDN is often easiest, because one rule covers every origin behind it.

After each change, run the Headers Checker against several page types — the homepage, a deep page, an error page and a static file. A common mistake is setting headers in application code, so they're missing on 404 pages and on files the CDN serves directly.

Sources

Try It Yourself

Use LookMyIP's free tools to look up IP addresses, check DNS records, verify SSL certificates, and more.