Security Headers Checker
Your server answers every single visitor with a set of HTTP headers - and several of them exist purely to make attacks against your users harder. Most sites are missing half of them. Here's how to check yours.
Check your headers
The fastest way to see where you stand is the free scan - paste your URL and it checks every header below, flags what's missing, and shows what each one was protecting. Run it against your real production URL, not a staging copy.
You can also check by hand in seconds. Open your browser's developer tools, go to the Network tab, reload the page, click the main document request (the first one), and read the response headers. Or from any terminal: curl -sI https://yoursite.com prints the full set.
Check more than the homepage. Each redirect - http to https, www to non-www - gets its own response and its own headers, and one missed redirect can quietly strip HSTS. And if you sit behind a CDN, check what's actually served at the edge, not just what your origin sends.
The headers that matter
Eight headers cover the vast majority of real-world risk. Here's what each one does and what a missing one costs you.
Strict-Transport-Security
Tells the browser to only ever talk to your site over HTTPS for the next max-age seconds, and to refuse to fall back to HTTP. That's precisely the protection against SSL-stripping, where a man-in-the-middle quietly downgrades a visitor to an unencrypted connection and reads or alters the traffic. A typical value: max-age=31536000; includeSubDomains. Deploy it only once HTTPS works everywhere on your domain - browsers cache this policy aggressively, and a mistake here can lock visitors out.
Content-Security-Policy
The big one. CSP tells the browser exactly which sources are allowed to load scripts, styles, frames, and everything else - so a script injected from an attacker's domain simply doesn't run. It's the strongest defense-in-depth against cross-site scripting that exists. It's also the one that takes real tuning: your analytics, fonts, tag managers, and payment iframes all need to be allowed explicitly. Start with Content-Security-Policy-Report-Only, watch the reports for a couple of weeks, fix what breaks, then switch to enforcing.
X-Frame-Options and frame-ancestors
Controls who can embed your pages in an iframe. Without it, an attacker can load your site invisible inside their own page and trick logged-in users into clicking things they can't see - clickjacking. X-Frame-Options: DENY or SAMEORIGIN covers older browsers; the modern equivalent is the CSP directive frame-ancestors 'self'. Set one of them, and if you already enforce a CSP, frame-ancestors is the cleaner choice.
X-Content-Type-Options: nosniff
Stops the browser from "sniffing" content types and overriding what the server declared. The classic failure: a site lets users upload files, an attacker uploads a file full of JavaScript, the server serves it with a generic content type, and the browser decides it's executable anyway. One word - nosniff - closes that entire class of MIME-confusion attacks. There is no reason not to set it.
Referrer-Policy
Every time a visitor follows a link away from your site, or your page loads a third-party resource, the browser may send the current URL along in the Referer header. If your URLs carry tokens, IDs, or anything else sensitive, that leaks to third parties. Referrer-Policy: strict-origin-when-cross-origin keeps the origin on cross-site requests but drops the full path - a sane default for almost any site.
Permissions-Policy
Lets you disable powerful browser APIs your site never uses: camera, microphone, geolocation, USB, accelerometer. If your own code never calls them, there's no reason injected code should be able to either. Permissions-Policy: camera=(), microphone=(), geolocation=() is cheap insurance.
Cross-Origin-Opener-Policy and Cross-Origin-Resource-Policy
Briefly: COOP isolates your pages from cross-origin windows that open them - part of the Spectre-era defenses, and a requirement for cross-origin isolation in high-security apps. CORP declares which other sites are allowed to load your resources. Set them if you can; they're hardening, not critical.
Server header hygiene
This one isn't a protection - it's the opposite. Default server configs advertise exact versions: Server: nginx/1.18.0, X-Powered-By: PHP/8.1.4. That hands every scanner your version number to look up known CVEs against. Trim or remove them. It won't stop a determined attacker, but stop doing their reconnaissance for them.
How to read the results
Think in two tiers. Critical: Strict-Transport-Security, Content-Security-Policy, X-Frame-Options or frame-ancestors, and X-Content-Type-Options. Missing any of these is a real, exploitable gap under the right conditions. Nice-to-have: Referrer-Policy, Permissions-Policy, COOP, CORP, and a quiet Server header.
The most common finding by far is no CSP at all - most sites simply never set one, which leaves one reflected XSS a single script tag away from full account compromise. The second most common: a CSP stuck in Report-Only mode forever, technically present but protecting nothing.
Don't chase a perfect scanner grade by pasting someone else's policy. A CSP that breaks your checkout gets rolled back within a day. A working, slightly looser policy that you tighten over time beats an ideal one that never ships.
Fixing them
Headers are set in your web server or CDN config, not in application code - which is good news, because it's a handful of lines:
- nginx:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;- one add_header per policy - Apache:
Header always set Content-Security-Policy "default-src 'self'" - The
alwayskeyword matters: without it, error responses can be served without your headers. - Only CSP has a meta-tag equivalent, and it's weaker - no frame-ancestors, no report-only mode, and no protection until the HTML starts parsing. Set headers at the server.
- Behind Cloudflare or another CDN? It can add and modify headers too (Cloudflare: Transform Rules, then Modify Response Header). But the CDN layer can also override or strip what your origin sends - after any deploy, re-check with
curl -sIto see what visitors actually get.
Headers are hardening, not testing
Get these right and you've made whole attack classes measurably harder. But headers don't touch your actual application: SQL injection, broken access control, broken authentication, business-logic flaws - all of them work exactly the same against a perfect headers score. Hardening reduces risk at the edges; it doesn't test what's in the middle.
So don't let a green scan stand in for an assessment. If you want the application itself attacked the way an attacker would attack it, look at web application penetration testing - manual testing, not a re-badged scanner. If you're weighing how much testing you actually need, the difference between a penetration test and a vulnerability scan is worth five minutes. More free checks live on the tools page, and if you'd rather just talk it through, contact me.
Header questions
Content-Security-Policy gives you the biggest single win, because it's the main defense-in-depth against cross-site scripting. But it's also the hardest one to get right. Strict-Transport-Security has the best effort-to-value ratio: one line, and it blocks SSL-stripping attacks. If you only fix two things, fix those two.
No. They add a few hundred bytes to each response - less than most images on the page. There's no measurable processing cost on the client beyond parsing the header once per response. The performance cost is effectively zero.
Only Content-Security-Policy has a meta-tag version, and it's weaker: no frame-ancestors, no report-only mode, and no protection until the HTML starts parsing. The rest - HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy - are HTTP-only by design. Set them in your web server or CDN config.
Then set them at the CDN. Cloudflare can add or modify response headers with Transform Rules, and most CDNs have an equivalent. Whichever layer serves the response last wins, so after any deploy, re-check with curl -sI to confirm what visitors actually receive.
No. They harden the browser against whole attack classes like XSS and clickjacking, but they don't fix anything in your application - SQL injection, broken access control, and business-logic flaws all work regardless of your headers. Headers are one layer; the application behind them still needs real testing.
Headers clean but still unsure about the app behind them?
Headers are the seatbelt; the code is the car. Test the application itself.