Guides Create account 🇬🇧 🇩🇪
  • Guides
  • Create account
  • Sign in
  • 🇬🇧 🇩🇪
  • Server recipe

    Security headers in WordPress

    You can set every header WordPress needs with a handful of lines and no plugin. The harder part is putting them somewhere a theme update will not remove, and knowing which one will break your admin area.

    Check my headers All security headers

    01 Where it goes

    A site-specific plugin, not the theme

    The send_headers action fires before WordPress sends a response, which makes it the right hook. The usual advice is to put the code in your theme functions.php, and that is where it goes wrong: switching themes removes your security headers, and updating a theme that is not a child theme overwrites the file entirely.

    Put it in a small site-specific plugin instead, a single PHP file in wp-content/plugins that you activate once. It survives theme changes and updates, it can be deactivated in one click if a header breaks something, and it keeps configuration out of presentation. If your web server is under your control, setting the headers there is better still, because they then also cover static files that never reach PHP.

    02 The recipe

    Paste this into a site-specific plugin

    A safe starting point. The is_admin guard matters: a strict Content-Security-Policy will break the block editor and the customiser, which rely on inline scripts, so the policy applies to the public site only.

    add_action( 'send_headers', function () { if ( is_admin() ) { return; }     header( 'Strict-Transport-Security: max-age=63072000; includeSubDomains' );
        header( "Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'" );
        header( 'X-Frame-Options: SAMEORIGIN' );
        header( 'X-Content-Type-Options: nosniff' );
        header( 'Referrer-Policy: strict-origin-when-cross-origin' );
        header( 'Permissions-Policy: geolocation=(), camera=(), microphone=(), payment=(), usb=()' ); } );

    The Content-Security-Policy above allows nothing from third parties and will block embedded maps, fonts, analytics and most page builders. On a real WordPress site, start with Content-Security-Policy-Report-Only, collect what it would have blocked and widen the policy from there.

    03 Line by line

    What each line does

    Every header on its own, so you can leave out the ones that do not fit your site rather than pasting something you cannot explain.

    Strict-Transport-Security

    What it does. Commits browsers to reaching your site over HTTPS only. Set this at the web server rather than in PHP if you can, so it also covers requests that never reach WordPress.

    header( 'Strict-Transport-Security: max-age=63072000; includeSubDomains' );
    Read the full guide

    Content-Security-Policy

    What it does. Start here rather than with the enforcing header. Report-only changes nothing for visitors but tells you what a strict policy would have blocked, which on a WordPress site with plugins is usually more than expected.

    header( "Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'" );
    header( "Content-Security-Policy-Report-Only: default-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'; report-uri /csp-report" );
    Read the full guide

    X-Frame-Options

    What it does. SAMEORIGIN rather than DENY, because parts of WordPress frame their own pages: the theme customiser previews the site in an iframe and DENY breaks it.

    header( 'X-Frame-Options: SAMEORIGIN' );
    Read the full guide

    X-Content-Type-Options

    What it does. Tells the browser to trust your declared content types. Safe on any site, including one that accepts media uploads, where it removes a whole class of upload-based attack.

    header( 'X-Content-Type-Options: nosniff' );

    Referrer-Policy

    What it does. Sends the full URL within your own site and only the bare domain to anyone else. The right value for almost every public WordPress site.

    header( 'Referrer-Policy: strict-origin-when-cross-origin' );
    Read the full guide

    Permissions-Policy

    What it does. Switches off camera, microphone, location, payment and USB access for your pages and for everything embedded in them. Extend the list rather than shortening it: a feature you do not name stays available, including to anything a plugin embeds.

    header( 'Permissions-Policy: geolocation=(), camera=(), microphone=(), payment=(), usb=()' );
    Read the full guide

    Set-Cookie

    What it does. WordPress sets its own authentication cookies with the right flags, but PHP session cookies from plugins often lack them. These three lines fix the session cookie defaults for everything running on the site.

    ini_set( 'session.cookie_secure', '1' );
    ini_set( 'session.cookie_httponly', '1' );
    ini_set( 'session.cookie_samesite', 'Lax' );
    Read the full guide

    04 Verifying it

    Read the response, not the plugin list

    A caching plugin or a CDN sits between PHP and your visitor and may serve a cached copy of a page that was generated before your headers existed. An activated plugin therefore proves nothing on its own.

    curl -sI https://example.com | grep -i "^strict-transport\|^content-security\|^x-frame"

    Clear every cache layer first, then request the page. Check the admin login page as well: if it stops rendering, the Content-Security-Policy is reaching pages the is_admin guard does not cover.

    Check my headers with a free scan

    05 Questions

    What people ask about WordPress headers

    Should I use a security plugin instead?

    A plugin is a reasonable choice if you would rather have a settings screen than a file, and the large security plugins do this correctly. What you are buying is convenience, not better headers: the values are the same and you take on another plugin to keep updated.

    Why not just put it in functions.php?

    Because it disappears the moment the theme changes, and it is lost on the next update unless you are using a child theme. Security configuration should not be tied to how your site looks.

    My site broke after adding the Content-Security-Policy. What do I do?

    Deactivate the plugin, then reintroduce the policy as Content-Security-Policy-Report-Only. WordPress sites load scripts and styles from plugins, page builders and often from external services, and a policy allowing only your own origin will block a good number of them.

    I am on managed hosting and cannot edit the server configuration. Does this still work?

    Yes, that is exactly the case the PHP approach covers. What you lose is coverage of files that never reach PHP, such as images and stylesheets served directly by the web server. For most sites the pages are what matter, and some managed hosts let you set headers in their control panel too.

    06 In depth

    The headers in detail

    This page is the configuration. These guides are what each header actually does and how to choose its value.

    HTTP security headers

    What each response header does, which ones your site should send and how to configure them correctly.

    Read guide

    Content Security Policy

    The strongest defence against cross-site scripting: which directives to set, how to roll a policy out in report-only mode and how to reach an enforcing policy without breaking your site.

    Read guide

    HSTS

    Strict-Transport-Security explained: what max-age, includeSubDomains and preload actually do, and why the preload list is a decision you cannot quickly undo.

    Read guide

    Cookie security

    Secure, HttpOnly and SameSite: the cookie attributes that keep a session out of reach of scripts and cross-site requests, with the settings for a typical stack.

    Read guide

    Clickjacking and X-Frame-Options

    How an invisible overlay turns a visitor click into an action on your site, and the two headers that stop it: X-Frame-Options for older clients, frame-ancestors for everything else.

    Read guide

    Referrer-Policy

    Which part of your URLs travels to other sites when a visitor clicks away: the five policy values that matter, what each one gives up and the one to set by default.

    Read guide

    Permissions-Policy

    Switch off camera, microphone, geolocation and payment for your site and everything it embeds: the allow-list syntax, and why a feature you did not name is not a feature you turned off.

    Read guide

    SPF, DMARC and DNSSEC

    The records that stop someone sending mail in your name, and the one that keeps your DNS answers honest: what each does and the order to introduce them in.

    Read guide

    security.txt

    The file that tells a researcher where to report a vulnerability. Two mandatory fields, ten minutes of work, and the difference between a private report and a public one.

    Read guide

    Monitoring security headers

    A header that is right today can be gone after the next deploy. Three ways to notice: a cron job with curl, a check in your CI pipeline and a scheduled scan, with what each of them catches and misses.

    Read guide

    Detecting CSP changes

    How a policy changes quietly through deploys, plugins and CDN rules, and how to catch it: a header diff, violation reports through report-to, and what a scan comparison can and cannot show.

    Read guide

    Keep reading

    Every guide stands on its own, and together they cover what our scanner looks at. Pick the one closest to your next question.

    Is your website affected? BFSG check Accessibility statement BFSG for online shops BFSG for medical practices BFSG for hotels BFSG for tradespeople HTTP security headers Privacy signals Content Security Policy HSTS Cookie security Clickjacking and X-Frame-Options Referrer-Policy Permissions-Policy SPF, DMARC and DNSSEC security.txt Monitoring security headers Detecting CSP changes Security headers in nginx Security headers in Apache Security headers in IIS Security headers in TYPO3 Security headers in Shopware 6 Security headers in Plesk All guides

    See which headers your WordPress site is sending

    Our free Quickscan reads the headers from your live response rather than from your plugin list, and explains every finding in plain language.

    Scan a website Latest scans
    Recent scans Guides Local leagues Pricing Methodology For hosters API Data protection Imprint Accessibility Terms Cancel contracts here © 2026 Erseni