Security

Website Security Headers Checklist 2026: CSP, HSTS, Permissions-Policy and the Rollout Order That Won't Break Your Site

The eight response headers every business website should send, what each one blocks, and copy-paste configs for Nginx, Apache, IIS, Vercel and Cloudflare.

TL;DR Eight HTTP response headers block most browser-side attacks: Strict-Transport-Security, Content-Security-Policy, X-Frame-Options (or frame-ancestors), X-Content-Type-Options, Referrer-Policy, Permissions-Policy, Cross-Origin-Opener-Policy and Cross-Origin-Resource-Policy. Roll them out in order of risk, ship CSP in report-only mode first, and re-scan after every deploy.

Checklist of HTTP security headers next to a website security grade

Security headers are instructions your server sends with every page that tell the browser what it may and may not do: only connect over HTTPS, only run scripts from approved origins, never let another site frame the page, and so on. They cost nothing, need no code changes, and close the gaps most automated scanners and security questionnaires look for first.

This is the checklist we use on client sites and on our own Angular site, including the mistakes we made rolling it out. Run your domain through the free ZetsApp Website Security Audit before and after, so you can see the grade move.

The 8 security headers at a glance

HeaderBlocksRecommended valueRisk of breaking the site
Strict-Transport-SecurityHTTPS downgrade and cookie theft on public Wi-Fimax-age=63072000; includeSubDomains; preloadLow, once every subdomain has HTTPS
Content-Security-PolicyCross-site scripting (XSS), data exfiltration, rogue third-party scriptsSite-specific allow-listHigh: test in report-only first
X-Frame-Options / frame-ancestorsClickjackingSAMEORIGIN and frame-ancestors 'self'Low, unless partners embed you
X-Content-Type-OptionsMIME sniffing (uploads executed as script)nosniffVery low
Referrer-PolicyLeaking full URLs (tokens, search terms) to other sitesstrict-origin-when-cross-originVery low
Permissions-PolicyEmbedded frames requesting camera, microphone, location, paymentcamera=(), microphone=(), geolocation=()Low: allow features you actually use
Cross-Origin-Opener-PolicyCross-window attacks and XS-Leakssame-origin-allow-popupsLow; plain same-origin can break OAuth and payment pop-ups
Cross-Origin-Resource-PolicyOther sites embedding your files (Spectre-style leaks)same-siteMedium if other domains hotlink your images

You can drop X-XSS-Protection. Modern browsers removed the filter it controlled, and the old filter could itself be abused. If you still send it, set it to 0.

The rollout order that won't break production

  1. Day 1, zero-risk headers: X-Content-Type-Options, Referrer-Policy, X-Frame-Options, Permissions-Policy. Deploy, re-scan, done.
  2. Day 1, HSTS with a short max-age: start at max-age=86400, confirm every subdomain serves HTTPS, then raise it to two years and add includeSubDomains; preload. Preloading is hard to undo, so only submit to hstspreload.org when you are sure.
  3. Week 1, CSP in report-only: send Content-Security-Policy-Report-Only with your best-guess policy and read the violations in the browser console (or collect them with report-to). Add each legitimate origin you find.
  4. Week 2, enforce CSP: switch the header name to Content-Security-Policy. Keep the report-only header alongside it with a stricter draft for the next iteration.
  5. Then COOP and CORP: check that sign-in pop-ups (Google, Microsoft, Stripe, PayPal) still work.

Writing a Content-Security-Policy that survives real life

CSP is the header with the most protection and the most breakage. A practical starting policy for a marketing or SaaS site:

default-src 'self';
script-src 'self' https://cdn.jsdelivr.net;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https:;
connect-src 'self' https://api.yourdomain.com;
frame-src https://www.youtube-nocookie.com;
object-src 'none'; base-uri 'self'; form-action 'self';
frame-ancestors 'self'; upgrade-insecure-requests

Four lessons from hardening our own site:

  • Inventory before you write. We grepped the codebase and our CMS content for every external host. The surprise was not the CDN scripts we knew about but a browser-side call to an AI API buried in one tool, plus scripts loaded on demand by two components.
  • Same-origin iframes inherit the parent's CSP. Blog posts we render inside srcdoc/document.write iframes run inline scripts. A policy without 'unsafe-inline' silently broke every one of them, even though the iframe looked separate. Either move that content to its own origin or accept 'unsafe-inline' for now and plan the migration.
  • A hash or nonce disables 'unsafe-inline'. If you add 'sha256-…' for one inline snippet, browsers ignore 'unsafe-inline' for everything else. Go all-in on hashes or nonces, or do not use them at all.
  • Remove eval() instead of allowing it. A calculator on our site used eval on user input. Replacing it with a 120-line whitelist parser let us drop 'unsafe-eval' and closed a self-XSS hole at the same time.

Copy-paste configs by platform

Nginx

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" 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 "camera=(), microphone=(), geolocation=()" always;

The always flag matters: without it Nginx omits the headers on 4xx and 5xx responses.

Apache (.htaccess, mod_headers)

Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"

Vercel (vercel.json)

{ "headers": [ { "source": "/(.*)", "headers": [
  { "key": "X-Content-Type-Options", "value": "nosniff" },
  { "key": "X-Frame-Options", "value": "SAMEORIGIN" } ] } ] }

On Vercel, vercel.json must sit in the root of the deployed folder. If you upload a pre-built dist folder, a vercel.json in the project root is ignored. This exact mistake kept our own headers from going live for weeks.

IIS / ASP.NET Core

Add headers in middleware with context.Response.OnStarting, call app.UseHsts() outside development, set AddServerHeader = false on Kestrel, and remove X-Powered-By in web.config under httpProtocol/customHeaders.

Headers are half the job: cookies, DNS and email

  • Cookies: session cookies need Secure; HttpOnly; SameSite=Lax. A missing Secure flag on an HTTPS site counts as a failure in most audits.
  • SPF and DMARC: without an enforced DMARC policy (p=quarantine or p=reject) anyone can send phishing that appears to come from your domain. Our audit flags this on most small-business sites we scan.
  • security.txt: publish /.well-known/security.txt with a contact address and expiry date (RFC 9116). Note that some static-site build tools skip dot-folders when copying, so check that the file is actually deployed.
  • Subresource Integrity: pin versioned CDN scripts with integrity="sha384-…" so a compromised CDN cannot inject code.

How to verify

Run the ZetsApp Website Security Audit after each step. It reads your live headers, certificate, cookies and DNS, grades the site A+ to F, and generates the missing headers for Nginx, Apache, IIS, Vercel, Netlify or Cloudflare in one block. It also produces a branded PDF you can attach to a client handover or a vendor security questionnaire.

If you would rather have it done for you, our cloud and DevOps team hardens headers, TLS and email authentication, usually within a day. Ask for a hardening quote.

Frequently asked questions

What are the most important HTTP security headers?

Strict-Transport-Security (forces HTTPS), Content-Security-Policy (limits which scripts can run), X-Frame-Options or CSP frame-ancestors (stops clickjacking) and X-Content-Type-Options: nosniff. Referrer-Policy and Permissions-Policy are quick wins on top.

Do security headers affect SEO?

Not directly as a ranking factor, but HTTPS is a confirmed signal, and headers like HSTS and a working CSP protect against injected spam links and malware that can get a site flagged or deindexed.

Will a Content-Security-Policy break my website?

It can, if it omits an origin your pages use. Deploy it first as Content-Security-Policy-Report-Only, fix every legitimate violation, and only then switch to the enforcing header.

Is X-XSS-Protection still needed?

No. Browsers removed the XSS auditor it controlled. Set it to 0 or remove it, and rely on a Content-Security-Policy instead.

How do I check my website's security headers?

Use a scanner such as the free ZetsApp Website Security Audit, which reads your live headers and grades them, or open your browser developer tools, select the page request in the Network tab and read the response headers.

Tags
Web SecuritySecurity HeadersCSPHSTSDevOps
Share this article