Fix Guide

How to Add Missing Security Headers in WordPress

February 8, 2026

Security headers are HTTP response headers that tell browsers to activate built-in security features. Most WordPress sites are missing several of these headers by default because neither WordPress core nor most hosting providers set them automatically. The good news is that adding them takes only a few minutes, and the security improvement is significant. This guide covers every important security header, explains what each one does, and shows you how to add them all at once.

Which Security Headers Should Every WordPress Site Have?

Here is the complete list of recommended security headers with their values and what they protect against:

  • X-Frame-Options: SAMEORIGIN: prevents your site from being loaded inside an iframe on another domain. This stops clickjacking attacks where a malicious site overlays invisible buttons on top of your page.
  • X-Content-Type-Options: nosniff: stops browsers from guessing the MIME type of a file. Without this, an attacker could upload a file that looks like an image but contains JavaScript, and the browser might execute it.
  • Referrer-Policy: strict-origin-when-cross-origin: controls how much URL information is shared when users click links to other sites. This setting sends the full URL for same-origin requests but only the domain for cross-origin ones, protecting sensitive URL parameters.
  • Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(): disables browser features your site does not use. The empty parentheses mean "nobody is allowed to use this feature," which prevents malicious scripts from accessing the camera, microphone, or location.
  • Strict-Transport-Security: max-age=31536000; includeSubDomains: forces browsers to always use HTTPS. See our dedicated HSTS guide for details on safe rollout.
  • X-XSS-Protection: 1; mode=block: a legacy header for older browsers that enables the built-in XSS filter. Modern browsers have deprecated this in favor of CSP, but it does not hurt to include it for backward compatibility.
  • Content-Security-Policy: the most powerful (and complex) security header. See our dedicated CSP guide for a complete walkthrough.

How to Prioritize: Which Headers to Add First

If you are adding security headers for the first time, here is the order that gives you the biggest security improvement with the least risk:

  1. X-Content-Type-Options: zero risk of breaking anything. Just add it.
  2. X-Frame-Options: safe unless you intentionally embed your site in iframes on other domains (unlikely for most WordPress sites).
  3. Referrer-Policy: very low risk. The recommended value strict-origin-when-cross-origin is already the default behavior in modern browsers.
  4. Permissions-Policy: low risk unless your site actually uses the camera, microphone, or geolocation APIs.
  5. X-XSS-Protection: legacy header, no risk at all.
  6. Strict-Transport-Security: requires working HTTPS first. Follow a gradual rollout (see our HSTS guide).
  7. Content-Security-Policy: highest risk of breaking things on WordPress. Always start with Report-Only mode (see our CSP guide).

Adding All Headers via Apache .htaccess

This is the most common method for WordPress sites on shared hosting. Open the .htaccess file in your WordPress root directory and add the following block. Place it before the # BEGIN WordPress section to keep things organized:

<IfModule mod_headers.c>
    # Prevent clickjacking
    Header always set X-Frame-Options "SAMEORIGIN"

    # Prevent MIME-type sniffing
    Header always set X-Content-Type-Options "nosniff"

    # Control referrer information
    Header always set Referrer-Policy "strict-origin-when-cross-origin"

    # Restrict browser features
    Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()"

    # Force HTTPS (only add if SSL is fully working)
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

    # XSS Protection for legacy browsers
    Header always set X-XSS-Protection "1; mode=block"
</IfModule>

A few things to keep in mind:

  • The IfModule wrapper ensures Apache does not throw a 500 error if mod_headers is not enabled on your server.
  • Using Header always set (instead of just Header set) ensures the headers are sent on all responses, including error pages.
  • If you already have a <IfModule mod_headers.c> block in your .htaccess, add the individual Header lines inside that existing block instead of creating a duplicate.

Adding All Headers via Nginx

If your WordPress site runs on Nginx (common with managed WordPress hosts like Kinsta, Cloudways, GridPane, or SpinupWP), add these lines inside your server block:

# Security Headers
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=()" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-XSS-Protection "1; mode=block" always;

Important Nginx-specific notes:

  • The always keyword sends the header on all response codes (including 4xx and 5xx). Without it, Nginx only sends headers on 2xx responses.
  • If you have a separate location block that handles PHP (e.g., location ~ .php$), you may need to add the headers there as well, depending on your Nginx configuration. Nginx does not inherit add_header directives from parent blocks if the child block has its own add_header.
  • After making changes, always test and reload: sudo nginx -t && sudo systemctl reload nginx.

Adding Headers via PHP in WordPress

If you cannot access server configuration files, you can send security headers from PHP. Add this to your theme's functions.php or to a custom site-specific plugin:

function iwp_add_security_headers() {
    if ( is_admin() ) {
        return;
    }

    header( 'X-Frame-Options: SAMEORIGIN' );
    header( 'X-Content-Type-Options: nosniff' );
    header( 'Referrer-Policy: strict-origin-when-cross-origin' );
    header( 'Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()' );
    header( 'X-XSS-Protection: 1; mode=block' );
}
add_action( 'send_headers', 'iwp_add_security_headers' );

The is_admin() check skips the WordPress admin area to avoid potential conflicts with admin functionality. Note that this PHP approach has a limitation: it only applies to pages that WordPress processes. Static files (images, CSS, JS, fonts) served directly by your web server will not carry these headers. For complete coverage, server-level configuration is always the better choice.

WordPress Security Header Plugins

If you prefer a plugin-based approach, these options provide a user-friendly interface for managing security headers:

  • HTTP Headers: a free plugin with a comprehensive UI for setting all security headers. It supports X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, and more. You configure everything from the WordPress admin without touching any files.
  • Really Simple SSL Pro: the premium version includes a security headers module that lets you toggle individual headers on or off. It also provides recommendations and explains each header.
  • Headers Security Advanced & HSTS WP: a focused plugin specifically for security headers. It covers all major headers and includes presets for common configurations.
  • Jeremykendall's Security Headers: a lightweight option that adds all recommended headers with sensible defaults.

While plugins are convenient, they come with trade-offs. If the plugin is deactivated, updated with a bug, or removed, your security headers disappear instantly. For production sites, server-level configuration is more reliable because it does not depend on WordPress or any plugin being active.

Testing Your Headers with Online Tools

After adding your headers, verify them with these free tools:

  • securityheaders.com: enter your URL and get an A+ to F grade along with a detailed breakdown of which headers are present and which are missing. Aim for at least an A grade.
  • observatory.mozilla.org: Mozilla's tool performs a more comprehensive analysis, including CSP evaluation and cookie security.
  • Browser DevTools: press F12, go to the Network tab, click the main document request, and scroll to Response Headers to see exactly what your server sends.

You can also check from the command line:

curl -sI https://example.com

This shows all response headers, including your newly added security headers.

CDN and Reverse Proxy Considerations

If your WordPress site sits behind a CDN like Cloudflare, Sucuri, or Fastly, be aware that the CDN can strip, override, or add its own headers. Here is what you need to know:

  • Cloudflare: does not strip security headers by default, so headers set on your origin server will pass through. Cloudflare also offers its own security headers settings in the dashboard under SSL/TLS > Edge Certificates (for HSTS) and in managed rules.
  • Sucuri: their firewall may add some security headers automatically. Check your Sucuri dashboard settings to see which ones are active and avoid duplicates.
  • Fastly / KeyCDN / BunnyCDN: most CDNs allow you to add custom response headers in their configuration panel. This can be a good alternative if you cannot modify server config on your origin.

If you see duplicate headers in your response (e.g., two X-Frame-Options lines), this can cause unpredictable browser behavior. Make sure each header is set in only one place: either on your origin server or at the CDN level, not both.

Common Mistakes When Adding Security Headers

  • Duplicate IfModule blocks: if your .htaccess already has a <IfModule mod_headers.c> block (perhaps from a plugin), adding a second one can cause conflicts. Merge your headers into the existing block.
  • Setting headers only via PHP: this misses all static files. Use server-level configuration for complete coverage.
  • Overriding headers in location blocks (Nginx): if a child location block has any add_header, it replaces all headers from the parent block. You need to repeat all headers in each location block that defines its own.
  • Not testing after plugin updates: a plugin update or theme switch can remove your PHP-based headers. Always re-test after major changes.
  • Forgetting about subdomains: if you have subdomains (staging, mail, etc.), make sure they also have security headers configured. Attackers often target the weakest subdomain.

Verify Your Security Headers with InspectWP

After adding all headers, run a new InspectWP scan on your WordPress site. The security section of your report lists every security header and shows its status. Each header that is present and correctly configured will appear as green. Missing headers show up as red or yellow, depending on their importance. Use InspectWP's automatic report feature to monitor your headers over time and catch any regressions caused by server changes or plugin updates.

Check your WordPress site now

InspectWP analyzes your WordPress site for security issues, SEO problems, GDPR compliance, and performance — for free.

Analyze your site free