Security headers are the fastest category to explain and the most commonly ignored. An agency running a prospect audit shows a client that their site is missing five of six security headers — and the client asks "what does that actually mean for me?" The answer isn't complicated, but it needs to be specific.
Here's exactly what the Security Headers Checker reports on, what each grade means, and the production-ready header value to deploy for each one.
Security headers aren't an SEO ranking factor in the direct sense — Google doesn't reward HSTS with higher rankings. But they're included in audit reports for three reasons:
Chrome's HTTPS warnings affect bounce rate. Missing HSTS combined with an insecure redirect path produces interstitial warnings that visibly increase abandonment.
Trust signals affect conversion. B2B and healthcare sites that handle contact forms, payment flows, or personal data face higher scrutiny from prospects. An absent Content-Security-Policy is a flag to security-aware buyers.
Misconfigured headers cause user-facing errors. An overly restrictive CSP breaks JavaScript loading. An X-Frame-Options: DENY setting breaks legitimate embeds. These aren't theoretical — agencies encounter them regularly on client sites they didn't build.
What it does: Tells the browser which sources are allowed to load scripts, styles, images, fonts, and other resources. Prevents cross-site scripting (XSS) attacks by blocking unauthorized scripts from executing.
Grade bands:
Pass: CSP header present with a policy that restricts script-src to specific origins (not 'unsafe-inline' or 'unsafe-eval' without nonce or hash mitigation)
Warn: CSP present but includes 'unsafe-inline' without nonce/hash equivalents, or uses a wildcard * for script sources
The nonce-{random} value is generated fresh per request by the server and injected into the CSP header and the corresponding <script nonce="..."> tags. This allows inline scripts with nonce verification while blocking injected scripts that lack it.
Common mistake: Setting script-src 'unsafe-inline' to "fix" a CSP that was blocking something. This defeats the entire purpose of the header. The right fix is to identify what's being blocked (browser console → Content Security Policy violations) and add the specific source rather than opening the wildcard.
What to skip: Report-only mode (Content-Security-Policy-Report-Only) is useful for testing, but shipping only report-only mode and no enforcement policy scores as a Warn in the checker — you've done the analysis work without the security benefit.
What it does: Tells browsers to only connect to this domain over HTTPS, for a specified duration. Prevents downgrade attacks where an attacker intercepts an HTTP request before it can redirect to HTTPS.
The preload directive is optional but significant: it submits the domain to Chrome's HSTS preload list, which hardcodes your domain as HTTPS-only in the browser binary. Users never make an initial HTTP request — there's no window for interception, even on the first visit.
Important caveat on preload: Once on the preload list, removal takes months to propagate. Don't add preload unless you're committed to HTTPS on all subdomains indefinitely. For most sites this is fine; for sites with HTTP-only subdomains for legacy tools, it causes breakage.
Why short max-age values appear: Developers set max-age=300 during testing to make it easier to revert HTTPS issues. That's reasonable in development. Shipping it to production is the mistake — users who visit during the 5-minute window get HSTS, but it expires quickly enough to provide minimal protection.
What it does: Controls whether this page can be loaded inside a <frame>, <iframe>, or <object>. Prevents clickjacking attacks where an attacker renders your site invisibly inside their own page and tricks users into clicking.
Grade bands:
Pass:DENY or SAMEORIGIN present
Warn:ALLOW-FROM uri — deprecated and not supported in most modern browsers
Fail: No header
Header values:
X-Frame-Options: DENY
or
X-Frame-Options: SAMEORIGIN
DENY prevents all framing. SAMEORIGIN allows framing from the same origin only. For most sites, DENY is the right choice.
The modern equivalent:Content-Security-Policy: frame-ancestors 'none' supersedes X-Frame-Options in modern browsers. If your CSP already includes frame-ancestors 'none', the checker notes this as a pass for both signals. Setting both is recommended for older browser coverage.
Where this causes client problems: Agencies frequently encounter X-Frame-Options: DENY on sites that the client wants to embed in a partner portal or third-party CMS. The fix is explicit — remove the header or change to SAMEORIGIN, and add frame-ancestors in the CSP with the specific allowed parent domain.
What it does: Prevents browsers from "MIME sniffing" — guessing a file's content type based on its contents rather than the declared Content-Type header. Stops attacks where a malicious file is disguised as an innocuous type.
Grade bands:
Pass:nosniff present
Fail: Missing
Header:
X-Content-Type-Options: nosniff
This is the simplest header to add and has no edge cases. It belongs on every site. The one context where it occasionally causes issues: servers that serve files with incorrect Content-Type headers. If you add nosniff and something breaks, the fix is to correct the Content-Type declaration — not to remove nosniff.
What it does: Controls how much referrer information is included in outgoing requests from your site. Affects both privacy (which pages users came from, exposed to third-party scripts) and analytics accuracy.
Grade bands:
Pass: A restrictive or moderate policy is set (strict-origin-when-cross-origin, no-referrer, same-origin)
Warn:unsafe-url — sends the full URL including path and query string to all destinations
Fail: No header (browser default applies, which varies)
Recommended header:
Referrer-Policy: strict-origin-when-cross-origin
This sends the full URL for same-origin requests (your analytics work correctly), sends only the origin for cross-origin requests over HTTPS (third-party scripts see your domain, not the specific page path), and sends nothing for cross-origin requests over HTTP (no data leakage to insecure destinations).
Why this matters for client sites: Sites using Google Analytics, Intercom, or advertising pixels are sending referrer information to those third parties for every page a user visits. strict-origin-when-cross-origin is the right balance between analytics functionality and privacy hygiene. It's also required for GDPR compliance in some interpretations.
What it does: Controls which browser features and APIs the page is allowed to use — camera, microphone, geolocation, payment, USB, and dozens of others. Prevents malicious third-party scripts from accessing device features without user consent.
Grade bands:
Pass: A Permissions-Policy header is present restricting at least a subset of sensitive features
Warn: Header is present but allows everything (empty policy or wildcard)
This denies camera and microphone access entirely, restricts geolocation and payment to first-party scripts only, and denies USB access. Adjust based on what the site actually uses — a site with a geolocation-based store finder needs geolocation=(self), not geolocation=().
Opinionated take: Permissions-Policy is the most frequently misconfigured header because most developers don't know what features their third-party scripts are requesting. The right approach: start with everything denied, then add back only what breaks. Running the site with a maximally restrictive policy in staging often reveals analytics or chat scripts making unexpected capability requests.
A site missing only Permissions-Policy scores in the high 80s. A site missing CSP and HSTS scores in the 30–40 range regardless of other headers. Prioritize in order of weight.
Setting headers in application code instead of the web server or CDN. Application-level headers are slower to deploy and easier to miss on edge cases. Put security headers in nginx config, Cloudflare transform rules, or Vercel headers config — not in middleware that might not run for certain routes.
Deploying a CSP without testing it. An untested CSP will break something. Always test in report-only mode first, collect violations for 48 hours, then enforce.
Adding preload to HSTS before confirming all subdomains are HTTPS. One HTTP-only subdomain after preload submission means your preload can't be applied — browsers require all subdomains to be HTTPS for preload eligibility.
Copying a CSP from Stack Overflow. Site-specific scripts, analytics tags, font sources, and API endpoints vary. A copied policy will either be too restrictive (breaking things) or too permissive (defeating the purpose).
Review your site's security grade alongside the full picture in What Your Audit Score Means — security is 20% of the overall audit score and the most actionable category for a quick win.
The audit one-liner for security headers: CSP with nonce or hash (no unsafe-inline), HSTS at one year with includeSubDomains, X-Content-Type-Options: nosniff, a restrictive Referrer-Policy, and deny-by-default Permissions-Policy — set all five at the CDN or server layer, not in application code.