Website problems
Missing security headers
Security headers are HTTP response headers that instruct the browser to restrict what a page may do. Their absence does not mean a site is compromised: they are defence in depth, reducing the damage a vulnerability elsewhere can cause. They are worth adding in a specific order, because one of them — Content-Security-Policy — can break a working site if deployed carelessly, while the others are almost always safe.
The ones that matter, in the order to add them
`X-Content-Type-Options: nosniff` stops the browser from guessing a response's type and executing something declared as an image. One line, no risk of breakage. Add it first.
`Referrer-Policy` controls how much of the current URL is sent when a visitor follows a link out. `strict-origin-when-cross-origin` is a sensible default and is what most browsers now apply anyway.
`Strict-Transport-Security` forces HTTPS. Safe once HTTPS works everywhere, and hard to undo — start with a short max-age.
`Permissions-Policy` disables browser features the site does not use: camera, microphone, geolocation. Low risk, and it limits what an injected third-party script can request.
`Content-Security-Policy` restricts where scripts, styles and frames may load from. It is the most effective of the set against cross-site scripting, and the only one that will break a site that relies on inline scripts or many third parties. Deploy it in report-only mode first, read the reports, then enforce.
What is obsolete and what replaced it
`X-Frame-Options` is superseded by CSP's `frame-ancestors` directive, which is more expressive. Sending both is harmless and still common for older browsers.
`X-XSS-Protection` is obsolete. The browser filter it controlled has been removed from modern browsers, and in some versions it introduced vulnerabilities of its own. Sending it does nothing useful.
`Server` and `X-Powered-By` are not security headers but disclosure: they name the software and version running. Removing them is not protection, but it removes a free hint.
How to check it yourself
`curl -I https://example.com` prints the response headers. Read them against the list above.
Check more than the home page. Headers are often set by the web server for static routes and forgotten on API responses or application routes served by a different layer.
A CSP is present but useless if it contains `unsafe-inline` for scripts, which permits exactly what CSP exists to stop. Present is not the same as effective.
How to deploy CSP without breaking things
Send `Content-Security-Policy-Report-Only` with the intended policy and a reporting endpoint. Nothing is blocked; violations are reported.
Leave it in place long enough to see the real traffic, including the pages nobody tests and the third parties that load conditionally.
Fix the violations — usually by moving inline scripts into files or adding nonces — then switch the header to enforcing.
Expect to iterate. A CSP written in one sitting and enforced immediately is how a checkout page stops working on a Friday evening.
How VeriFixScan detects it
`security.csp`, `security.x_content_type_options`, `security.x_frame_options`, `security.referrer_policy`, `security.permissions_policy`, `security.coop` and `security.corp` read the response headers of the pages crawled and report which are absent or permissive.
`security.server_disclosure` reports software and version banners.
`transport.hsts_directives` reads the HSTS parameters, since the header's value matters as much as its presence.
The report distinguishes a header that is absent from one that is present and permissive, because the second is the one that gives false confidence.
Where to set them, and why it decides the outcome
A header can be added at four layers, and the choice determines whether the site ends up protected or merely appearing to be.
At the CDN or edge. One place, applies to everything, survives a change of backend. It is also the layer most likely to be forgotten when a new route bypasses the CDN, and it cannot generate a per-response nonce, which rules it out for a serious CSP.
At the web server, in the nginx or Apache configuration. Reliable for everything that server handles, and invisible to the application — which means a developer reading the code has no idea the headers exist.
In application middleware. The only layer that can set a per-request nonce, and the only one that can vary a policy by route. It also misses anything the application does not serve: static files, error pages generated upstream, redirects issued before the request arrives.
Per response, by hand. Consistent only by accident.
The failure this produces is always the same shape: headers present on the home page and absent somewhere else. API responses served by a different process. A static asset host. The 404 and 500 pages, which are frequently generated above the application. An upload or media subdomain nobody thought of as part of the site.
So the check that matters is not "are the headers set" but "are they set on every kind of response" — and the cheapest way to answer it is to request one of each and read what comes back.
One layer must be chosen as the one that owns them. Two layers both setting a policy produces duplicate headers, and browsers enforce the intersection of both, which is the strictest combination rather than the one anybody designed.
Frequently asked questions
- Are security headers required?
- No standard requires them. They are defence in depth: each one reduces what an attacker can do with a vulnerability elsewhere, and none of them fix the vulnerability itself.
- Which single header gives the most benefit?
- A well-written Content-Security-Policy, by a wide margin, because it targets cross-site scripting directly. It is also the only one that can break a working site, which is why it is deployed last and in report-only mode first.
- Should I still send X-XSS-Protection?
- No. The browser feature it controlled has been removed, and past versions introduced their own issues. MDN documents it as non-standard and deprecated.
Sources
Related
VeriFixScan crawls a site and applies its checks to every page it reaches, keeping the evidence behind each finding. Scanning one website is free.
Scan a website