Skip to content
SEO Optimizer is live, Audit on page SEO and AI-search readiness. Try it free
Sign in

How to Fix Missing Security Headers

Security headers are HTTP response headers that tell the browser to enforce a policy on your behalf - refuse to load your site in an iframe, refuse plain HTTP after the first HTTPS visit, refuse scripts from anywhere but your own domain. Missing or weak ones leave otherwise-secure application code exposed to entire classes of browser-level attacks.

Why Headers Go Missing or Stay Weak

  • Default hosting configurations ship with none of them. Most web servers and platforms don't add security headers unless you explicitly configure them.
  • A CSP gets added once, then never revisited. As an app adds new third-party scripts, embeds, or fonts, a static CSP written a year ago silently blocks them - or worse, someone widens it with a wildcard to make the errors stop, defeating the policy.
  • HSTS gets added without the directives that make it actually effective. A bare Strict-Transport-Security header with a short max-age and no includeSubDomains or preload offers far weaker protection than it appears to.
  • Deprecated headers copied from an old tutorial stick around, like X-XSS-Protection, which modern browsers ignore or which can itself introduce risk.

How to Identify the Problem

This is Scanverra's deepest security capability. The security scanner performs real CSP directive analysis - checking specifically for base-uri, form-action, object-src, and frame-ancestors, plus detecting overly broad wildcard sources - and a full HSTS depth check covering max-age, includeSubDomains, and preload. For the preload directive specifically, it makes a live call to the hstspreload.org API to confirm your domain is actually in Chrome's preload list, not just claiming to be. It also flags deprecated headers like X-XSS-Protection, and maps every finding to a CWE and OWASP Top 10 reference - for example, a missing HSTS header maps to CWE-319 and OWASP A05:2021 - so findings translate directly into compliance documentation.

How to Fix It

1. Add HSTS with the full set of directives

response headersEnforce HTTPS for a full year, across subdomains, and register for preload
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

After adding preload, submit your domain at hstspreload.org - the directive alone doesn't enroll you, and Scanverra's live check will keep flagging the gap until the submission is actually accepted.

2. Write a Content-Security-Policy scoped to what your site actually loads

response headersA starting CSP with the directives Scanverra checks for explicitly
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; base-uri 'self'; form-action 'self'; object-src 'none'; frame-ancestors 'self'

Add specific trusted origins to script-src/style-src only as needed - never fall back to a wildcard (*) or scheme-only source (https:) just to silence console errors, since that defeats the policy's purpose entirely.

3. Add the remaining standard headers

response headersClickjacking, MIME-sniffing, and referrer protection
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin

frame-ancestors in your CSP is the modern replacement for X-Frame-Options, part of protecting your site against clickjacking, and is more flexible - ship both, since older browsers only respect the legacy header.

4. Remove deprecated headers instead of leaving them in place

X-XSS-Protection is deprecated and OWASP explicitly recommends removing it entirely rather than setting it to any value - a real CSP is the modern replacement, not a companion to it.

Security Headers in Nginx

Add the headers to the server block. The always flag makes Nginx send them on error responses too. Note that an add_header inside a location block replaces all inherited headers, so repeat them there or keep them in an included snippet.

/etc/nginx/conf.d/security-headers.confSecurity headers in Nginx
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
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 Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "default-src 'self'; object-src 'none'; frame-ancestors 'self'" always;
server_tokens off;

Security Headers in Apache

Enable mod_headers and add the headers to your virtual host or .htaccess:

.htaccessSecurity headers in Apache
<IfModule mod_headers.c>
  Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
  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"
  Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
</IfModule>

Security Headers on Vercel, Netlify and Cloudflare

On Vercel, return headers from vercel.json or your framework config. On Netlify, use a _headers file in the publish directory. On Cloudflare, add a Transform Rule that sets response headers, or set them in a Worker. For Next.js apps specifically, framework-level config and a nonce-based CSP work better than platform rules.

public/_headersSecurity headers on Netlify
/*
  Strict-Transport-Security: max-age=63072000; includeSubDomains
  X-Frame-Options: SAMEORIGIN
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()

What each header protects against, one by one, is covered in website security headers explained. After any change, confirm the live response with the security headers checker.

How Scanverra Detects This

Scanverra's security scanner reads your live response headers and performs genuine directive-level analysis, not a simple presence check - CSP directive coverage and wildcard detection, HSTS value depth including a live hstspreload.org lookup, and deprecated header detection, each finding mapped to CWE, OWASP, and PCI references where applicable.

FAQ

Frequently asked questions

Strict-Transport-Security (HSTS), if you're missing it - it's the difference between "HTTPS is available" and "HTTPS is enforced," and it maps to CWE-319 and OWASP A05:2021, both commonly required for PCI compliance. A Content-Security-Policy is the next highest-leverage addition, but takes more tuning to get right.

It makes a live call to the hstspreload.org API for your hostname, rather than trusting your header alone - so if your HSTS header includes the preload directive but your domain was never actually submitted and accepted into Chrome's preload list, Scanverra flags that gap specifically.

It's deprecated, and in some older browsers it could actively introduce XSS vulnerabilities rather than prevent them - OWASP explicitly recommends removing it in favor of a real Content-Security-Policy. Seeing it present isn't a point in your favor; it's flagged as a finding to remove.

CWE (Common Weakness Enumeration) and OWASP Top 10 references map each finding to an industry-standard vulnerability classification - useful for pasting directly into a compliance ticket, a security review, or evidence for a SOC 2 / PCI audit, rather than just an informal recommendation.

No - a generic CSP either breaks your site's actual scripts/styles or is so permissive it does nothing. Scanverra's check looks at your specific directives (base-uri, form-action, object-src, frame-ancestors, and wildcard sources) so you can tighten exactly what's missing rather than guessing.

Free - no sign-up required

Check this on your own site

Run a free scan and see exactly where you stand - no sign-up required.

Run free audit