Guides
HTTP security headers: what each one does and how to check a site's
What the six common HTTP security headers tell a browser, typical values, frequent mistakes, and how to check the headers any site sends.
7 min read
Security headers are lines in a site’s HTTP response that tell the browser which restrictions to apply: where scripts may load from, whether HTTPS is mandatory, whether other sites may frame the page. Six of them cover most of what matters. You can read them in a second with curl -sI https://example.com or in the Network tab of your browser’s developer tools, and the StatsMyWeb domain report lists all six for any domain. Below is each header with a typical value, what it does, and common mistakes.
The six headers side by side
| Header | Typical value | What it controls |
|---|---|---|
Content-Security-Policy | default-src 'self' | Which sources the page may load scripts, styles, frames and other resources from |
Strict-Transport-Security | max-age=31536000; includeSubDomains | Whether the browser must use HTTPS for this host from now on |
X-Frame-Options | DENY or SAMEORIGIN | Whether other sites may show the page inside a frame |
X-Content-Type-Options | nosniff | Whether the browser may guess a file’s type instead of trusting Content-Type |
Referrer-Policy | strict-origin-when-cross-origin | How much of the current URL the next site receives in the Referer header |
Permissions-Policy | camera=(), microphone=(), geolocation=() | Which browser features the page and its frames may use |
Content-Security-Policy: where the page may load code from
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'self'
A Content Security Policy is an allow-list. Each directive names a type of resource and the sources it may come from, and default-src covers every type not listed separately. If someone manages to inject a <script> tag into the page, a strict policy stops the browser from running it. That limits what cross-site scripting (XSS) can do.
Common mistakes:
'unsafe-inline'or'unsafe-eval'inscript-src. They allow the inline code a policy exists to block, and most of the protection goes with them.- Wildcards such as
script-src *orscript-src https:, which accept scripts from any host. - Sending
Content-Security-Policy-Report-Onlyduring testing and never switching. The report-only header records violations but enforces nothing. - Declaring the policy only in a
<meta>tag. Most directives work there, butframe-ancestorsand reporting do not.
Strict-Transport-Security: HTTPS only, from now on
Strict-Transport-Security: max-age=31536000; includeSubDomains
HSTS, defined in RFC 6797, tells the browser to use HTTPS for this host for max-age seconds. A max-age of 31536000 seconds is one year. During that time the browser turns any http:// link into https:// before sending anything, and does not let the visitor click through a certificate error.
The header does nothing on the first visit, because the browser has not seen it yet. The HSTS preload list, built into browsers, closes that gap for domains that add the preload directive and apply to be listed.
Common mistakes:
- Sending the header only on the plain-HTTP response. Browsers ignore HSTS received over HTTP; it has to arrive over HTTPS.
- A short
max-age, such as 300 seconds (five minutes), which expires before it protects much. includeSubDomainswhile some subdomain still serves only HTTP. Browsers will refuse to load it for as long as the policy lasts.- Adding
preloadwithout meaning it. Getting off the preload list is slow to reach users.
X-Frame-Options: who may put the page in a frame
X-Frame-Options: SAMEORIGIN
DENY forbids every site from framing the page; SAMEORIGIN allows only pages from the same origin. This blocks clickjacking, where another site shows your page in an invisible frame and leads the visitor to click its buttons.
The modern replacement is the CSP directive frame-ancestors. When both are present, browsers that support frame-ancestors follow it and ignore X-Frame-Options.
Common mistakes:
ALLOW-FROM https://example.org. It is obsolete and current browsers ignore it. Useframe-ancestorsto allow specific sites.- Setting it in a
<meta>tag, where browsers ignore it.
X-Content-Type-Options: no guessing file types
X-Content-Type-Options: nosniff
nosniff is the only valid value. It tells the browser to trust the Content-Type header instead of guessing from the file’s contents. With it, the browser refuses to run a script or apply a stylesheet whose declared type does not match, so an uploaded file that looks like JavaScript is not run as JavaScript.
Common mistakes:
- Turning it on while the server labels files wrongly, for example JavaScript served as
text/plain. The browser then blocks those files and parts of the page stop working. The fix is theContent-Type, not removing the header. - Misspelling the value (
no-sniff), which browsers ignore.
Referrer-Policy: what the next site learns about where you came from
Referrer-Policy: strict-origin-when-cross-origin
When a visitor follows a link, the browser sends a Referer header (spelt that way in the standard) naming the page they came from. strict-origin-when-cross-origin sends the full URL within the same site, only the origin (https://example.com/) to other sites, and nothing when moving from HTTPS to HTTP. Paths and query strings, which can carry search terms or tokens, stay inside the site.
Most current browsers already apply this value when the header is absent, so a missing Referrer-Policy changes less than it once did.
Common mistakes:
unsafe-url, which sends the full URL, query string included, to every site, even over plain HTTP.- Treating the header as a fix for tokens in URLs. A token in a URL still ends up in browser history and server logs.
Permissions-Policy: which browser features the page may use
Permissions-Policy: camera=(), microphone=(), geolocation=()
Each entry names a browser feature and the origins allowed to use it. An empty list () turns the feature off for the page and every frame inside it, and self allows the site’s own origin. A site that never needs the camera loses nothing by switching it off.
Common mistakes:
- Copying the syntax of the older
Feature-Policyheader (camera 'none').Permissions-Policyuses a different, structured syntax, and a header that fails to parse is ignored. - Expecting identical behaviour everywhere. Support for individual features differs between browser engines.
How to see the headers a site sends
With curl
curl -sI https://example.com
-I asks for the headers only (a HEAD request) and -s hides the progress bar. The output has this shape; the values here are illustrative:
HTTP/2 200
content-type: text/html; charset=UTF-8
strict-transport-security: max-age=31536000; includeSubDomains
x-content-type-options: nosniff
referrer-policy: strict-origin-when-cross-origin
Header names are case-insensitive, and over HTTP/2 they arrive in lower case. Add -L to follow redirects and print the headers of every hop. Some servers answer HEAD differently from GET; to see the headers of a real page load, run curl -s -D - -o /dev/null https://example.com. To keep only the six, pipe either command through grep -iE 'content-security|strict-transport|x-frame|x-content-type|referrer-policy|permissions-policy'.
In the browser
Open developer tools (F12, or Cmd+Option+I on macOS), go to the Network tab and reload the page. Click the first row, which is the document itself, and read the Response Headers section. This is the method to use for a page behind a login, which curl cannot see without your session cookie.
In the StatsMyWeb domain report
Open the domain report for example.com, or replace the domain in the address with any other. The security-headers section lists the six headers above, each with the value the site sent or a note that it is absent. They are read from the final response: the report requests https:// and follows any redirects first. It does not grade or score them. The same report shows the redirect chain and the other HTTP response headers, which helps with the limits below.
What a header check does not tell you
- Whether the policy is any good. A present header can still be permissive.
Content-Security-Policy: default-src * 'unsafe-inline' 'unsafe-eval'counts as present and blocks almost nothing. - What other pages send. Headers are set per response. A login page, an API path or a section served by another application may send different ones from the home page.
- What each redirect hop sent.
http://example.commay redirect tohttps://example.com/and then tohttps://www.example.com/. Each hop has its own headers, and HSTS on the plain-HTTP hop is ignored.curl -sILshows them all. - Policies set outside the headers. A CSP in a
<meta>tag never appears among the response headers. - Whether every visitor gets the same answer. Behind a CDN, responses can differ by country or user agent, so two checks from different places can disagree.
- Whether the site is secure. Headers are instructions to the browser. They say nothing about the server’s software, its passwords or its code.
For how a domain sits in the wider web, read what a connectivity rank tells you about a domain. The methodology page explains where the report’s data comes from, and the guides index collects the other checks.
Frequently asked questions
- Does a missing security header mean a site is unsafe?
- No. It means the browser falls back to its default behaviour for that feature. How much that matters depends on the site: a static page with no forms and no third-party scripts has less to protect than a web application with user accounts.
- Should a site send both X-Frame-Options and a CSP frame-ancestors directive?
- Sending both is common and harmless. Browsers that support frame-ancestors follow it and ignore X-Frame-Options, while older browsers fall back to X-Frame-Options.
- Why does curl show different headers from my browser?
- curl -I sends a HEAD request with no cookies and its own user agent. A server or CDN can answer that differently from a full GET sent by a browser. Compare with curl -s -D - -o /dev/null, or read the Network tab in developer tools.