Security Headers: What Each One Does and How to Set Them Up
A handful of HTTP response headers protect your visitors from cross-site scripting, clickjacking and downgrade attacks. What each one does, safe values to start with, how our free security headers checker tests your site and how to fix every problem it reports, on nginx, Apache and Cloudflare.
Searvia · 10/24/2026 · 11 min read
In short: Security headers are instructions your server sends with every page, telling browsers to always use HTTPS, run scripts only from sources you approve, refuse to let other sites frame your pages, never guess file types and keep full URLs out of the Referer header. Start with HTTPS everywhere and a valid certificate, then add
Strict-Transport-Security,X-Content-Type-Options: nosniff,Referrer-Policy: strict-origin-when-cross-originandframe-ancestorsorX-Frame-Options. Roll outContent-Security-Policyin report-only mode first. All of them are set in the server or CDN configuration.
Security headers are cheap insurance. Most take one line of server configuration, cost nothing in performance and close off whole classes of attacks: an injected script that steals sessions, an invisible frame that tricks people into clicking, a coffee-shop network that downgrades a visitor to plain HTTP. They are also among the first things security scanners, enterprise customers and security questionnaires check.
This guide explains each header and the connection basics around them, gives safe starting values, shows how our free security headers checker tests your site, and walks through a fix for every problem it reports.
The basics first: HTTPS, certificate and mixed content
Headers sit on top of an encrypted connection; without one, they don't help. Three basics come first:
- HTTPS on every page, with HTTP redirected. Since Chrome 68 in July 2018, Chrome marks every HTTP page as "not secure" (Chromium Blog), and Google's page experience guidance asks: "Are your pages served in a secure fashion?"
- A valid certificate that covers every hostname you use, including
wwwand the bare domain. An expired or mismatched certificate shows a full-page warning. - No mixed content. A secure page that loads files over
http://isn't fully secure. Per MDN, browsers upgrade images, audio and video to HTTPS automatically, and block scripts, stylesheets, iframes and other "blockable" content outright, which breaks the page.
The headers, one by one
| Header | Protects against | Safe starting value |
|---|---|---|
Strict-Transport-Security | Downgrade to HTTP, cookie theft on open networks | max-age=31536000; includeSubDomains (after testing) |
Content-Security-Policy | Cross-site scripting (XSS), injected content, clickjacking via frame-ancestors | Start with Content-Security-Policy-Report-Only |
X-Frame-Options or CSP frame-ancestors | Clickjacking | SAMEORIGIN or frame-ancestors 'self' |
X-Content-Type-Options | MIME sniffing: files run as scripts or styles | nosniff |
Referrer-Policy | Leaking full URLs to other sites | strict-origin-when-cross-origin |
Permissions-Policy | Embedded scripts using camera, microphone, location | camera=(), microphone=(), geolocation=() |
Strict-Transport-Security (HSTS)
HSTS tells the browser to use HTTPS for your host for a set number of seconds, even when someone types http:// or clicks an old link. MDN notes three things to know:
- Browsers ignore the header when it arrives over plain HTTP. Send it on HTTPS responses; your HTTP-to-HTTPS redirect doesn't need it.
- It only works after the first secure visit. Preloading closes that gap.
includeSubDomainsapplies the policy to every subdomain, so check them all first.
The HSTS preload list hard-codes your domain into browsers. It requires a valid certificate, an HTTP-to-HTTPS redirect on the same host, all subdomains on HTTPS, and a header with max-age of at least 31536000, includeSubDomains and preload. It also warns that "inclusion in the preload list cannot easily be undone": removal takes months to reach users. Preload only when you are sure.
Content-Security-Policy (CSP)
CSP is the strongest of the headers and the hardest to deploy. It lists where scripts, styles, images and other resources may load from, and the browser refuses everything else. MDN calls it mainly "a defense against cross-site scripting (XSS) attacks."
The recommended modern form is a strict CSP based on nonces or hashes rather than long domain allowlists:
Content-Security-Policy: script-src 'nonce-{RANDOM}'; object-src 'none'; base-uri 'none'
The nonce must be random and different on every response. MDN warns that 'unsafe-inline' "defeats much of the purpose of having a CSP."
Because a strict policy can break analytics tags, chat widgets and inline scripts, deploy it in two steps: send Content-Security-Policy-Report-Only with a reporting endpoint, fix what the reports show, then switch to the enforcing header. CSP is also where upgrade-insecure-requests lives (a mixed-content safety net, not a replacement for HSTS) and where frame-ancestors controls framing.
X-Frame-Options and frame-ancestors
Clickjacking loads your page in an invisible frame on another site and tricks people into clicking your buttons. Two ways to stop it:
X-Frame-Options: SAMEORIGIN(orDENY), the older header.ALLOW-FROMis obsolete and makes modern browsers ignore the header.Content-Security-Policy: frame-ancestors 'self', the more flexible option, which can also list specific partner origins.
Neither works from a <meta> tag. MDN states that X-Frame-Options in a meta element "has no effect" and that frame-ancestors "is not supported in the <meta> element." Note also that frame-ancestors doesn't fall back to default-src: a policy of default-src 'none' still allows anyone to frame the page.
X-Content-Type-Options
nosniff tells browsers to trust your Content-Type header. With it, a response requested as a script or stylesheet is blocked if its MIME type doesn't match, which stops an uploaded file disguised as an image from running as a script. It only has one valid value:
X-Content-Type-Options: nosniff
Make sure your server sends correct content types, especially for JavaScript and CSS, before you add it.
Referrer-Policy
When a visitor clicks a link to another site, the browser sends a Referer header. Modern browsers already default to strict-origin-when-cross-origin, which sends only the origin (https://example.com/) cross-site and nothing when going from HTTPS to HTTP (MDN). OWASP still recommends setting it explicitly "rather than relying on browser defaults." Avoid unsafe-url, which sends full URLs, including any tokens in them, everywhere.
Permissions-Policy, and the headers to drop
Permissions-Policy switches off browser features you don't use, so an embedded script can't ask for the camera or location. MDN marks it as experimental with limited browser support, so treat it as an extra layer. Our free checker doesn't test it; the full security scan does.
Two headers to remove rather than add: OWASP says not to set X-XSS-Protection (or set it to 0), because the old filters it controlled could create vulnerabilities, and calls Expect-CT deprecated. It also recommends removing X-Powered-By and giving Server a non-informative value.
How the Searvia security headers checker works
The free security headers checker runs the same engine as our website SEO checker and puts five checks first. It makes ordinary requests that any visitor could make; nothing is attacked or probed.
| Check | How it tests | Pass | Warning | Fail | Priority |
|---|---|---|---|---|---|
| HTTPS | Loads the page over HTTPS, then requests http:// without following redirects | HTTPS works and HTTP redirects to an https:// address (if HTTP doesn't answer at all, you get a note instead) | HTTPS doesn't work, or HTTP doesn't redirect | High | |
| TLS certificate | Reads the certificate of the final host | Valid, matches the host, 14+ days left | Under 14 days left | Expired or doesn't cover the host | High |
| HSTS | Reads max-age from the page's response | 15,552,000 seconds (about 6 months) or more | Missing or shorter | Low | |
| Mixed content | Finds http:// URLs in scripts, iframes, stylesheets, images, video, audio and sources on an HTTPS page | None | Only images or media | Scripts, iframes or stylesheets | Medium |
| Security headers | Looks for three key response headers and shows Referrer-Policy | All three present | Any missing | Low |
The three it counts are Content-Security-Policy, X-Frame-Options (or a CSP header containing frame-ancestors) and X-Content-Type-Options. Referrer-Policy is listed too, but a missing one is not counted against you: browsers already default to strict-origin-when-cross-origin, and the table says so. The result table shows each one's value, with long policies shortened.
Some limits to keep in mind:
- It checks presence, not quality. A CSP full of
'unsafe-inline'passes here; the full security scan grades that. - It reads the HTTP response headers. A CSP set in a
<meta>tag counts, with a note:frame-ancestorsand reporting don't work there, so it can't replaceX-Frame-Options. AContent-Security-Policy-Report-Onlyheader doesn't count as an enforced policy. - It checks one page, the one you enter (the home page if you enter only a domain). Other paths, such as an admin area or an API, can send different headers.
- Mixed content is found in the HTML only: absolute
http://URLs in tags. Files requested later by CSS or JavaScript aren't seen.
How to read the results
Work top to bottom by priority. A failing HTTPS or certificate check is urgent: visitors see browser warnings or travel unencrypted. Active mixed content (scripts, iframes, stylesheets) is next, because browsers block those files and the page may break. HSTS and the security headers are low priority in the score, but they are also the quickest to fix, so batch them in one configuration change.
How to fix every problem the checker reports
"The site does not work over HTTPS." / "HTTP does not redirect to HTTPS."
- Install a certificate. Let's Encrypt is free and most hosts offer it in one click.
- Add a permanent redirect from every
http://URL to the samehttps://URL.
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
On Cloudflare, turn on "Always Use HTTPS" under SSL/TLS › Edge Certificates.
"The certificate expired N days ago." / "The certificate does not cover this domain."
- Renew it now and turn on automatic renewal (Certbot and most hosts and CDNs do this by default).
- Make sure the certificate lists every hostname you serve, at least the bare domain and
www. - If renewal fails silently, the usual causes are a changed DNS record, a blocked validation path or an expired API token for DNS validation.
A warning with fewer than 14 days left usually means automatic renewal is not working; investigate rather than renewing by hand each time.
"HSTS is not set." / "HSTS is on but shorter than 6 months."
Roll it out in stages so a forgotten HTTP-only subdomain doesn't get locked out:
- Confirm the main site and every subdomain work over HTTPS.
- Send
Strict-Transport-Security: max-age=300for a day, then a week (604800), then a month. - Move to
max-age=31536000; includeSubDomains. - Only then consider
preloadand submit at hstspreload.org.
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
"N files load over plain HTTP."
- Open the list of URLs in the report.
- Change each to
https://, or to a relative path for your own files. For third-party files, check that the provider serves HTTPS; if not, replace the provider. - Search your database and theme for hard-coded
http://links; after a migration to HTTPS, old post content is the usual source. - As a safety net, add
Content-Security-Policy: upgrade-insecure-requests.
"N key security headers are missing."
Add the missing ones in one change. For nginx:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy-Report-Only "default-src 'self'; frame-ancestors 'self'; report-to csp" always;
For Apache:
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
On Cloudflare, add response headers with a Transform Rule. On Netlify, use a _headers file; on Vercel, the headers setting in vercel.json. In nginx, remember that add_header in a location block replaces the headers set at server level, so repeat them or use an include file.
For CSP, the report-only line above is a starting point, not a finished policy. Point report-to at a reporting endpoint you control, review violations for a week or two, adjust the policy, then rename the header to Content-Security-Policy. Once enforced, the CSP check passes.
How to check headers yourself
The checker shows what the checked page sends, but other pages, files and error responses can differ. Two quick ways to look:
- Browser DevTools. Open the Network panel, reload the page, click the first request and read the Response Headers section.
- The command line.
curl -sI https://www.example.com/prints only the response headers. Run it for the home page, a deep page, a PDF, a 404 page and your login page.
A common surprise is headers that appear on normal pages but not on errors or redirects. In nginx, add_header only applies to successful and redirect responses unless you add the always parameter, which is why every example above includes it. Another is a CDN or host that adds or strips headers, so check the response the public sees, not just your server configuration file.
Remember that each header protects a different thing. A perfect CSP does nothing for an expired certificate, and HSTS does nothing against an injected script. The goal is the whole set, added in a sensible order.
Worked example (hypothetical)
The following is a hypothetical example, not real data. Halden Legal, an invented law firm, receives a security questionnaire from a corporate client asking about its website headers. The security headers checker reports:
- HTTPS, TLS certificate: pass, 61 days left.
- HSTS: warning,
max-age=86400(one day). - Mixed content: warning, 2 images load over HTTP (a partner badge and an old team photo).
- Security headers: 2 key headers missing; only
X-Content-Type-Optionsis set, andReferrer-Policyshows the browser default.
The fixes take one afternoon. The two image URLs are changed to HTTPS. After confirming that the client portal subdomain works over HTTPS, HSTS goes to max-age=31536000; includeSubDomains in two steps over a week. X-Frame-Options: SAMEORIGIN and Referrer-Policy: strict-origin-when-cross-origin are added at the server. A report-only CSP runs for ten days and reveals an inline script from the booking widget, which is moved to a file and allowed by its origin before the policy is enforced. The final run passes every check, and the firm answers the questionnaire with a link to the shared report.
A one-hour checklist
- Run the security headers checker and fix HTTPS and certificate failures first.
- Remove mixed content listed in the report.
- Add
X-Content-Type-Options,Referrer-PolicyandX-Frame-Optionsorframe-ancestors. - Stage HSTS up to one year once every subdomain works over HTTPS.
- Start a report-only CSP and schedule a review.
- Remove
X-Powered-By, version numbers inServer, and anyX-XSS-Protectionother than0.
How Searvia keeps watching your site's security
The free security headers checker checks your home page, or any page you enter, in about 20 seconds, without sign-up, and the same report covers 33 more checks on search appearance, crawling, speed, mobile and email authentication.
A free Searvia account adds a weekly security scan: passive checks of encryption, browser protections, cookie flags, software versions and email spoofing protection, with the request and response that prove each finding. It grades your CSP (for example, 'unsafe-inline' without nonces or hashes), checks Permissions-Policy, and after you verify ownership also looks for exposed .env and .git files, public backups, debug pages and unsafe CORS. Write the remediation plan with AI turns the findings into configuration you can paste (5 credits). On paid plans from Starter, uptime monitoring also checks your TLS certificate daily and alerts you before it expires.
Frequently Asked Questions
- What are security headers?
- Security headers are HTTP response headers that tell the browser how to handle your page more safely: always use HTTPS (Strict-Transport-Security), only run scripts from approved sources (Content-Security-Policy), don't allow framing by other sites (X-Frame-Options or frame-ancestors), don't guess file types (X-Content-Type-Options) and limit what is sent in the Referer header (Referrer-Policy). They are set on the server or CDN, not in your page content.
- Do security headers affect SEO?
- Not as individual ranking factors. Google's page experience guidance asks whether pages are served securely, so HTTPS matters, and broken HTTPS or mixed content harms visitors and trust. Headers like Content-Security-Policy and X-Frame-Options protect users from attacks; their value is security, not rankings.
- What is a good HSTS max-age?
- One year (max-age=31536000) is a common choice and the minimum for the HSTS preload list, which also requires includeSubDomains and preload. Start lower, for example a few minutes, then a week, then a month, while you confirm that every subdomain works over HTTPS, because browsers will refuse plain HTTP for the whole period. Our checker passes values of about six months or more.
- Should I use X-Frame-Options or frame-ancestors?
- Both are fine; frame-ancestors in Content-Security-Policy is the more flexible modern option and X-Frame-Options is the older fallback. Use frame-ancestors 'self' or X-Frame-Options: SAMEORIGIN if you never need your pages embedded elsewhere. Neither works in a meta tag; they must be sent as HTTP headers.
- Is X-XSS-Protection still needed?
- No. OWASP recommends not setting it or explicitly turning it off with X-XSS-Protection: 0, because the old browser XSS filters it controlled could introduce vulnerabilities. Use a Content-Security-Policy that blocks inline scripts instead.
Sources
- HTTP Security Response Headers Cheat Sheet – OWASP
- Strict-Transport-Security – MDN Web Docs
- HSTS Preload List Submission – hstspreload.org
- Content Security Policy (CSP) – MDN Web Docs
- CSP: frame-ancestors – MDN Web Docs
- X-Frame-Options – MDN Web Docs
- X-Content-Type-Options – MDN Web Docs
- Referrer-Policy – MDN Web Docs
- Permissions-Policy – MDN Web Docs
- Mixed content – MDN Web Docs
- Understanding page experience in Google Search results – Google Search Central
- Module ngx_http_headers_module – nginx documentation
- A secure web is here to stay – Chromium Blog