security DigitalFixes
Security Headers in WordPress shown on a computer screen with CSP, HSTS, and X-Frame-Options guide elements.

Security Headers in WordPress: A Practical Guide to CSP, HSTS, and X-Frame-Options

April 30, 2026

If your WordPress site gets hacked through a fake admin page or a sneaky script, it often starts with something called content injection. The fix isn’t just “remove the malware.” You also need guard rails in the browser. That’s where security headers come in—especially CSP, HSTS, and X-Frame-Options.

Security headers in WordPress are small settings your server sends with every page view. They tell browsers what’s allowed and what’s not. In this guide, I’ll show you how to set them up in a practical, WordPress-friendly way, based on what I’ve seen go wrong during real cleanups in 2024–2026.

Security headers in WordPress: what they actually do (plain English)

Security headers in WordPress are rules sent in your HTTP response. They work in the user’s browser, before any page code runs.

Think of it like a sign at a store door. If the browser doesn’t like what it sees in your page (or in any script requests), it stops the action. These headers don’t replace malware cleanup, but they make the same type of attack harder to repeat.

Here’s the simplest way I explain the three headers you asked about:

  • CSP (Content Security Policy) is a “permissions list” for scripts, images, and other page resources.
  • HSTS (HTTP Strict Transport Security) forces browsers to use HTTPS, even if they try to go to HTTP.
  • X-Frame-Options tells the browser whether your site can be shown inside an iframe (a common trick for clickjacking).

Why CSP, HSTS, and X-Frame-Options matter after malware removal

After a cleanup, you’re often focused on files and plugins. But the attacker’s real goal is usually to get a browser to run bad code.

In several incidents I’ve worked on, the infection wasn’t just “one bad script file.” It was also a way to load scripts from other domains, change forms, or trick a user into clicking something. CSP blocks a lot of that behavior.

And HSTS helps with a different problem: attackers trying HTTPS downgrade or SSL stripping style tricks. If a visitor types http:// in a hurry, HSTS keeps them on HTTPS automatically.

Finally, X-Frame-Options helps with clickjacking. That’s when someone loads your login or payment page inside a hidden frame and overlays fake buttons on top.

CSP in WordPress: the practical way to set it without breaking your site

CSP is powerful, but it can break things if you turn it on too fast. The safe approach is to start in “report-only” mode, then tighten the rules.

What CSP is (and what most people get wrong)

CSP is a browser rule that defines where scripts and other resources are allowed to load from. If the page tries to load a script from an unapproved place, the browser blocks it.

What most people get wrong is copying a random CSP from the internet. WordPress pages often load resources from your theme, WordPress core, CDNs, analytics, chat widgets, and sometimes inline scripts. One wrong directive and suddenly your forms stop working.

My rule for 2026 WordPress sites: start loose, measure errors for a week, then lock down.

Step-by-step: add a CSP header for WordPress using report-only first

Here’s how I recommend you do it if you want fewer surprises.

  1. Enable HTTPS for the site first. If you’re not on HTTPS everywhere, HSTS comes later.
  2. Turn on CSP in report-only mode so browsers log violations but don’t block.
  3. Watch logs using your server logs, hosting panel logs, or a CSP report endpoint (more on this below).
  4. Update the policy gradually after you see which domains and script types your site truly needs.
  5. Switch from report-only to enforcing once you’re confident.

Common CSP directives for WordPress (CSP allowlist basics)

These are the directives I see most often on WordPress sites. You won’t need all of them at once, but it helps to understand what they do.

  • default-src: fallback rule for content types you didn’t explicitly set.
  • script-src: where JavaScript can come from.
  • style-src: where CSS comes from (including inline rules).
  • img-src: where images can come from.
  • connect-src: where fetch/XHR and websockets can connect.
  • frame-ancestors: who is allowed to embed your site (this overlaps with X-Frame-Options).
  • frame-src: where iframes inside your site can load content from.

Also, pay attention to unsafe-inline and unsafe-eval. They weaken CSP. If you use them, keep them temporary and only if you must.

A working “starter” CSP for WordPress (example)

This is a starter example for a typical WordPress setup with analytics and a CDN. You must replace the domains with your real ones.

Note: I’m giving this as a starting point. Your plugin stack decides what you need.

Content-Security-Policy-Report-Only: default-src 'self'; 
script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com https://cdnjs.cloudflare.com; 
style-src 'self' 'unsafe-inline'; 
img-src 'self' data: https:; 
connect-src 'self' https://www.google-analytics.com https://www.googletagmanager.com; 
font-src 'self' https://cdnjs.cloudflare.com https://fonts.gstatic.com; 
frame-ancestors 'self'; 
object-src 'none'; 
base-uri 'self'; 
form-action 'self';

After a week, you’ll see what got blocked in your reports. Then you tighten it.

How to use CSP reporting in a WordPress-friendly way

To get value from CSP, you need to see the exact violations. CSP can send reports using report-to or report-uri. Many hosts don’t let you easily create custom report endpoints, so you may rely on browser console logs during testing.

In my experience, the fastest path is:

  • Use a staging copy of your WordPress site.
  • Apply CSP report-only.
  • Test key pages: homepage, blog post, product page, contact form, and login.
  • Open the browser console to catch blocked resources.

If your hosting supports custom headers in a config file, you can also capture CSP violation logs at the server level.

HSTS in WordPress: stop HTTP before it starts

Developer checking CSP report-only violations in browser console
Developer checking CSP report-only violations in browser console

HSTS is simple but you must set it carefully. Once enabled with a long max-age, it’s hard to undo.

What HSTS is (and why it matters in real attacks)

HSTS (HTTP Strict Transport Security) is a header that tells browsers to always use HTTPS for your domain for a period of time.

In 2026, most WordPress sites already run on HTTPS, but attackers still try to trick users into visiting http:// versions. HSTS blocks that by forcing HTTPS automatically.

Another benefit: HSTS reduces the “first connection” window where some downgrade attacks can happen. If you’ve done malware cleanups, you already know how small openings can become bigger infections.

Recommended HSTS settings for most WordPress sites

I recommend this order:

  • Start with a shorter max-age like 30 days.
  • Use includeSubDomains only if every subdomain is HTTPS-ready.
  • Use preload only if you’re sure you want it and meet the browser rules.

Example header:

Strict-Transport-Security: max-age=2592000; includeSubDomains

That’s 30 days (2592000 seconds). If you don’t have HTTPS on every subdomain (like blog.domain.com, mail.domain.com), don’t turn on includeSubDomains yet.

HSTS gotchas that cause support tickets

Here are the main reasons I see HSTS cause issues:

  • You don’t have HTTPS on a subdomain, so browsers block it.
  • Your SSL certificate changes and doesn’t match the domain consistently.
  • You misconfigured redirects, so HTTP doesn’t properly 301 to HTTPS.

If you just migrated hosting or changed DNS recently, wait until HTTPS is stable for a few days before enabling HSTS.

X-Frame-Options (and modern iframe controls) for WordPress

Illustration-style scene of a website displayed inside an iframe to explain clickjacking protection
Illustration-style scene of a website displayed inside an iframe to explain clickjacking protection

X-Frame-Options helps prevent clickjacking by blocking your site being loaded inside an iframe.

What X-Frame-Options means (and how it works)

X-Frame-Options is a legacy-but-still-relevant header. It tells the browser: “Don’t show my site in an iframe.”

Common values are:

  • DENY: the site cannot be framed by any site.
  • SAMEORIGIN: it can only be framed by the same origin.
  • ALLOW-FROM: used in the past, but support is spotty across browsers.

Recommended X-Frame-Options for most WordPress sites

If you don’t embed your site inside other sites, DENY is the safest option.

X-Frame-Options: DENY

If you embed content on the same domain (or use certain admin tools in iframes), you may need SAMEORIGIN instead.

Don’t forget frame-ancestors (CSP’s modern version)

In 2026 best practice, the more future-proof control is frame-ancestors inside CSP. Many setups use both. If they conflict, browsers follow the stricter policy.

For example, if your CSP includes:

frame-ancestors 'self'

…it means your pages can only be framed by the same site.

My take: Use CSP’s frame-ancestors for modern browsers, and keep X-Frame-Options for extra coverage.

How to add these headers in WordPress (without fighting your stack)

The right method depends on your hosting and plugins. Here are the practical options I see work every day.

Option 1: Add headers in your web server (best control)

If you have server access, you can set headers in Nginx or Apache. This is the most reliable way because it applies to all pages consistently.

For Apache, you might use:

<IfModule mod_headers.c>
  Header always set X-Frame-Options "DENY"
  Header always set Strict-Transport-Security "max-age=2592000; includeSubDomains"
</IfModule>

For Nginx, you might use:

add_header X-Frame-Options "DENY" always;
add_header Strict-Transport-Security "max-age=2592000; includeSubDomains" always;

Option 2: Use a security plugin (fast, but verify)

Many WordPress security plugins let you set security headers. This is quick, especially if your host restricts server changes.

One important step: verify the actual response headers with a tool like Chrome DevTools or an online header checker. Plugins sometimes change behavior on caching layers, or they don’t apply to non-HTML endpoints.

In my cleanup work, I’ve seen sites where the plugin claims CSP is enabled, but the CDN serves a different header set.

Option 3: Set headers at the CDN / edge (powerful, watch for caching)

If you use Cloudflare, Fastly, or another CDN, you can set headers there. This often works great and speeds things up.

But caching can bite you. If the CDN caches an older policy, users might get the wrong CSP for some pages.

In 2026, I recommend purging caches after header changes and testing on multiple pages: homepage, a post, and the login page.

Comparison: what CSP vs HSTS vs X-Frame-Options protect against

People mix these up, so here’s a simple comparison that you can keep as a checklist.

Header What it protects Common WordPress issue it helps Typical risk if you misconfigure
CSP Stops unwanted scripts and resource loads Plugin/JS injections after a compromise Broken forms, blocked fonts, console errors
HSTS Forces HTTPS Downgrade attempts and http entry points Subdomain HTTPS failures cause access problems
X-Frame-Options Stops clickjacking via iframes Hidden login/payment overlays Embeds won’t work if you rely on framing

People Also Ask: Security headers in WordPress

Should I use CSP and X-Frame-Options together?

Yes, for most sites. CSP’s frame-ancestors is the modern control, while X-Frame-Options adds extra protection for older browser behavior.

If you need your site embedded on other domains, don’t just set DENY—use the correct allow rules in CSP and set X-Frame-Options accordingly.

Will CSP block my WordPress plugins and analytics?

CSP can block them if your policy doesn’t allow their script and connection sources.

This is why I insist on CSP report-only first. During a week of testing, you’ll find the exact domains your plugins use (analytics, tags, chat widgets, video embeds), then you add them to the allowlist.

How long does it take to set CSP correctly?

For small business sites, a good target is 1–2 weeks if you’re doing it carefully in report-only mode and testing key pages.

If you flip it to enforce mode right away, you can lose forms and checkout flows in a single afternoon. That turns a security fix into an emergency ticket.

Can I turn off CSP later if it breaks something?

Yes, but don’t depend on that as your plan. When CSP is enforcing, browsers will block content immediately.

Instead, apply changes during low-traffic hours, test on staging, and keep a rollback path (like disabling the header at your server/CDN level).

My “cleanup to hardening” checklist for 2026 WordPress sites

When I handle a malware removal case study, I don’t stop at removing the bad code. I also harden the site right after, because it stops the same attacker path from working again.

Here’s the checklist I use for small business owners:

  1. Confirm the compromise is gone (scan files, review recent admin changes, check for suspicious users).
  2. Remove unknown scripts from theme files, headers, footers, and plugin settings.
  3. Turn on HSTS after HTTPS is stable and redirects are correct.
  4. Add CSP in report-only mode first and test key pages.
  5. Lock down CSP gradually and remove “unsafe” rules once you don’t need them.
  6. Add X-Frame-Options (or rely on CSP frame-ancestors) to reduce clickjacking risk.
  7. Re-check headers after caching/CDN changes so you know the real response matches your plan.

If you want more incident-style guidance, you can also read our related post on website hardening tips for WordPress and our malware removal checklist for compromised sites. (We keep those updated for current hosting setups.)

Real-world examples: what broke when teams rushed headers

Every site stack is different, but the failures repeat. Here are three patterns I’ve seen often.

Example 1: CSP blocked a contact form script

A client added a “strict CSP” and the contact form stopped working. The form relied on an inline script added by a builder plugin, plus an external script for spam checks.

Fix: switch to report-only, add the builder’s script source, and reduce inline rules step-by-step. After that, the site worked and stayed locked down.

Example 2: HSTS enabled includeSubDomains too early

Another site turned on HSTS with includeSubDomains right after a migration. Their mail subdomain wasn’t ready on HTTPS, so some users couldn’t access email-related pages or subdomains.

Fix: remove includeSubDomains, keep max-age short, and verify HTTPS for every subdomain first.

Example 3: X-Frame-Options blocked an embedded widget

A small business used an embedded tool (like a booking widget) and tested on the homepage only. After X-Frame-Options DENY, the widget didn’t load on a specific page layout.

Fix: confirm whether your site is being framed or whether you’re framing others. Then choose SAMEORIGIN or rely on CSP frame-ancestors to match your real embed needs.

Where security headers fit with other defenses

Security headers are one layer. They work best with the basics: clean code, strong passwords, updated plugins, and good backups.

For example, if you haven’t removed a backdoor file, CSP can slow some scripts but it won’t remove the attacker’s access. This is why your malware cleanup still comes first.

If you’re dealing with a suspected infection right now, check our threat alerts for WordPress websites and our guidance on hack case studies to see how attackers typically enter and what defenses stopped them.

Clear takeaway: secure your WordPress browsers without breaking your business

CSP, HSTS, and X-Frame-Options are three of the most useful security headers for WordPress. CSP reduces script-based attacks, HSTS blocks HTTP downgrade attempts, and X-Frame-Options (plus CSP frame-ancestors) fights clickjacking.

Do this in order: stabilize HTTPS, enable HSTS with a safe max-age, add CSP in report-only mode, test key pages for at least a week, then enforce. Finish with X-Frame-Options so your site can’t be framed by attackers.

If you treat headers like a careful change (not a copy-paste setting), you get real protection without turning your site into a support problem.

Featured image alt text suggestion: Security headers in WordPress showing CSP, HSTS, and X-Frame-Options settings.