/ Security Audit Fixes / WordPress

How to Fix Security Headers in WordPress

WordPress powers ~43% of the web — and adds zero HTTP security headers by default. It also broadcasts its version via a <meta name="generator"> tag on every page, giving attackers a one-step path to known CVEs. This guide walks through fixing every WordPress-related finding from our Security Audit: removing the version meta, adding the six security headers, hardening wp-config.php, and dealing with cookies. Works on WordPress 6.x and any hosting environment.

1. Remove the WordPress generator meta tag

By default, WordPress adds <meta name="generator" content="WordPress 6.4.2"> to every page's HTML head. That single line tells anyone — including automated CVE scanners — exactly which version of WordPress you're running. Remove it.

Method 1
Via functions.php (recommended)
Edit your active theme's functions.php file. If you use a third-party theme, edit the child theme's functions.php — never the parent — or the change is wiped on theme updates.
// Remove WordPress version from generator meta
remove_action('wp_head', 'wp_generator');

// Also strip version from CSS and JS file URLs
function aw_remove_version_from_assets( $src ) {
    if ( strpos( $src, 'ver=' ) ) {
        $src = remove_query_arg( 'ver', $src );
    }
    return $src;
}
add_filter( 'style_loader_src', 'aw_remove_version_from_assets', 9999 );
add_filter( 'script_loader_src', 'aw_remove_version_from_assets', 9999 );

// Remove version from RSS feeds
add_filter( 'the_generator', '__return_empty_string' );
Save. View page source on the live site — you should no longer see the WordPress version anywhere.
Method 2
Via a plugin
If you cannot edit theme files (managed hosting restriction, or you're not comfortable with code), use a security plugin. Free options that handle this:
  • Wordfence Security — comprehensive, with header management in the firewall settings
  • Sucuri Security — comprehensive, includes header management
  • Really Simple SSL (Pro version) — focused specifically on HTTPS and security headers
  • WPCode — for adding code snippets without editing functions.php directly

2. Add HTTP security headers

Six headers cover the audit's HTTP Security Headers section: CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy and Permissions-Policy. The best place to add them depends on your hosting.

If you control the web server (recommended)

Adding headers at the nginx or Apache layer is faster than at the PHP layer. See the Plesk fix guide or the upcoming nginx/Apache/cPanel guides for server-level instructions. WordPress works correctly behind any of these.

If you cannot edit server config — use functions.php

On managed WordPress hosting where you can't touch nginx or Apache, add headers via the send_headers action:

add_action( 'send_headers', function() {
    header( "Strict-Transport-Security: max-age=31536000; includeSubDomains" );
    header( "X-Frame-Options: SAMEORIGIN" );
    header( "X-Content-Type-Options: nosniff" );
    header( "Referrer-Policy: strict-origin-when-cross-origin" );
    header( "Permissions-Policy: geolocation=(), microphone=(), camera=()" );
    header( "Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-ancestors 'self'" );
});
⚠️ The 'unsafe-inline' in script-src and style-src is needed because WordPress core and most plugins emit inline JS/CSS. Removing it will break the admin and most page builders. This is a real trade-off — WordPress was not designed CSP-first.

3. Harden wp-config.php

Several security wins happen in wp-config.php — the WordPress configuration file in your site's document root. Edit via SFTP or your hosting file manager.

Step 1
Disable file editing in the admin
WordPress allows administrators to edit theme and plugin files from the admin UI. If an attacker compromises an admin account, this becomes their entry point for code execution. Disable it:
define( 'DISALLOW_FILE_EDIT', true );
You and your team will need to edit files via SFTP from now on. That is the point.
Step 2
Force SSL for the admin and login
Prevents anyone from accidentally accessing the admin over HTTP:
define( 'FORCE_SSL_ADMIN', true );
If your site is behind a reverse proxy (Cloudflare, load balancer), also add:
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) {
    $_SERVER['HTTPS'] = 'on';
}
Without this, WordPress thinks it's serving over HTTP and may set cookies without the Secure flag.
Step 3
Rotate authentication salts
Your wp-config.php has eight AUTH_KEY/AUTH_SALT lines used to sign cookies and hash passwords. If they are still the defaults from when you installed WordPress years ago, rotate them. Generate new values at api.wordpress.org/secret-key/1.1/salt/ and replace the eight lines in wp-config.php. All users will be logged out and need to log in again — that's expected and is the point.

4. WordPress cookie flags

WordPress sets a handful of session cookies (wordpress_, wordpress_logged_in_, wp-settings-). The audit checks for Secure, HttpOnly and SameSite flags. WordPress sets HttpOnly correctly by default. Secure depends on your HTTPS configuration. SameSite is the modern gap.

Add this to functions.php to enforce all three on WordPress's session cookies:

add_action( 'init', function() {
    if ( ! headers_sent() ) {
        $secure   = is_ssl();
        $httponly = true;
        $samesite = 'Lax';
        session_set_cookie_params([
            'lifetime' => 0,
            'path'     => COOKIEPATH ?: '/',
            'domain'   => COOKIE_DOMAIN,
            'secure'   => $secure,
            'httponly' => $httponly,
            'samesite' => $samesite,
        ]);
    }
});

For cookies set by plugins, you may need to also add header-level overrides via your web server, since WordPress can't retroactively modify cookies third-party code has already sent.

5. Remove WordPress fingerprint files

WordPress ships with several files that broadcast its presence even if you remove the generator meta. They aren't strictly required and can be deleted or 404'd.

PathWhat it leaksAction
/readme.htmlWordPress version, install dateDelete from your site root
/license.txtConfirms WordPressDelete from your site root
/wp-config-sample.phpConfirms WordPressDelete from your site root
/xmlrpc.phpWordPress XML-RPC endpoint, attack targetBlock via .htaccess if not in use

Block XML-RPC if you don't use Jetpack, mobile WordPress apps or any service that requires it:

// In .htaccess (Apache) — return 403 for /xmlrpc.php
<Files xmlrpc.php>
    Order Deny,Allow
    Deny from all
</Files>

6. The WordPress version is still in CVEs — keep it updated

Removing the generator meta hides the version from casual fingerprinting, but the actual CVE risk only goes away when you patch. The audit's CVE check via NVD will not flag WordPress on a clean site without an exposed version. But that doesn't mean you can skip updates — set WordPress core, plugins and themes to auto-update major releases. Add to wp-config.php:

define( 'WP_AUTO_UPDATE_CORE', true );

Frequently Asked Questions

My audit shows CMS detected: WordPress but no version — is that OK?
Yes. Detection means the audit found WordPress signals (wp-content paths, REST API at /wp-json/, etc.) but no exact version number — that's the goal. The audit only fails when the version is explicitly exposed. You can never fully hide that WordPress is in use without major theme changes, and that's not worth the effort. Hide the version, not the platform.
Will removing readme.html break WordPress updates?
No. readme.html is a static documentation file that WordPress updates regenerate on every core update. You can delete it freely; WordPress will re-create it after each update, so consider adding a delete step to your update workflow or using a security plugin to keep it 404'd automatically.
I use Cloudflare in front of WordPress — should I add headers in WordPress or Cloudflare?
Cloudflare's Transform Rules are the simpler place to add response headers because they apply regardless of which origin server, theme or plugin generates the response. WordPress functions.php is appropriate when you don't have Cloudflare. Don't add the same header in both places — the result is duplicate headers in the response, which some clients reject.
My WordPress hosting provider says they handle security headers — should I still add them?
Run the audit on your domain first. Some managed WP hosts (Kinsta, WP Engine) do add a baseline set of headers. Others claim to but don't add enough. The audit shows ground truth. If the audit passes, leave well alone. If it fails, add the missing headers via functions.php — that won't conflict with whatever the host adds, because PHP-set headers replace existing values rather than duplicating.
Do these changes affect the WordPress admin dashboard?
Mostly no. The generator meta removal, salt rotation and wp-config hardening don't affect admin functionality. The CSP can break the admin if you make it too strict, because the WordPress admin loads inline scripts from many sources. If you scope your CSP only to the public site (using the is_admin() check), you avoid that risk.

🛡 Re-run the audit when you're done

After applying these fixes, run the Security Audit again to confirm your score has improved. Each fix should move you closer to 100/100.

Run free security audit →
Related Guides: All Fix Guides  ·  Fix Headers in Plesk  ·  Security Audit Guide  ·  Beginner Fix Guide
💬 Got a problem?