Reference · Updated 2026

WordPress
security headers
reference.

Every HTTP header a serious WordPress site should send in 2026 — with the exact recommended value, why it matters, and the gotchas that break WordPress specifically. Copy, paste, test.

Header

Strict-Transport-Security (HSTS)

Why: Forces every browser that has ever visited your site to only use HTTPS for the next N seconds. Kills SSL-stripping attacks and stops downgrade attempts on public wifi.

Recommended value
max-age=63072000; includeSubDomains; preload

Notes: Only enable includeSubDomains after auditing every subdomain has valid HTTPS. Preload requires submission at hstspreload.org and is essentially irreversible — do it last, once you're certain.

Header

Content-Security-Policy (CSP)

Why: The single most important header. Controls what scripts, styles, images, iframes, and forms can load. Blocks nearly every stored-XSS and injected-crypto-miner attack even if a plugin gets compromised.

Recommended value
default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; frame-src 'self' https://www.youtube.com https://player.vimeo.com; connect-src 'self' https://www.google-analytics.com; object-src 'none'; base-uri 'self'; form-action 'self'

Notes: Ship in Report-Only mode first (`Content-Security-Policy-Report-Only`) for a week to a reporting endpoint before enforcing. WordPress with plugins usually needs 'unsafe-inline' for styles — accept that unless you can inline-hash-audit every plugin.

Header

X-Frame-Options

Why: Stops other sites embedding yours in an iframe (clickjacking). Superseded by CSP's frame-ancestors but still respected by older browsers — ship both.

Recommended value
SAMEORIGIN

Notes: Use DENY if you never need to embed your own pages (rare). Also add `frame-ancestors 'self'` to your CSP as the modern equivalent.

Header

X-Content-Type-Options

Why: Stops browsers 'sniffing' file types and executing an uploaded .txt as JavaScript. Critical for any WordPress site with user uploads.

Recommended value
nosniff

Notes: Zero downside, always enable.

Header

Referrer-Policy

Why: Controls how much URL info gets sent when a visitor clicks a link off your site. Default browser behaviour leaks the full URL to third parties — including query strings that can contain session data.

Recommended value
strict-origin-when-cross-origin

Notes: Good balance. Use `no-referrer` if privacy-critical (health, legal); use `same-origin` for internal admin panels.

Header

Permissions-Policy (formerly Feature-Policy)

Why: Disables browser APIs your site doesn't need — camera, microphone, geolocation, autoplay, USB, payments. A compromised plugin can't ask for the user's camera if you've turned the API off at the header level.

Recommended value
camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()

Notes: interest-cohort=() opts out of Google's FLoC / Topics API — worth including for privacy positioning.

Header

Cross-Origin-Opener-Policy

Why: Isolates your browsing context from cross-origin windows opened via window.open. Protects against tab-nabbing and Spectre-class side-channel attacks.

Recommended value
same-origin

Notes: May break OAuth popups (Google login, Stripe Checkout iframe) — test the auth flow after enabling. Use `same-origin-allow-popups` if it does.

Header

Cross-Origin-Resource-Policy

Why: Stops other sites hot-linking your images and scripts, and prevents your assets from being loaded into a compromised third-party context.

Recommended value
same-site

Notes: Use `cross-origin` for CDN-served assets you explicitly want embeddable (e.g. a logo hosted on a CDN sub-domain).

Header

X-XSS-Protection

Why: Legacy header. Modern browsers ignore it; older ones can be actively harmful with it enabled. Ship it disabled explicitly.

Recommended value
0

Notes: Yes, 0 not 1. See github.com/helmetjs/helmet — the modern recommendation is to disable the old XSS auditor and rely on CSP instead.

Copy-paste configs

Nginx (site config, inside `server `)

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Resource-Policy "same-site" always;
add_header X-XSS-Protection "0" always;
# CSP: start with Report-Only for a week
add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; frame-src 'self' https://www.youtube.com; report-uri /csp-report" always;

Apache (in .htaccess or vhost)

<IfModule mod_headers.c>
  Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set X-Content-Type-Options "nosniff"
  Header always set Referrer-Policy "strict-origin-when-cross-origin"
  Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()"
  Header always set Cross-Origin-Opener-Policy "same-origin"
  Header always set Cross-Origin-Resource-Policy "same-site"
  Header always set X-XSS-Protection "0"
</IfModule>

Cloudflare (Transform Rules → Response Header)

Add each header as a separate Response Header rule. Cloudflare's UI is per-header, which is actually easier than editing config — and headers set here survive a WordPress compromise entirely.

Free plan includes Response Header Transform Rules. Use them.

WordPress-specific gotchas

  • The customizer breaks under strict CSP. WordPress's Customizer live-preview uses postMessage inside an iframe with inline scripts. Add a CSP exception for /wp-admin/customize.php, or accept 'unsafe-inline' in script-src (which most WordPress sites do anyway).
  • Gutenberg needs 'unsafe-eval'. The block editor uses eval() for some block registration. If you strip it, the editor breaks. Scope 'unsafe-eval' to /wp-admin/ only via a separate CSP rule.
  • jQuery migrate emits inline scripts. WordPress core still ships wp-includes/js/jquery/jquery-migrate.min.js with inline hooks. Either accept 'unsafe-inline' or deregister jQuery migrate.
  • Elementor, Divi, and WPBakery inject inline everything. If you use these, strict CSP is essentially impossible without 'unsafe-inline'. This is one of many reasons block themes exist.
  • HSTS preload is nearly irreversible. Once on the preload list, removing takes months. Only submit once you've run HSTS with preload directive for at least 30 days without incident.
  • CSP report-uri needs an endpoint that accepts POST + application/csp-report. WordPress doesn't ship one. Use report-to instead, or a service like report-uri.com (free tier available).
  • Cloudflare adds its own headers. If you're behind Cloudflare, check that your origin's headers aren't being stripped or duplicated. Cloudflare's `Cf-Cache-Status` and `Cf-Ray` are harmless; duplicate `X-Frame-Options` will confuse strict browsers.

A security header is the last line of defence that doesn't rely on your plugins being uncompromised. Ship them at the server or CDN layer, not from a WordPress plugin — that plugin can be the thing that gets compromised.

Want these audited on your site?

Every security audit I ship includes a full headers rewrite — server, CDN, and any WordPress-side fallbacks — tested against securityheaders.com and Mozilla Observatory. Median rating goes from D to A+ in one pass.

Site down? WhatsApp now