Situación actual: PHP2Shell. Dos vulnerabilidades críticas de PHP (CVSS 9.2) permiten ejecutar código malicioso sin autenticación. Parches desde el 21/10/2026.Qué hacer ahora →

expose_php y cabecera Server: ocultar los datos de versión

PHP, Nginx y Apache indican por defecto su número de versión en cada respuesta. Eso no protege de ataques, pero da a los escáneres una cómoda lista de objetivos. Cómo desactivarlo sin perder la visión de conjunto.

Actualizado 6 min de lecturaRefuerzo y configuración

En resumen: con expose_php = Off en el php.ini desaparece la cabecera X-Powered-By: PHP/…. A ello se añaden server_tokens off; en Nginx y ServerTokens Prod y ServerSignature Off en Apache. Eso no hace más seguro a ningún servidor, pero quita a los escaneos automatizados una lista de versiones ya hecha. Su propio nivel de parcheo se comprueba después mediante el gestor de paquetes y los procesos, no mediante cabeceras.

La tarjeta de visita en cada respuesta

Quien quisiera saber después del 21 de octubre de 2026 cuántos servidores muestran todavía una versión antigua de PHP no tenía que buscar mucho. Muchos servidores web indican su versión de PHP en cada respuesta, por ejemplo como X-Powered-By: PHP/8.2.20. Los servicios de búsqueda de alcance mundial leen estos datos, y Unit 42 de Palo Alto Networks contó para PHP2Shell más de 22,6 millones de instancias expuestas y potencialmente vulnerables.

Para los atacantes, un dato así es un atajo. Pueden preseleccionar objetivos en lugar de probar a ciegas. Para los responsables, en cambio, es inútil, porque ni siquiera prueba el propio nivel de parcheo: las distribuciones suelen entregar las correcciones como backport sin un nuevo número de versión.

Este artículo muestra dónde revelan información de versión PHP y el servidor web y cómo desactivarla. También dice con franqueza qué aporta eso y qué no.

Dónde aparecen los datos de versión

La versión de PHP es solo uno de varios lugares. Merece la pena una visión completa, porque un único lugar olvidado anula las demás medidas.

Lugar del hallazgo Ejemplo Cómo desactivarlo
Cabecera HTTP de PHP X-Powered-By: PHP/8.3.x expose_php = Off en el php.ini
Cabecera HTTP del servidor web Server: nginx/1.x o Server: Apache/2.4.x (Debian) server_tokens off; o ServerTokens Prod
Páginas de error del servidor web línea de versión bajo las páginas 404 o 500 los mismos ajustes; en Apache, además, ServerSignature Off
Cabeceras de la aplicación indicaciones propias X-Powered-By de frameworks fastcgi_hide_header o Header unset
Archivos en el webroot archivos de prueba con phpinfo(), archivos readme y changelog borrarlos o bloquearlos
Mensajes de error de PHP rutas y detalles de versión en el navegador display_errors = Off

Los archivos de prueba con phpinfo() son, con diferencia, la fuente más fructífera para los atacantes. Revelan no solo la versión, sino toda la configuración; más información en el artículo Archivos phpinfo(): por qué deben desaparecer.

Desactivar expose_php en el php.ini

Según el manual de PHP, expose_php está activado por defecto y solo puede cambiarse en el php.ini, no en el pool, ni mediante .htaccess, ni en tiempo de ejecución. Por eso fije el valor en el php.ini de cada modo de funcionamiento que sirva sitios web, es decir, en Debian y Ubuntu, por ejemplo, en /etc/php/<version>/fpm/ y, en su caso, en /etc/php/<version>/apache2/.

; php.ini de la SAPI web; después, reiniciar PHP-FPM o Apache
expose_php = Off

Si se ejecutan varias versiones de PHP en paralelo, cada una necesita su entrada. Reiniciar el servicio correspondiente es obligatorio, porque PHP solo lee el php.ini al arrancar.

Acortar la cabecera Server en Nginx y Apache

Nginx indica por defecto su versión en la cabecera Server y en las páginas de error. Con server_tokens off; solo queda el nombre del producto. Además, una cabecera X-Powered-By que fije la propia aplicación puede filtrarse con fastcgi_hide_header. Según la documentación de Nginx, este por sí mismo solo oculta Status y X-Accel-…, no X-Powered-By.

# en el bloque http, rige para todos los sitios web
server_tokens off;
fastcgi_hide_header X-Powered-By;

En Apache, ServerTokens controla la cabecera Server y, a la vez, la línea de versión bajo las páginas de error. Según la documentación de Apache, el ajuste solo rige para todo el servidor, no por VirtualHost. Como las respuestas de PHP-FPM a través de mod_proxy_fcgi acaban en una tabla de cabeceras distinta de las respuestas del propio servidor, aplique la eliminación en ambas variantes.

# configuración de todo el servidor, p. ej. /etc/apache2/conf-available/security.conf
ServerTokens Prod
ServerSignature Off

# requiere mod_headers
<IfModule mod_headers.c>
    Header unset X-Powered-By
    Header always unset X-Powered-By
</IfModule>

Compruebe la configuración antes de recargarla con nginx -t o apachectl configtest. Cómo activar protección para los visitantes mediante cabeceras, por ejemplo con HSTS o una Content-Security-Policy, lo describe la guía Cabeceras de seguridad para sitios web.

Cómo comprobar el resultado desde fuera

Tras el cambio, compruebe cada dominio por separado, a ser posible también una página de error. Los comandos siguientes solo envían peticiones normales a sus propios sitios web:

# Cabeceras de la página de inicio
curl -sI https://www.example.de/ | grep -iE '^(server|x-powered-by):'
# Cabeceras de una página inexistente
curl -sI https://www.example.de/gibt-es-nicht-4711 | grep -iE '^(server|x-powered-by):'

Se espera una cabecera Server sin número de versión y ningún X-Powered-By. Si sigue apareciendo una versión de PHP, normalmente hay otro php.ini activo o un servicio no se ha reiniciado todavía. Si hay un CDN delante del servidor, compruebe además directamente el servidor de origen, siempre que sea accesible.

Consejo

Aun así, registre la versión de PHP internamente: en su inventario de servidores, con el estado de los paquetes y la fecha de la última comprobación. El nivel de parcheo se acredita a través del servidor, como se describe en Comprobar bien la versión de PHP y el nivel de parcheo.

Qué aporta ocultar y qué no

Con honestidad, desactivar los datos de versión es una medida pequeña con poco esfuerzo. Reduce la probabilidad de aparecer en búsquedas dirigidas de versiones concretas y disminuye la información que un atacante obtiene sin esfuerzo.

Pero no protege de ataques automatizados que simplemente envían sus peticiones a todos los servidores accesibles. Cómo transcurren esos escaneos masivos lo describe el artículo Escaneos masivos tras PHP sin parchear. Frente a PHP2Shell solo ayuda la actualización a PHP 8.5.12, 8.4.27, 8.3.36 o 8.2.35, o a un backport confirmado, seguida del reinicio de todos los procesos de PHP.

Un efecto secundario, en cambio, es muy útil: quien desactiva los datos de versión hacia el exterior debe registrar bien sus versiones internamente. En una situación como PHP2Shell, precisamente ese inventario decide la rapidez con que se sabe qué servidores están afectados.

Por eso, ocultar tiene sentido como parte de un refuerzo básico, no como medida aislada. Pertenece a la misma lista de verificación que las salidas de error desactivadas, los archivos de prueba borrados y los valores de configuración bloqueados.

¿Cómo ayuda PHP SOS en concreto?

  • Registrar los lugares del hallazgo: revisamos cabeceras, páginas de error y webroot de todos los dominios en busca de datos de versión y archivos de prueba olvidados.
  • Adaptar la configuración: fijamos expose_php, cabeceras Server y filtros de cabeceras en cada versión de PHP y cada servidor web, y reiniciamos los servicios de forma controlada.
  • Acreditar internamente el nivel de parcheo: documentamos versiones y estados de paquetes donde corresponde, en el inventario y no en la cabecera HTTP.
  • Integrarlo en el refuerzo: la medida forma parte de nuestro refuerzo de servidores para servidores web PHP.

Preguntas frecuentes

¿Hace expose_php = Off que mi servidor sea seguro?

No. El ajuste solo elimina el dato de versión de la cabecera HTTP. Una versión vulnerable de PHP sigue siendo vulnerable, y los ataques automatizados suelen probar sus peticiones de todos modos en cada servidor accesible. Pero le quita al atacante una preselección sencilla y reduce la visibilidad en los motores de búsqueda de servidores.

¿Puedo fijar expose_php en el pool de FPM o mediante .htaccess?

Según el manual de PHP, expose_php solo puede cambiarse en el php.ini. Por eso fije el valor en el php.ini de cada SAPI web y reinicie después PHP-FPM o Apache.

¿Por qué sigo viendo X-Powered-By a pesar de expose_php = Off?

Entonces suele ser la propia aplicación o un framework quien fija la cabecera, o hay otro php.ini activo. Elimine además la cabecera en el servidor web: con fastcgi_hide_header en Nginx o Header unset en Apache.

¿Cómo compruebo mi nivel de parcheo si la versión ya no es visible?

A través del propio servidor: gestor de paquetes, changelog del paquete con los números CVE y hora de inicio de los procesos de PHP. El dato de la cabecera HTTP nunca fue una prueba, porque las distribuciones suelen aplicar backports sin un nuevo número de versión.

Fuentes

Contacto inmediato

Cada hora cuenta. Escríbanos ahora.

Describa brevemente el sistema, la versión de PHP y lo que ha observado. Nos pondremos en contacto, aclararemos la situación y trabajaremos en remoto hasta que el incidente quede cerrado y documentado.

Ayuda inmediata