Hiding the WordPress Version: Why It Helps and Why It Is Not Enough

WordPress leaks its version in several places. Hiding it reduces bot noise but is no substitute for actually updating.

WordPress leaks its version in several public places: inside a <meta name="generator"> tag, in RSS feeds, in the ?ver= query parameter on CSS and JS files, and in readme.html and license.txt. Attackers scan the web for sites running versions with known CVEs, and any version indicator makes their filtering job easier. Hiding the version does not patch a real vulnerability, but it dramatically reduces the volume of targeted scans.

Why this matters

A typical scanner works in two stages: version discovery, then exploitation. If discovery fails (no meta generator, no readme, no ?ver=) the scanner usually gives up and moves to the next site. This does not make the site invulnerable — a determined attacker can still fingerprint a version through subtle behavior (response timing, specific error strings) — but they need to invest effort. The difference between 100 automated scans a day and two scans a day is the difference between "running an unmaintained blog under constant attack noise" and "running quietly". It also makes the Wayback Machine less useful to an attacker who wants to time upgrades.

How to detect

Run:

curl -s https://example.com/ | grep -i "generator"
curl -s https://example.com/feed/ | grep -i "generator"
curl -s https://example.com/readme.html | head -5
curl -sI https://example.com/wp-content/plugins/jquery/jquery.js | grep -i "ver="

If you see <meta name="generator" content="WordPress 6.4.2"> or <generator>https://wordpress.org/?v=6.4.2</generator> in the feed, the version is exposed. A ?ver=6.4.2 parameter on CSS or JS asset URLs also leaks it.

How to fix

Add to an mu-plugin or child theme. This block handles every source at once:

// 1. Strip the meta generator from HTML
remove_action('wp_head', 'wp_generator');

// 2. Empty the generator value in RSS/Atom/RDF feeds
add_filter('the_generator', '__return_empty_string');

// 3. Drop ?ver= from core CSS/JS URLs
add_filter('style_loader_src', 'rp_strip_version_query', 10, 2);
add_filter('script_loader_src', 'rp_strip_version_query', 10, 2);
function rp_strip_version_query($src, $handle) {
    if (strpos($src, 'ver=') !== false) {
        $src = remove_query_arg('ver', $src);
    }
    return $src;
}

Block the readme files at the web server:

# In root .htaccess
<FilesMatch "^(readme\.html|license\.txt|wp-config-sample\.php)$">
    Require all denied
</FilesMatch>
location ~* /(readme\.html|license\.txt|wp-config-sample\.php)$ {
    deny all;
}

Common mistakes

Mistake one: treating concealment as a substitute for updates. It is not. A site on WordPress 5.8 is going to break eventually even if the version is hidden — a future vulnerability will not skip it just because the number is invisible. Updates are the only real fix; hiding is a complementary layer. Mistake two: deleting readme.html manually — WordPress recreates it on the next core update. Only a server-level rule in .htaccess is durable. Mistake three: stripping ?ver= from plugin assets and discovering that browser caches no longer invalidate. The parameter is used for cache busting; if you remove it, add another mechanism (filemtime or hash) or use hashed filenames. Mistake four: relying on a "Hide WP Version" plugin that itself has vulnerabilities. The code above is five lines — add it directly, no plugin required.

Verifying the fix

Re-run every curl command from the detection step. All four should produce empty output or 403. Then visit the site in Chrome > View Source and confirm "WordPress" no longer appears in the rendered HTML. Finally, run wpscan --url https://example.com — it should report "Could not detect WordPress version".

Tip: Version hiding is the last 10% after everything else is in order. If your site is still on WP 6.0 in 2026, do not invest effort in hiding — invest it in upgrading.