Beginner levelFree

Security headers checklist 2026 — CSP, HSTS, X-Frame, Referrer-Policy and more

A complete audit of the HTTP security headers every modern application should have: real examples, correct configuration, common mistakes and how to report them.

Gorka El BochiMay 11, 202611 min

Quick answer

Security headers are the first line of defense against XSS, clickjacking, MITM and info leaks. Minimal audit in 2026: CSP (with frame-ancestors + no unsafe-inline), HSTS (preload + includeSubDomains), X-Content-Type-Options nosniff, Referrer-Policy strict-origin-when-cross-origin, Cookies Secure + HttpOnly + SameSite=Lax, an explicit Permissions-Policy. Header-only reports are usually info/low (€50-€200), but as a chain with another bug they can raise the severity 2 levels.


Full table — headers to check

HeaderRecommended valueWhat it preventsSeverity if missing
Strict-Transport-Securitymax-age=31536000; includeSubDomains; preloadMITM / HTTPS downgradeMedium
Content-Security-Policydefault-src 'self'; frame-ancestors 'none'; ...XSS, clickjacking, exfilMedium-High
X-Content-Type-OptionsnosniffMIME confusion XSSLow
X-Frame-OptionsDENY or SAMEORIGINClickjacking (legacy)Low (if CSP frame-ancestors exists)
Referrer-Policystrict-origin-when-cross-originToken leak via RefererLow-Medium
Permissions-Policycamera=(), microphone=(), geolocation=()Abuse of sensitive APIsLow
Cross-Origin-Opener-Policysame-originSpectre, window.openerLow-Medium
Cross-Origin-Embedder-Policyrequire-corpSpectre, side-channelLow
Cross-Origin-Resource-Policysame-originCross-origin leaksLow
Cache-Control (on auth endpoints)no-store, privateData leak via cacheMedium

Content-Security-Policy — the king header

CSP is the most complex and the most impactful. A poorly built CSP lets every XSS through; a well-built CSP blocks even reflected XSS.

Example of a correct CSP (modern SPA)

css
Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-RANDOM123' https://js.stripe.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  font-src 'self' data:;
  connect-src 'self' https://api.target.com wss://ws.target.com;
  frame-src https://js.stripe.com https://hooks.stripe.com;
  frame-ancestors 'none';
  form-action 'self';
  base-uri 'self';
  object-src 'none';
  upgrade-insecure-requests;
  report-uri /csp-report

Mistakes that kill the CSP

Anti-patternWhy it's bad
script-src 'unsafe-inline'Allows inline <script> → any reflected XSS passes
script-src 'unsafe-eval'eval(), Function(), setTimeout(string) → gadget chains
script-src https:Allows scripts from any HTTPS → protects nothing
script-src *.cdn.comIf the CDN serves user content (JSONP) → instant bypass
No frame-ancestorsClickjacking
No base-uri<base> injection → changes the origin of relative scripts
No form-actionForm hijacking in XSS to exfil credentials
default-src 'self' without object-src 'none'<object data="data:..."> executes legacy Flash

[!danger] frame-ancestors is NOT enough frame-ancestors 'none' prevents traditional iframing, but it does not prevent window.open() popups with clickjacking via UI confusion. To close the full vector: add Cross-Origin-Opener-Policy: same-origin + a window.top === window.self check in critical JS.

Test the CSP

bash
# See the header
curl -sI https://target.com | grep -i content-security

# Google's official evaluator
# Paste the CSP into https://csp-evaluator.withgoogle.com/

# Search for bypasses based on the whitelisted domains
# https://github.com/PortSwigger/csp-bypass

HSTS — the simple but critical header

ini
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
DirectiveMeaning
max-age=315360001 year in seconds. How long the browser forces HTTPS
includeSubDomainsApplies to all subdomains. Critical to prevent attacks via http subdomain takeover
preloadEnrollment in the Chrome/Firefox HSTS preload list (hardcoded)

Preload list

Enroll at hstspreload.org. Requirements:

  • Serve HSTS with max-age >= 31536000, includeSubDomains, preload.
  • Redirect HTTP → HTTPS on root + on all subdomains.
  • Support HTTPS on all subdomains (including mail.target.com).

Cookies — the flags that count

ini
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600
FlagWhat it doesWhen it's critical
SecureOnly over HTTPSAlways. Without it = leak via downgrade
HttpOnlyInaccessible from JSSessions, auth tokens — always
SameSite=LaxNot sent in cross-site POSTsSensible default. Protects CSRF on mutations
SameSite=StrictNot sent in ANY cross-site requestFor maximum-sensitivity tokens (banking)
SameSite=NoneSent cross-site (requires Secure)Third-party widget cookies (analytics, embeds)
__Host- prefixForces Secure, Path=/, no DomainDefense in depth for auth
__Secure- prefixForces SecureFor auth/session cookies

[!tip] SameSite Lax vs Strict in 2026 Chrome and Firefox already default to SameSite=Lax if not specified. But there are still endpoints in old frameworks that set it without an explicit SameSite → reportable. Strict breaks OAuth/SSO flows because the cookie doesn't arrive after the redirect → Lax is the sweet spot.


Referrer-Policy — the silent leak

sql
Referrer-Policy: strict-origin-when-cross-origin

Without this header, the browser sends the full Referer (including path + query) to any external site you link to. Result: password reset tokens, session IDs in the query, OAuth codes — all leaked to third parties.

ValueWhen
no-referrerMaximum privacy. For sensitive apps (medical, financial)
strict-origin-when-cross-originSensible default in 2026. Full origin same-origin, only origin cross-origin, nothing cross-origin http→https
same-originOnly same-origin sends the referrer, cross-origin sends nothing
unsafe-urlNEVER. Always sends the full URL

Reportable

URLs with tokens in the query (?reset_token=, ?session=, ?code=) + no restrictive Referrer-Policy → when the user clicks an external link, the token leaks to the third party. Medium severity if the token is still valid.


Permissions-Policy — the modern one (formerly Feature-Policy)

It used to be Feature-Policy. Renamed to Permissions-Policy in 2020+ and it supports more APIs.

ini
Permissions-Policy:
  camera=(),
  microphone=(),
  geolocation=(),
  payment=(self),
  usb=(),
  accelerometer=(),
  gyroscope=(),
  magnetometer=(),
  interest-cohort=()
  • camera=() → no origin can access it.
  • camera=(self) → only same-origin.
  • camera=(self "https://trusted.com") → same-origin + that specific one.
  • interest-cohort=() → opt out of FLoC/Topics API tracking.

Reportable if the app embedded in an iframe can activate mic/camera/geolocation without the user expecting it → defense in depth.


COEP / COOP / CORP — cross-origin isolation

Needed to use SharedArrayBuffer, high-precision performance.now(), and to mitigate Spectre/Meltdown.

makefile
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-origin
  • COOP same-origin: your window doesn't share a browsing context with cross-origin popups → closes window.opener attacks.
  • COEP require-corp: all loaded resources must have Cross-Origin-Resource-Policy or CORS. Enables "cross-origin isolation".
  • CORP same-origin: your resources can only be loaded from same-origin.

[!info] When to enable them If your app uses SharedArrayBuffer (WebAssembly, threading), you need COOP+COEP. If your app does NOT use them, COOP same-origin is a free win to close window.opener attacks. COEP breaks many third-party apps — implement carefully.


How to test it all at once

Tools

bash
# Quick CLI
curl -sI https://target.com

# Online — the de facto standard
# https://securityheaders.com/?q=target.com
# https://observatory.mozilla.org/analyze/target.com

# CSP specific
# https://csp-evaluator.withgoogle.com/

Manual audit script

bash
#!/bin/bash
URL="$1"

echo "=== Headers for $URL ==="
curl -sI "$URL" | grep -iE "(strict-transport|content-security|x-content-type|x-frame|referrer-policy|permissions-policy|cross-origin|set-cookie)"

echo -e "\n=== Cookies analysis ==="
curl -sI "$URL" | grep -i "set-cookie:" | while read -r line; do
    name=$(echo "$line" | sed 's/.*Set-Cookie: \([^=]*\)=.*/\1/i')
    flags=""
    echo "$line" | grep -qi "HttpOnly" || flags+="[NO HttpOnly] "
    echo "$line" | grep -qi "Secure" || flags+="[NO Secure] "
    echo "$line" | grep -qi "SameSite" || flags+="[NO SameSite] "
    [ -n "$flags" ] && echo "Cookie '$name': $flags"
done

How to report it (without being low-info spam)

Standalone headers are almost always info / low. For the report to have value:

  1. Don't send 10 separate reports. One report with a table of missing headers + aggregated impact.
  2. Demonstrate real impact. "CSP allows unsafe-inline" is not actionable. "CSP allows unsafe-inline AND there's a reflected XSS in /search → direct bypass" → Medium.
  3. Chain it with another bug. Missing header + working bug = severity boosted.
  4. Specific bug bounty programs. Some (Shopify, GitHub) explicitly say "Missing security headers = N/A". Read the scope.

Header report template

markdown
## Summary
The application lacks the following security headers, exposing users to clickjacking, amplified XSS and token leak via Referer.

## Affected headers
| Header | Expected | Actual |
|---|---|---|
| CSP | `default-src 'self'; frame-ancestors 'none'` | Absent |
| HSTS | `max-age=31536000; includeSubDomains` | `max-age=300` |
| X-Frame-Options | `DENY` | Absent |

## Impact
- No CSP: any reflected/stored XSS executes without restriction.
- Weak HSTS: 5-minute window for a MITM downgrade.
- No X-Frame-Options: clickjacking on the /transfer endpoint is possible.

## Clickjacking PoC
[HTML with an iframe of the /transfer endpoint + malicious overlay]

Hunting checklist

  • curl -sI the target → list all response headers
  • securityheaders.com for a target grade (A+ is the goal)
  • CSP in csp-evaluator.withgoogle.com → look for unsafe-inline, unsafe-eval, whitelisted *
  • HSTS: max-age >= 31536000 + includeSubDomains + preload
  • Auth cookies: Secure + HttpOnly + SameSite=Lax|Strict
  • Referrer-Policy present and restrictive
  • X-Frame-Options or CSP frame-ancestors on ALL pages
  • Permissions-Policy disabling unused APIs
  • COOP same-origin at minimum on auth-critical pages
  • Cache-Control on authenticated endpoints: no-store, private
  • Try a chain with XSS/CSRF/clickjacking to raise the severity

Practice exploiting applications with misconfigured headers and XSS → CSP bypass chains in the Security Headers labs.

Practice this in a lab

Security Headers

Solve

Keep learning · free account

Save your progress, unlock advanced payloads and rank your flags.

Create account
Premium · 1 more technique

There's an extra payload at the end

Why `frame-ancestors 'none'` in the CSP is NOT enough to prevent clickjacking in 2026, and the CSP rule that actually closes every vector.

Unlock

€7.99/mo · cancel anytime

Related articles

hunters training
711

hunters training

labs from real reports
55

labs from real reports

completions
1,205

completions

in bounties practiced
$213,970

in bounties practiced

46 flags captured this week·Real reports from HackerOne · Bugcrowd · Intigriti·No commitment·Free Academy