Developer Tools

HTTP Security Headers: The Complete Hardening Guide

PAYATE Team· · 19 min read
HPAYATE

Every time a browser requests a page, the server replies with two things: the content, and a set of HTTP response headers — metadata that describes how the browser should treat that content. Most headers are mundane (content type, caching, compression). But a specific group of them are among the cheapest and most effective security upgrades you can make to any website. They’re called HTTP security headers, and adding a handful of lines to your server config can shut down entire classes of attack — clickjacking, cross-site scripting, protocol downgrades, MIME sniffing, and more.

The best part: they’re invisible to users, require no code changes to your app, and take effect immediately. This guide explains each important security header, what attack it prevents, exactly what to set, and how to verify it — which you can do for any URL right now with the free HTTP Headers Checker.

Why Headers, and Why They Matter So Much

Browsers are powerful, and that power can be turned against your users. An attacker who finds a way to inject a script, frame your page, or intercept a connection can steal sessions, deface content, or phish credentials. Security headers are the browser’s instruction set for refusing to do dangerous things. They move enforcement from your application code (which can have bugs) into the browser itself (which enforces consistently).

Because they’re set at the server or CDN level, you can roll them out across an entire site at once. A WordPress blog, a React SPA, a PHP app, or a static site all benefit identically. Let’s go through them in order of impact.

1. Strict-Transport-Security (HSTS)

What it prevents: protocol downgrade and SSL-stripping attacks, where an attacker on the network forces a victim onto insecure http://.

Even if you redirect HTTP to HTTPS, there’s a window: the very first request to http:// can be intercepted before the redirect happens. HSTS closes that window by telling the browser “for the next N seconds, always use HTTPS for this domain, no exceptions, and don’t let the user click through certificate warnings.”

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  • max-age=31536000 — remember this for one year (in seconds).
  • includeSubDomains — apply to every subdomain too. Only add this once you’re sure all subdomains support HTTPS.
  • preload — opt into the browser preload list so the very first visit is already protected. Submit your domain at hstspreload.org after testing.

Caution: HSTS is sticky. If you enable includeSubDomains or preload and later break HTTPS on a subdomain, users will be locked out until the max-age expires or you preload a fix. Start with a short max-age, confirm everything works, then raise it.

2. Content-Security-Policy (CSP)

What it prevents: cross-site scripting (XSS) and data injection — the most common and damaging web vulnerability.

CSP is the most powerful security header and also the most involved. It defines a whitelist of where the browser may load resources from — scripts, styles, images, fonts, frames — and refuses anything else. Even if an attacker manages to inject a <script> tag, a good CSP stops it from executing because it doesn’t come from an allowed source.

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none'; base-uri 'self'; object-src 'none'

Reading that policy:

  • default-src 'self' — by default, only load resources from your own origin.
  • script-src — scripts may come from your origin and a named CDN. Avoid 'unsafe-inline' and 'unsafe-eval' for scripts if at all possible; they defeat much of CSP’s protection.
  • frame-ancestors 'none' — nobody may frame your page (the modern replacement for X-Frame-Options).
  • object-src 'none' — block legacy plugins like Flash.

How to Roll Out CSP Safely

CSP can break your site if you’re too strict too fast, so deploy it in two phases:

  1. Report-only mode first. Use Content-Security-Policy-Report-Only with a report-uri/report-to endpoint. The browser reports violations without blocking anything, so you can see what your site actually loads.
  2. Tighten and enforce. Once the reports are clean, switch to the enforcing header. Prefer nonces or hashes for inline scripts over 'unsafe-inline'.

A strong CSP is more work than the other headers, but it’s the single biggest defence against XSS — worth the effort for any site handling logins or sensitive data.

3. X-Frame-Options

What it prevents: clickjacking — an attacker loads your page in an invisible iframe over their own, tricking users into clicking buttons they can’t see.

X-Frame-Options: DENY

Values are DENY (never allow framing) or SAMEORIGIN (only your own site may frame the page). This header is older and slightly less flexible than CSP’s frame-ancestors, but it’s still widely respected and worth setting for older browsers. If you set both, use frame-ancestors in CSP as the primary control and keep X-Frame-Options as a fallback.

4. X-Content-Type-Options

What it prevents: MIME-type sniffing attacks, where a browser second-guesses a file’s type and executes something you served as plain data.

X-Content-Type-Options: nosniff

This one is simple and should be on every response. It tells the browser: “trust the Content-Type I sent; don’t try to guess.” Without it, an attacker who can upload a file might get the browser to treat a disguised file as executable script. There’s no downside to nosniff, so there’s no reason not to set it.

5. Referrer-Policy

What it prevents: leaking sensitive URLs and user data to third parties through the Referer header.

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

When a user clicks a link or your page loads a third-party resource, the browser normally sends the full URL of your page as the referrer. If your URLs contain tokens, IDs, or sensitive paths, that’s a privacy leak. strict-origin-when-cross-origin (now the browser default) sends the full URL to your own origin but only the origin (not the path) to other sites, and nothing when downgrading from HTTPS to HTTP. For maximum privacy, no-referrer sends nothing at all.

6. Permissions-Policy

What it prevents: abuse of powerful browser features — camera, microphone, geolocation, USB — by your own page or embedded third parties.

Permissions-Policy: geolocation=(), camera=(), microphone=(), payment=()

Formerly called Feature-Policy, this header lets you explicitly disable features your site doesn’t use. An empty allow-list () means “no one, not even me.” If a third-party script or a compromised embed tries to prompt for the camera or location, the browser refuses. Disabling what you don’t need shrinks your attack surface for free.

7. Cross-Origin Headers (COOP, COEP, CORP)

A newer family of headers isolates your page from cross-origin attacks like Spectre-style side channels and cross-window leaks:

  • Cross-Origin-Opener-Policy (COOP) — same-origin isolates your browsing context from windows you open or that open you.
  • Cross-Origin-Embedder-Policy (COEP) — require-corp ensures all embedded resources explicitly opt in.
  • Cross-Origin-Resource-Policy (CORP) — controls who may embed your resources.

Together COOP + COEP unlock powerful features like SharedArrayBuffer while hardening isolation. They’re more advanced and can require careful testing with third-party embeds, so treat them as a second-phase hardening step after the fundamentals above.

Headers You Should REMOVE

Security isn’t only about adding headers — some headers leak information and should be stripped:

  • Server — revealing nginx/1.18.0 or Apache/2.4.41 tells attackers exactly which version to target. Minimise or generic-ise it.
  • X-Powered-By — broadcasting PHP/8.1 or your framework version is pure gift to attackers. Remove it.
  • X-AspNet-Version and similar — same story.

The principle is “don’t volunteer your stack.” It won’t stop a determined attacker, but there’s no reason to make reconnaissance easy. You can see exactly which of these headers your server currently sends with the HTTP Headers Checker.

How to Check Your Security Headers

You can’t fix what you can’t see. The fastest way to audit any site is to look at its live response headers:

  1. Open the HTTP Headers Checker.
  2. Enter your URL (it follows redirects and shows the full chain).
  3. Review the security-header checklist — it flags which of HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy and Permissions-Policy are present or missing.
  4. Read the full response headers to spot information leaks like Server and X-Powered-By.

Run it on a few big sites you admire and copy what they do — it’s a great way to learn sensible real-world values. Then run it on your own site to build a punch list.

Where to Add the Headers

You set security headers once, at the layer closest to the user:

  • Nginx — add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; in your server block.
  • Apache — Header always set X-Content-Type-Options "nosniff" in .htaccess or the vhost.
  • CDN (Cloudflare, Fastly, etc.) — add response-header rules in the dashboard; this applies site-wide without touching your origin.
  • Application / framework — middleware in Express, Django, Laravel, ASP.NET, etc. Libraries like Helmet (Node) set sensible defaults in one line.

Pick one layer to avoid conflicting duplicate headers, and verify the result with the HTTP Headers Checker after each change.

A Sensible Starter Set

If you want a solid baseline to paste in and refine, start here and then build a real CSP in report-only mode:

Strict-Transport-Security: max-age=63072000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

This immediately kills clickjacking, MIME sniffing, and protocol downgrades, tightens referrer leakage, locks down unused device features, and begins measuring what you’ll need for a full CSP — all without risking an outage.

Common Configuration Pitfalls

Even experienced teams trip over the same handful of mistakes. Watch for these:

  • Setting headers in two places at once. If both your application and your CDN add a header, you can end up with duplicate or conflicting values, which browsers handle unpredictably. Choose a single layer to own each header.
  • Enabling HSTS before HTTPS is fully ready. HSTS is sticky — if you turn it on (especially with includeSubDomains or preload) and a subdomain still lacks a valid certificate, those pages become unreachable until the max-age expires. Always confirm every host serves clean HTTPS first.
  • A CSP so strict it breaks the site, so you disable it entirely. The all-or-nothing trap. Use report-only mode to tune gradually instead of giving up.
  • Forgetting redirects. Your apex-to-www or HTTP-to-HTTPS redirect responses should carry the same security headers (especially HSTS). The redirect-chain view in the HTTP Headers Checker makes it easy to confirm each hop is protected.
  • Wildcarding CORS on an authenticated API. Access-Control-Allow-Origin: * plus credentials is a serious data-exposure risk. Reflect only specific trusted origins.
  • Assuming headers protect non-HTML responses. JSON APIs, file downloads, and error pages also benefit from nosniff and sensible caching — apply headers globally, not just to pages.

Headers for APIs and Single-Page Apps

Modern architectures split the front-end (a React/Vue SPA) from a JSON API, and each has its own header priorities. For the API, the big ones are correctly scoped CORS, Content-Type: application/json with nosniff so responses are never interpreted as HTML, Cache-Control: no-store on anything containing user data, and strict authentication headers. A CSP is less relevant to a pure JSON endpoint (there’s no HTML to inject into), but it’s still wise to send a minimal one in case an error page renders.

For the SPA itself, the full suite applies: HSTS, a CSP that allows your bundled scripts and whatever third-party widgets you use, framing protection, and secure cookies if you use them for sessions. One SPA-specific note: because routing happens client-side, make sure your server still sends security headers on the single HTML entry point and on every asset, not just on the “home” route. Verify a few deep asset URLs with the HTTP Headers Checker to be sure the headers aren’t only present on the index page.

Security Headers and SEO

While security headers aren’t a direct ranking factor, they support SEO indirectly. HTTPS (which HSTS enforces) is a confirmed ranking signal. A site that’s been defaced or compromised via XSS can be flagged by Google Safe Browsing and dropped from results entirely. And a fast, secure, well-configured site earns trust — from users and from search engines. Hardening your headers is part of running a professional site, and professional sites rank.

No discussion of HTTP security is complete without cookies, because a stolen session cookie is often the real prize behind an XSS or network attack. Three attributes on the Set-Cookie header harden them dramatically:

  • Secure — the cookie is only ever sent over HTTPS, so it can’t be captured on a plaintext connection.
  • HttpOnly — JavaScript cannot read the cookie, which neutralises most session-theft-via-XSS. Any cookie holding a session token should have this.
  • SameSite — controls whether the cookie is sent on cross-site requests. SameSite=Lax (the modern default) or Strict is a strong defence against CSRF (cross-site request forgery).
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/

When you inspect a site with the HTTP Headers Checker, look at its Set-Cookie headers: session cookies without HttpOnly and Secure are a red flag you can fix in minutes.

Caching Headers: Performance Meets Security

Caching headers aren’t “security” headers in the strict sense, but misconfigured caching leaks data, so they belong in any audit. The key ones:

  • Cache-Control — the master control. Use public, max-age=31536000, immutable for versioned static assets, and no-store for anything containing personal or sensitive data so it’s never written to disk or a shared cache.
  • ETag / Last-Modified — let the browser revalidate without re-downloading.

The security angle: never let authenticated pages (account dashboards, order details) be cached by shared proxies. A Cache-Control: private, no-store on those responses prevents one user’s data leaking into another’s cache — a surprisingly common real-world bug.

Real CSP Examples for Common Stacks

Because CSP trips people up most, here are realistic starting points for three common setups. Always begin in report-only mode and tighten from there.

A Mostly-Static Marketing Site

Content-Security-Policy: default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self'; frame-ancestors 'none'; base-uri 'self'; object-src 'none'; upgrade-insecure-requests

A WordPress Site

WordPress and its plugins lean on inline scripts and styles, so a strict CSP takes tuning. Start permissive, watch the violation reports, and progressively restrict — ideally moving toward nonces for inline scripts rather than blanket 'unsafe-inline'.

A Single-Page App (React/Vue)

SPAs usually bundle their JS, which makes script-src 'self' achievable. The challenge is third-party widgets (analytics, chat, payments) — add each to the appropriate directive explicitly, and prefer providers that support nonces or hashes.

The upgrade-insecure-requests directive is a handy helper that rewrites any stray http:// sub-resource to https://, catching mixed-content issues automatically.

A Complete Server Config Example

Here’s a drop-in baseline for Nginx that sets the safe headers site-wide. Put it in your server block and reload:

add_header Strict-Transport-Security "max-age=63072000; 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 "geolocation=(), camera=(), microphone=()" always;
# CSP: start in report-only, then switch to enforcing
add_header Content-Security-Policy-Report-Only "default-src 'self'" always;
# hide server details
server_tokens off;

The Apache equivalent uses Header always set directives in your vhost or .htaccess, and ServerTokens Prod / ServerSignature Off to trim the Server header. On a CDN like Cloudflare, you add the same headers as response-header transform rules in the dashboard — which applies them without touching your origin at all.

Testing and Monitoring Headers Over Time

Headers aren’t “set once and forget.” A deployment, a new plugin, or a CDN change can silently remove or weaken them. Build verification into your routine:

  • After every deploy, re-run the HTTP Headers Checker on key pages to confirm nothing regressed.
  • Watch CSP reports while in report-only mode to catch resources you missed before enforcing.
  • Re-audit quarterly even on stable sites — browser best practices evolve (COOP/COEP and Permissions-Policy are relatively recent additions).
  • Check redirects too — a redirect from http to https or from apex to www should itself carry HSTS, which the checker’s redirect-chain view makes easy to confirm.

Subresource Integrity (SRI): Trusting Third-Party Scripts

Security headers control where resources load from, but what if an allowed CDN itself gets compromised and serves a malicious version of a script? Subresource Integrity (SRI) answers that. It’s not a header but an attribute you add to <script> and <link> tags: a cryptographic hash of the exact file you expect.

<script src="https://cdn.example.com/lib.js"
        integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
        crossorigin="anonymous"></script>

If the fetched file doesn’t match the hash — because it was tampered with or swapped — the browser refuses to run it. Pairing SRI with a strict CSP gives you defence in depth: CSP limits which sources are allowed, and SRI guarantees the content from those sources is exactly what you vetted. For any third-party script you pin to a fixed version, SRI is a cheap, powerful safeguard.

CORS: Getting Cross-Origin Right

Cross-Origin Resource Sharing (CORS) is where security headers meet APIs, and it’s widely misconfigured. By default, browsers block a page on one origin from reading responses from another origin — a protection called the same-origin policy. CORS headers let a server opt in to sharing with specific origins:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization

The cardinal sin here is Access-Control-Allow-Origin: * combined with credentials — effectively telling every website on the internet it may call your authenticated API. Instead, reflect only the specific trusted origins you intend to serve, and never wildcard an API that uses cookies or tokens. When debugging a “CORS error,” inspect the actual response headers with the HTTP Headers Checker to see exactly what the server is (or isn’t) allowing.

Legacy Headers to Retire

Web security evolves, and a few headers that were once recommended are now obsolete or even harmful:

  • X-XSS-Protection — this old header controlled the browser’s built-in XSS filter, which has been removed from modern browsers because it caused its own vulnerabilities. Set it to 0 or omit it, and rely on CSP instead.
  • Public-Key-Pins (HPKP) — certificate pinning via headers proved too dangerous (sites bricked themselves) and is deprecated. Use Certificate Transparency and CAA records instead.
  • Expect-CT — largely obsolete now that Certificate Transparency is enforced by default.

Seeing these on a site is a sign its security config hasn’t been reviewed in a while — a good prompt to audit everything else too.

Defense in Depth: Where Headers Fit

It’s important to keep perspective: security headers are a layer, not the whole wall. The principle of defense in depth says you assume any single control can fail and stack several so an attacker has to beat all of them. Headers sit alongside:

  • Secure coding and input validation — the first line against injection. A CSP mitigates XSS, but you should still escape output and validate input so the hole never exists.
  • A Web Application Firewall (WAF) — filters malicious requests before they reach your app, catching common attack patterns.
  • TLS configuration — strong ciphers and a valid, current certificate (verify with the SSL Certificate Checker). HSTS only helps if HTTPS itself is solid.
  • Authentication and session management — secure cookies, short-lived tokens, and MFA.
  • Patching and dependency hygiene — most breaches exploit known, unpatched vulnerabilities.

Headers are the cheapest and fastest of these layers to deploy, which is why they’re the ideal starting point — but treat them as the beginning of a security program, not the end of one.

A Real-World Incident Walkthrough

To see why these headers matter, consider a common attack chain and where each header would have broken it:

  1. An attacker finds a comment field that doesn’t properly escape input and injects a <script> that steals session cookies. — A strict CSP blocks the injected script from executing because it isn’t from an allowed source. HttpOnly cookies mean even a running script can’t read the session token.
  2. Failing that, the attacker embeds your banking page in an invisible iframe on a lookalike site to harvest clicks. — X-Frame-Options / frame-ancestors stops the page from being framed at all.
  3. On a coffee-shop Wi-Fi, the attacker tries to downgrade a victim’s connection to HTTP and intercept it. — HSTS forces the browser to refuse anything but HTTPS for your domain.
  4. The attacker uploads a “.jpg” that’s actually a script and tricks the browser into executing it. — X-Content-Type-Options: nosniff makes the browser honour the declared type instead of guessing.

No single header is a silver bullet, but together they close off entire attack avenues — which is exactly why running the HTTP Headers Checker and filling the gaps delivers such outsized value for so little effort.

Grading Your Site: A Practical Scorecard

To turn all of this into an action plan, grade your site against a simple tiered scorecard and work upward:

  1. Tier 1 — The non-negotiables (do today): HTTPS everywhere, HSTS, X-Content-Type-Options: nosniff, framing protection (X-Frame-Options or CSP frame-ancestors), and secure cookies (Secure; HttpOnly; SameSite).
  2. Tier 2 — Strong hardening (this week): a Referrer-Policy, a Permissions-Policy locking down unused features, hidden server/version headers, and a Content-Security-Policy in report-only mode.
  3. Tier 3 — Advanced (this month): an enforcing CSP with nonces/hashes, Subresource Integrity on third-party scripts, correctly scoped CORS, and the cross-origin isolation headers (COOP/COEP) where you need them.

Measure where you stand right now by running your key pages through the HTTP Headers Checker, then tackle the tiers in order. Most sites can clear Tier 1 and Tier 2 in a single afternoon, which already eliminates the large majority of header-preventable attacks. Tier 3 is where you go from “well-configured” to “genuinely hardened,” and it’s well worth it for anything handling accounts, payments, or sensitive data.

Frequently Asked Questions

Will adding security headers break my site?

Most won’t — nosniff, X-Frame-Options, and Referrer-Policy are safe to add immediately. The two to deploy carefully are HSTS (sticky; start with a short max-age) and CSP (roll out in report-only mode first). Always verify with the HTTP Headers Checker.

What’s the single most important security header?

Content-Security-Policy, because it defends against XSS, the most common serious web vulnerability. HSTS is a close second for enforcing HTTPS.

Is X-Frame-Options still needed if I use CSP frame-ancestors?

CSP’s frame-ancestors is the modern control, but keeping X-Frame-Options as a fallback covers older browsers. Setting both is harmless.

How do I check headers on a page behind a login?

Public header checkers only see public pages. For authenticated pages, use your browser’s DevTools (Network tab) to inspect the response headers while logged in.

Do static sites need security headers?

Yes. Even a static site benefits from HSTS, nosniff, framing protection, and a basic CSP — and they’re trivial to add at the CDN or host level.

How often should I review my security headers?

Re-check after every deployment (a config or plugin change can silently drop a header) and do a deeper audit quarterly, since browser best practices evolve — COOP/COEP and Permissions-Policy are relatively recent additions. A 30-second run through the HTTP Headers Checker after each release catches regressions before your users do.

Are security headers enough on their own?

No — they’re one layer of defense in depth. They dramatically reduce browser-side attacks like XSS, clickjacking, and protocol downgrades, but you still need secure coding, input validation, patched dependencies, strong TLS, and sound authentication. Think of headers as the fastest, cheapest layer to add — the ideal place to start, not to finish.

Conclusion

HTTP security headers are the rare security win that’s cheap, fast, and genuinely effective. A few lines in your server or CDN config shut down clickjacking, MIME sniffing, protocol downgrades, and — with a proper CSP — cross-site scripting, the most dangerous class of web attack. You don’t need to touch your application code, and the protection applies to every visitor instantly.

Start by auditing what you have: run your site through the HTTP Headers Checker and note what’s missing. Add the safe headers today, confirm HTTPS and your certificate are solid with the SSL Certificate Checker, then build a Content-Security-Policy in report-only mode and tighten it over a week or two. In an afternoon of work you can take any site from “default” to “hardened” — and verify every step along the way.

Key Takeaways

  • Add the safe headers today: HSTS, X-Content-Type-Options: nosniff, framing protection, Referrer-Policy, and a Permissions-Policy.
  • Build a Content-Security-Policy in report-only mode first, then enforce it — it’s your strongest defence against XSS.
  • Harden cookies with Secure; HttpOnly; SameSite, and scope CORS to specific trusted origins — never wildcard an authenticated API.
  • Remove information-leaking headers like Server and X-Powered-By, and retire obsolete ones like X-XSS-Protection and HPKP.
  • Set headers at one layer (server, CDN, or app), cover redirects and assets too, and re-verify after every deploy.
  • Treat headers as one layer of defense in depth alongside secure coding, TLS, patching, and authentication.
P
Written by PAYATE Team

The PAYATE team builds free tools for merchants and developers.

More PAYATE tools