Security headers are one of the cheapest, fastest wins in web security. They take minutes to configure, cost nothing, and block entire classes of attacks — clickjacking, MIME sniffing, mixed content, and many XSS variants. Yet most sites either don't set them or set them incorrectly.
If you're auditing a site (yours or a client's), here's exactly how to check what headers are present, what they should say, and how to spot misconfigurations that scanners flag as serious issues.
The Headers That Actually Matter
Before checking anything, know what you're looking for. These are the headers that move the needle on a security audit:
- Strict-Transport-Security (HSTS) — Forces HTTPS for a set period.
- Content-Security-Policy (CSP) — Controls what scripts, styles, and resources can load.
- X-Frame-Options or
frame-ancestorsin CSP — Prevents clickjacking. - X-Content-Type-Options — Stops MIME-type sniffing.
- Referrer-Policy — Controls how much referrer info leaks to other sites.
- Permissions-Policy — Disables browser features like camera, geolocation, microphone.
- Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy — Isolate your origin from cross-origin attacks.
Anything else is either deprecated (like X-XSS-Protection) or niche.
Method 1: Browser DevTools (Quick and Free)
The fastest way to inspect headers on a single page:
- Open the site in Chrome or Firefox.
- Press
F12or right-click and choose Inspect. - Go to the Network tab.
- Reload the page.
- Click the first request (usually the document itself).
- Scroll to Response Headers.
You'll see every header the server returned. This is great for spot-checking one page, but it doesn't tell you whether values are correct — only what they are. A site can have a Content-Security-Policy header that's wide open with unsafe-inline 'unsafe-eval' * and DevTools won't flag it.
What to look for in DevTools
- Is
Strict-Transport-Securitypresent with at leastmax-age=31536000? - Does
Content-Security-Policyexist at all? If yes, does it include'unsafe-inline'or*inscript-src? - Is
X-Frame-Optionsset toDENYorSAMEORIGIN? - Is
X-Content-Type-Options: nosniffpresent?
Method 2: curl from the Command Line
For scripting, CI checks, or quickly comparing multiple URLs, curl is faster than DevTools.
curl -I https://example.comThe -I flag does a HEAD request and returns only headers. For sites that block HEAD, use:
curl -sD - -o /dev/null https://example.comTo grep for a specific header:
curl -sI https://example.com | grep -i "strict-transport-security"To check headers across a list of URLs in a shell script:
for url in $(cat urls.txt); do
echo "=== $url ==="
curl -sI "$url" | grep -iE "strict-transport|content-security|x-frame|x-content-type|referrer-policy|permissions-policy"
doneThis is the fastest way to audit a batch of staging environments or client sites before a deeper scan.
Method 3: Automated Scanners
Manual checks tell you what's there. Scanners tell you whether it's correct. A scanner will flag things like:
- HSTS missing
includeSubDomainsorpreload. - CSP that allows
'unsafe-inline'inscript-src. - CSP that uses
*as a source. - HSTS
max-ageunder six months. - Missing
frame-ancestorsdirective whenX-Frame-Optionsisn't set. - Cookies without
Secure,HttpOnly, orSameSite.
WebSentry runs all of these checks in one pass and grades each category A–F so you can see at a glance what needs work. It also checks adjacent issues — TLS configuration, DNS records like CAA and DMARC, CORS policy, and cookie flags — that header-only tools miss.
Reading a CSP Without Going Insane
CSP is the header that catches most people out. A real-world example:
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'Read it directive by directive:
default-src 'self'— fallback is the same origin only. Good baseline.script-srcuses a nonce, not'unsafe-inline'. Strong.style-srcstill allows'unsafe-inline'— common compromise for inline styles, weaker but pragmatic.frame-ancestors 'none'— equivalent toX-Frame-Options: DENY. Good.base-uri 'self'— prevents base tag injection. Often forgotten.
Red flags to watch for in any CSP:
'unsafe-inline'inscript-src(defeats most of the protection).'unsafe-eval'unless you genuinely need it.*orhttps:as a script source (allows any origin).- Missing
object-src 'none'ordefault-src.
Common Misconfigurations
HSTS on the apex but not subdomains
If your HSTS header lacks includeSubDomains, an attacker can MITM a subdomain like api.example.com over HTTP. Always include it once you're confident all subdomains support HTTPS.
Headers set on HTML but not on API responses
JSON endpoints need X-Content-Type-Options: nosniff too. Otherwise a browser may try to interpret a JSON response as HTML in certain conditions.
Setting headers in the app but losing them at the proxy
If you set headers in Express or Django but front everything with Nginx or Cloudflare, check the final response. Proxies sometimes strip or override headers. Always test the public URL, not localhost.
Conflicting CSP from multiple sources
If your app sends a CSP and Cloudflare adds another via Transform Rules, browsers intersect them — and the result is often broken. Set CSP in exactly one place.
Build It Into Your Workflow
Checking headers once isn't enough — deployments change behavior, plugins get added, CDN rules get tweaked. Bake it in:
- Add a curl-based header check to your CI pipeline post-deploy.
- Schedule a weekly scan against production. WebSentry can be run on-demand whenever you push major changes and gives you a graded report you can share with clients.
- Document your expected header values in a repo so any drift is obvious in code review.
Run a free scan at websentry.dev to see exactly which headers your site is missing, which values are weak, and what an A-grade configuration looks like for your stack.
Check your own site
Run a free security scan and see if your site has the issues covered in this article. Results in under 30 seconds.