/ โ† Security Audit Fixes / Missing Security Headers
In this guide
  1. What a missing security headers finding means
  2. Which security headers matter, and their valid values
  3. How to check security headers on your site
  4. Why a security header is not valid
  5. Adding the headers at server, CDN or CMS level
  6. A worked example
  7. The fix, step by step
  8. Edge cases worth knowing
  9. Keeping the headers in place
  10. Questions and answers

What a missing security headers finding means

HTTP security headers are instructions your server sends with every response, telling the browser how to handle the page: always use HTTPS, which scripts may run, whether the page may be framed by another site, and how much of the URL to share when a visitor clicks away. They cost nothing to add and protect against whole classes of attack, such as clickjacking, content-type confusion and some cross-site scripting.

A vulnerability scanner or penetration test reports a finding such as Missing HTTP security headers when one or more of these is absent, or when a header is present but has a value the browser will ignore, often reported as security header is not valid. On its own, a missing header is usually rated low or informational, because it does not create a hole by itself. It removes a layer of protection that would limit the damage if something else went wrong.

That rating matters for how you respond. You do not need to take the site offline, but you should fix it, because the fix is quick, and because insurers, clients and compliance checklists increasingly expect to see these headers.

๐Ÿ’ก Before you change anything, save the scanner's exact wording and the URL it tested. Some findings apply only to one subdomain, one file type or error pages, and the fix depends on which.

Which security headers matter, and their valid values

These are the headers most scanners check for, with a safe starting value for a typical small business site.

HeaderSafe starting valueWhat it does
Strict-Transport-Securitymax-age=31536000; includeSubDomainsTells browsers to use HTTPS only for this host for a year
Content-Security-PolicyBuilt for your site; start in report-only modeLimits where scripts, styles, frames and other resources may load from
X-Content-Type-OptionsnosniffStops the browser guessing file types, so a text file is not run as a script
X-Frame-OptionsSAMEORIGINStops other sites framing your pages for clickjacking
Referrer-Policystrict-origin-when-cross-originSends only your domain, not the full URL, to other sites
Permissions-Policycamera=(), microphone=(), geolocation=()Switches off browser features your site does not use

Two older headers still appear in some scan reports. X-XSS-Protection controlled a filter that modern browsers have removed; current advice is to omit it or send 0, so if a scanner asks for 1; mode=block, note that the recommendation is outdated. Feature-Policy was replaced by Permissions-Policy, which uses a different syntax.

โš ๏ธ HSTS is hard to undo. Once a browser has seen it, it refuses plain HTTP for the whole max-age. Start with max-age=300, check every subdomain works over HTTPS, then raise it. Do not add preload until you are sure. See fixing HSTS errors.

How to check security headers on your site

The quickest check is from the command line, which shows exactly what the server sends:

curl -sI https://www.example.com/ | grep -iE 'strict-transport|content-security|x-content-type|x-frame|referrer-policy|permissions-policy'

Any header missing from the output is missing from the response. Run the same check on a few different URL types, because headers are often set for some responses and not others:

for u in / /contact/ /wp-login.php /images/logo.png /no-such-page; do
  echo "== $u"; curl -sI "https://www.example.com$u" | grep -icE 'strict-transport|x-frame|x-content-type'
done

A count that drops for the 404 or the image tells you the headers are being added by the application rather than the server, or only on successful responses.

Checking security headers online

If you would rather not use a terminal, an online checker fetches the page and grades each header. Our security audit lists missing and invalid headers with the value it found, and Mozilla's HTTP Observatory is a well-known free alternative. In the browser, open DevTools, go to the Network tab, reload, click the first document request and read the Response Headers section.

Why a security header is not valid

A header that is present but wrongly written is treated by the browser as if it were missing. These are the mistakes scanners flag most often:

ProblemExampleCorrect form
HSTS with no max-ageincludeSubDomainsmax-age=31536000; includeSubDomains
HSTS sent over plain HTTPHeader on the http:// responseBrowsers ignore it there; send it on HTTPS responses
Obsolete framing valueX-Frame-Options: ALLOW-FROM https://example.orgUse CSP frame-ancestors instead
CSP keywords without quotesscript-src selfscript-src 'self'
Old Feature-Policy syntaxPermissions-Policy: camera 'none'Permissions-Policy: camera=()
Duplicate headersTwo X-Frame-Options with different valuesSet it in one place only
Unknown valueReferrer-Policy: strictOne of the defined values, such as no-referrer

Duplicates are the hardest to spot. They happen when the application, a plugin, the web server and the CDN each add the same header. Check with curl -sI and count the lines for each header name; there should be one.

A Content Security Policy needs more care than the others, because a strict one can block your own scripts, analytics or payment forms. Roll it out with Content-Security-Policy-Report-Only first, as the CSP report-only guide explains.

Adding the headers at server, CDN or CMS level

Add the headers in one place, as close to the edge as you can, so every response gets them. On nginx, in the server block for HTTPS:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

The always flag adds the header to error responses too. Note that a location block with any add_header of its own stops inheriting these, so repeat them there or move them into an included file. More in the nginx security headers guide.

On Apache with mod_headers enabled, in the virtual host or .htaccess:

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"

Using set rather than add replaces any copy the application already sent, which avoids duplicates.

Other platforms

A worked example

A scan of www.example.com, an Example Bakery site on nginx behind Cloudflare, reported four findings: missing HSTS, missing X-Frame-Options, missing Content-Security-Policy and X-XSS-Protection not set.

  1. curl showed HSTS was present on the home page but missing on /shop/. The shop's location block had its own add_header for caching, which stopped inheritance. Moving the security headers into an included file and including it in both blocks fixed it.
  2. X-Frame-Options was added at server level with always.
  3. For CSP, the team sent a report-only policy for two weeks, collected the reports, added the payment provider's domain to frame-src and script-src, then switched to enforcing.
  4. X-XSS-Protection was left out on purpose, with a note in the response to the scanner explaining that the filter has been removed from modern browsers.

The rescan showed all required headers present with valid values on every URL type tested, including the 404 page.

The fix, step by step

Step 1

Record exactly what was flagged

Copy the scanner or pentest finding word for word, with the URLs tested and the headers named as missing or not valid. Note whether it tested HTTP or HTTPS, and whether it covered images, error pages or other subdomains. This tells you where the gap is, and it gives you something to compare against when you rescan. Keep the original report with the date, so the fix can be shown to whoever asked for it.

Step 2

Check the live headers with curl

Run curl -sI on the home page and on a few other URL types, such as a deep page, an image, a login page and a 404, and grep for the security header names. Note which are missing on which responses. If they appear on some and not others, the cause is usually inheritance or error responses, rather than the header never being set at all.

Step 3

Find what already sets headers

Look at every layer that could add headers: the application or CMS, plugins, the web server configuration, the control panel and the CDN. Search the configuration for add_header on nginx, Header set on Apache, or customHeaders on IIS. Pick one layer to own the security headers, ideally the web server or CDN, and plan to remove duplicates from the others. Write down which layer you chose, so the next person who edits the site knows where the headers live.

Step 4

Add the simple headers first

Add X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, Referrer-Policy: strict-origin-when-cross-origin and a Permissions-Policy that switches off features you do not use. On nginx use always, on Apache use Header always set. Test the configuration first with nginx -t or apachectl configtest, so a typing mistake does not take the site down. Reload the server, then run curl again and confirm each header appears exactly once on every URL type you checked, including the 404 page.

Step 5

Add HSTS carefully

Confirm every page and subdomain works over HTTPS, then add Strict-Transport-Security with a short max-age such as 300 on HTTPS responses. Check the site for a few days, then raise it to 31536000 and add includeSubDomains once each subdomain is confirmed. Leave preload until you are certain. Confirm with curl that the header appears on the HTTPS response with exactly the value you set.

Step 6

Roll out CSP in report-only mode

Write a starting policy listing the domains your site loads scripts, styles, fonts, images and frames from. Send it as Content-Security-Policy-Report-Only with a reporting endpoint, and leave it for one to two weeks. Review the reports, add the legitimate sources, then switch the header name to Content-Security-Policy. Afterwards, test checkout, contact forms, embedded maps and analytics with the browser console open, and look for any blocked resource messages that the reports missed.

Step 7

Fix invalid values and duplicates

Compare each header against its valid syntax: HSTS needs max-age, CSP keywords need single quotes, Permissions-Policy uses the feature=() form, and ALLOW-FROM is obsolete. Count each header in the curl output; any header listed twice needs removing from one layer. Use set rather than add on Apache so the server replaces copies the application sends, and check again with curl after each change until every header appears once.

Step 8

Rescan and document the result

Purge any CDN or page cache, then run the same scanner on the same URLs as the original report. Confirm each finding is closed. For items you chose not to apply, such as X-XSS-Protection, write a short note explaining why, citing current browser behaviour. Keep the before and after reports together, so you can show auditors or clients when and how each finding was fixed.

Edge cases worth knowing

Keeping the headers in place

Headers disappear after server migrations, control panel updates, new CDN rules and theme changes. Make checking them routine:

Questions and answers

1. How do I fix missing security headers?

You fix missing security headers by adding them to every response at one layer, usually the web server or CDN. On nginx use add_header with the always flag, on Apache use Header always set, on IIS use customHeaders in web.config, and on Cloudflare use response header transform rules. Start with X-Content-Type-Options, X-Frame-Options, Referrer-Policy and Permissions-Policy, add HSTS with a short max-age first, and roll out Content-Security-Policy in report-only mode before you enforce it, then confirm with curl.

2. Is a missing security header a real vulnerability?

A missing security header is a real finding but usually rated low or informational, because it does not open a hole by itself. It removes a layer of defence that limits damage from other problems, such as clickjacking, script injection or content-type confusion. Fix it because the change is quick and cheap, and because clients, insurers and compliance checklists increasingly expect these headers. Do not treat it as an emergency unless the scanner reports another, more serious issue alongside it.

3. How can I check security headers online?

You can check security headers online with a scanner that fetches your page and grades each header, such as our security audit or Mozilla's HTTP Observatory. Enter the full https address and read which headers are missing or invalid. For a quick manual check, open the browser's developer tools, go to the Network tab, reload, select the main document and read its response headers. curl with the -I option shows the same information from a terminal.

4. What does security header is not valid mean?

Security header is not valid means the header is present but its value does not follow the expected syntax, so the browser ignores it. Common causes are HSTS without a max-age, CSP keywords such as self written without single quotes, X-Frame-Options using the obsolete ALLOW-FROM value, Permissions-Policy written in old Feature-Policy syntax, and the same header sent twice with different values. Compare the value against its specification, correct the syntax, and remove any duplicates set by other layers.

5. Which security headers should every website have?

Every website should send Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options or a CSP frame-ancestors rule, Referrer-Policy and Permissions-Policy, plus a Content-Security-Policy suited to the site. The first five can be added with standard values in a few minutes. CSP needs more planning, because it lists the sources your scripts, styles and frames come from, and a wrong policy can break features. X-XSS-Protection is no longer recommended, and modern browsers ignore it, so leave it out unless a policy you must follow insists on a value of 0.

6. Why are my security headers missing on some pages?

Security headers go missing on some pages when they are added by only one part of the stack. On nginx, a location block with its own add_header stops inheriting the server-level headers, and headers without always are dropped from error responses. Images and scripts served from a CDN or storage bucket do not get your server's headers. Pages served from a cache may predate the change. Check several URL types with curl to find which responses lack them.

7. Should I add X-XSS-Protection to fix the scan?

No, you should generally not add X-XSS-Protection with a value of 1 and mode block, even if an older scanner asks for it. The filter it controlled has been removed from modern browsers, and in older ones it could be abused to cause problems of its own. Current guidance is to omit the header or send it with a value of 0, and rely on a Content Security Policy instead. Note your reasoning in the response to the scan.

Check your security headers

Our security audit fetches your pages, lists every missing or invalid security header with the value it found, and shows which responses lack them, so you can fix the right layer.

Open Security Audit Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All Security Audit Fixes  ·  Security headers on nginx  ·  Security headers on Apache  ·  Rolling out CSP in report-only mode  ·  Fixing HSTS missing from an HTTPS server  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.