2026年11月12日

2026年11月12日

Ocultar la versión de WordPress: generator, ver= y readme.html

Este artículo es una edición localizada del original en japonés. Leer el original en japonés

En su estado inicial, WordPress escribe su número de versión en el HTML de las páginas y en los feeds. Un atacante puede usarlo como pista para comprobar si es una versión con vulnerabilidades conocidas. En este artículo se comprueba dónde aparece la versión y se explican los pasos para ocultarla. El contenido corresponde a octubre de 2026.

Lo primero que conviene saber es que ocultar la versión es solo una medida de apoyo. También hay maneras de deducir la versión, por ejemplo por el contenido de los archivos CSS y JavaScript, así que ocultarla no siempre impide que se la identifique. Lo que más eficacia tiene es mantener actualizados el núcleo de WordPress, los plugins y los temas.

Dónde se muestra la versión

Lugar Qué se muestra Solución
<head> del HTML Una etiqueta como <meta name="generator" content="WordPress 6.9.4"> Filtro the_generator
Feeds RSS y Atom Una línea como <generator>https://wordpress.org/?v=6.9.4</generator> También el filtro the_generator
URL de CSS y JavaScript Un ?ver= como style.min.css?ver=6.9.4 Filtros style_loader_src y script_loader_src
Cabeceras HTTP X-Powered-By: PHP/8.3.x (la versión de PHP, no la de WordPress) expose_php en php.ini
readme.html Que se usa WordPress (el readme.html actual no incluye el número de versión) Denegar el acceso

La información del sitio que devuelve /wp-json/ de la REST API (nombre, descripción, URL, zona horaria, espacios de nombres, etc.) no incluye la versión de WordPress. No hace falta recortar la salida de la REST API para ocultar la versión.

Paso 1: comprobar lo que se muestra ahora

Sustituye la URL por la de tu sitio y ejecútalo en tu ordenador o en el servidor. Si usas plugins de caché o una CDN, vacía la caché después de los cambios antes de comprobar.

# Etiqueta generator del HTML
curl -s https://example.com/ | grep -i 'name="generator"'

# generator del feed
curl -s https://example.com/feed/ | grep -i '<generator>'

# Lista de valores de ver= que llevan el CSS y el JS
curl -s https://example.com/ | grep -oE 'ver=[0-9][0-9.]*' | sort | uniq -c

# X-Powered-By en las cabeceras HTTP
curl -sI https://example.com/ | grep -i 'x-powered-by'

# ¿Se puede leer readme.html? (si devuelve 200, se puede)
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/readme.html

Si en la lista de ver= aparece el mismo valor que la versión de WordPress que se muestra en "Escritorio" (Dashboard) → "Actualizaciones" (Updates) del panel de administración, significa que con ese CSS o JS se puede conocer la versión de WordPress. Cuando el núcleo de WordPress o el tema cargan CSS y JS sin indicar una versión, la versión de WordPress acaba en ver=.

Paso 2: decidir dónde poner el código

El código de este artículo se coloca en uno de estos dos sitios.

  • mu-plugin (recomendado): si colocas un archivo PHP en la carpeta wp-content/mu-plugins/ (créala si no existe), se carga sin necesidad de activarlo. El efecto continúa aunque cambies de tema
  • functions.php de un tema hijo: si lo escribes en el functions.php del tema padre, se pierde al actualizar el tema

Si lo haces como mu-plugin, guarda el código del siguiente paso en un solo archivo con un nombre como wp-content/mu-plugins/hide-wp-version.php.

Paso 3: ocultar la etiqueta generator y la versión de ver=

<?php
/**
 * Plugin Name: Hide WordPress Version
 * Description: Oculta la versión de WordPress que aparece en la etiqueta generator y en el ver= de CSS y JS.
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Vaciar el <meta name="generator"> del HTML y el <generator> de los feeds.
add_filter( 'the_generator', '__return_empty_string' );

/**
 * Solo cuando el ver= de CSS y JS es igual a la versión de WordPress, sustituirlo por otro valor.
 * Como se sustituye y no se elimina ver=, la caché del navegador se renueva tras actualizar WordPress.
 */
function wpdr_replace_wp_version_query( $src ) {
	if ( ! is_string( $src ) || '' === $src ) {
		return $src;
	}

	$query = wp_parse_url( $src, PHP_URL_QUERY );
	if ( empty( $query ) ) {
		return $src;
	}

	parse_str( $query, $args );
	$wp_version = get_bloginfo( 'version' );

	if ( isset( $args['ver'] ) && $args['ver'] === $wp_version ) {
		$src = add_query_arg( 'ver', substr( wp_hash( $wp_version ), 0, 10 ), $src );
	}

	return $src;
}
add_filter( 'style_loader_src', 'wpdr_replace_wp_version_query', 20 );
add_filter( 'script_loader_src', 'wpdr_replace_wp_version_query', 20 );

Los puntos clave son estos.

  • El filtro the_generator actúa tanto en la etiqueta generator del HTML como en el generator de los feeds. remove_action( 'wp_head', 'wp_generator' ); solo elimina la etiqueta del HTML, así que no afecta a los feeds
  • Si eliminas todos los ?ver=, al actualizar plugins o temas el navegador puede seguir usando el CSS y el JS antiguos. El código anterior solo sustituye el valor cuando coincide con la versión de WordPress, por una cadena distinta generada a partir de esa versión. Al actualizar WordPress el valor también cambia, de modo que la caché se renueva
  • No se modifican los ver= a los que los plugins o los temas ponen su propio número de versión

Cuando lo hayas guardado, comprueba con los comandos del paso 1 que ya no aparece generator y que en la lista de ver= no queda la versión de WordPress. Si usas plugins de caché, vacía la caché antes de comprobar.

Paso 4: detener la cabecera X-Powered-By

X-Powered-By es una cabecera que añade PHP, no WordPress, y deja ver la versión de PHP. Para detenerla, pon expose_php en Off en el archivo de configuración de PHP (php.ini). Este ajuste solo se puede cambiar en php.ini.

expose_php = Off

Este es un ejemplo con PHP-FPM en Ubuntu en un VPS (el 8.3 debe coincidir con la versión de PHP que uses).

# Comprobar el ajuste actual
grep -n '^expose_php' /etc/php/8.3/fpm/php.ini

# Tras modificarlo, reiniciar PHP-FPM
sudo systemctl restart php8.3-fpm

El php.ini de los paquetes de Ubuntu viene con expose_php = Off en su estado inicial. Si la cabecera aparece, comprueba que no se esté usando otro php.ini. En los servidores de alojamiento compartido, a veces se puede cambiar desde los ajustes de PHP del panel y a veces no.

Si no puedes cambiar php.ini, también hay una manera de quitar la cabecera desde el código de WordPress. Sin embargo, send_headers es un hook que se ejecuta cuando WordPress muestra las páginas del front-end, por lo que no afecta al panel de administración ni a la pantalla de inicio de sesión.

add_action( 'send_headers', function () {
	header_remove( 'X-Powered-By' );
} );

Paso 5: denegar el acceso a readme.html y license.txt

readme.html y license.txt son archivos del núcleo de WordPress. El readme.html actual no incluye el número de versión, pero deja ver que se usa WordPress.

Aunque los borres, vuelven al actualizar el núcleo de WordPress. Es menos trabajo denegar el acceso en el servidor web que borrarlos.

Con Apache (.htaccess)

Añádelo al .htaccess de la carpeta de WordPress. Lo que queda entre # BEGIN WordPress y # END WordPress lo reescribe WordPress, así que escríbelo fuera de ese bloque. Es la sintaxis de Apache 2.4 (Order y Deny from all son la sintaxis antigua de Apache 2.2).

<FilesMatch "^(readme\.html|license\.txt)$">
    Require all denied
</FilesMatch>

Con Nginx

Añádelo al bloque server del sitio, comprueba la configuración y vuelve a cargarla.

location ~* ^/(readme\.html|license\.txt)$ {
    deny all;
}
sudo nginx -t && sudo systemctl reload nginx

Tras añadirlo, ejecuta curl -s -o /dev/null -w '%{http_code}\n' https://example.com/readme.html y comprueba que devuelve 403.

Lo que ocultar no evita

  • El contenido de los archivos CSS y JS de WordPress cambia un poco en cada versión, así que se puede deducir la versión descargando los archivos y comparándolos
  • A veces se puede conocer la versión de los plugins a partir del readme.txt o del ver= de los plugins y los temas. Lo que más se ataca son las vulnerabilidades de los plugins más que las del núcleo
  • Para quien puede iniciar sesión en el panel de administración, la versión se sigue mostrando como siempre

Por eso, además de ocultar la versión, actualiza el núcleo, los plugins y los temas, elimina los plugins que no uses y desactiva la edición de archivos desde el panel de administración, entre otras medidas.

Resumen

  • La versión se muestra principalmente en el generator del HTML y de los feeds y en el ?ver= de CSS y JS. El /wp-json/ de la REST API no incluye la versión de WordPress
  • Con add_filter( 'the_generator', '__return_empty_string' ); se elimina el generator tanto del HTML como de los feeds
  • Si no eliminas ?ver= y solo sustituyes el valor igual a la versión de WordPress, no detienes la renovación de la caché
  • X-Powered-By se detiene con expose_php = Off en php.ini
  • readme.html vuelve tras las actualizaciones aunque lo borres, así que deniega el acceso en el servidor web
  • Ocultarla es una medida de apoyo; lo más importante es seguir actualizando

Artículos relacionados:

¿Dudas sobre WordPress? Quien publica este sitio está en Japón y responde en japonés. Contacto (en japonés)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲