mod_php o PHP-FPM: Riesgo, Rendimiento y Migración

PHP señaló a mod_php como el peor escenario en PHP2Shell. Descubre qué cambia en la arquitectura y cómo migrar a FPM sin detener el sitio.

por Cleverson Gouvêa

mod_php o PHP-FPM: Riesgo, Rendimiento y Migración

Elegir entre mod_php o PHP-FPM ha dejado de ser una decisión de rendimiento para convertirse en una decisión de riesgo. En el aviso del 21 de octubre de 2026 sobre PHP2Shell, el propio equipo de seguridad de PHP escribió que el riesgo es mayor en mod_php, porque el ataque demostrado se activa antes de cualquier línea del código de la aplicación. En esta comparativa muestro la diferencia de arquitectura, seguridad y rendimiento, y cómo migrar sin desconectar el sitio.

TL;DR

  • PHP2Shell (CVE-2026-109618 y CVE-2026-109619) es ejecución remota de código sin autenticación, con CVSS 4.0 de 9,2. La corrección salió el 21/10/2026 en las versiones 8.5.12, 8.4.27, 8.3.36 y 8.2.35.
  • Todos los modos de ejecutar PHP para web están afectados: PHP-FPM, Apache con mod_php, php-cgi, FrankenPHP y LiteSpeed. Pero PHP clasifica a mod_php como el escenario de mayor riesgo.
  • Migrar a PHP-FPM no sustituye el parche. El orden correcto es actualizar PHP primero y migrar después, con calma.
  • El propio manual de PHP ya dice que no se use mod_php en instalaciones nuevas: retiene a Apache en el MPM prefork, lo que limita significativamente la concurrencia.
  • La migración en Apache requiere pocos comandos, pero tiene trampas reales: php_value en el .htaccess deja de funcionar y el pool de FPM necesita ser dimensionado.

Qué cambia en la elección mod_php o PHP-FPM después de PHP2Shell

Hasta la semana pasada, la conversación sobre mod_php o PHP-FPM era sobre memoria, concurrencia y comodidad. mod_php era el camino "funciona a la primera" de Apache, y muchos servidores antiguos se quedaron en él por inercia.

PHP2Shell cambió el peso de esta elección. Los hechos, confirmados en las fuentes oficiales:

  • Dos fallos, ambos sin autenticación. La CVE-2026-109618 es una corrupción de memoria en el tratamiento de out-of-memory (falta de memoria): PHP se interrumpe con datos internos de la petición montados a medias, y la limpieza al final de la petición trata esos datos como válidos. La CVE-2026-109619 es un estado global obsoleto (use-after-free): un dato interno sobrevive a la memoria a la que apuntaba, y la siguiente petición en el mismo proceso utiliza esa basura.
  • Gravedad. Ambas tienen CVSS 4.0 de 9,2, según el equipo de seguridad de PHP. El FAQ de Tenable registra que identificadores con una puntuación de 10,0 circularon antes y eran erróneos.
  • Cronología. Amazon vio un intento de explotación en su honeypot el 17/10, cuatro días antes de la divulgación, según Tenable. Los detalles se filtraron el 20/10, antes de la fecha coordinada, y PHP anticipó la corrección para el 21/10 a las 14h UTC, según Huntress.
  • Exposición. Unit 42, de Palo Alto Networks, contó a través de Cortex Xpanse más de 22,6 millones de instancias expuestas potencialmente vulnerables.

Y la frase que cambia la ecuación de mod_php o PHP-FPM para quien decide la infraestructura procede del aviso oficial de PHP: el riesgo se considera mayor para mod_php, "con un exploit demostrado en el que una petición no autenticada puede activar el ataque antes de que cualquier código de la aplicación se ejecute".

Traduciendo para los responsables: en mod_php, no importa si tu WordPress, Moodle o Laravel está actualizado y bien escrito. El ataque ocurre antes de que la aplicación tenga la oportunidad de hacer cualquier cosa.

Arquitectura: cómo cada modelo ejecuta PHP

mod_php: PHP dentro de Apache

En mod_php (el módulo apache2handler), el intérprete PHP se carga dentro de cada proceso de Apache. Cada proceso hijo del servidor web es, al mismo tiempo, servidor HTTP e intérprete PHP.

Esto tiene dos consecuencias directas:

  1. Apache queda anclado al MPM prefork. El manual de PHP dice que, sin PHP compilado con thread safety (ZTS), mod_php exige prefork, "lo que limita significativamente la concurrencia". La mayoría de los paquetes de distribución no vienen con ZTS.
  2. Todo proceso carga PHP, incluso para servir una imagen. Una petición de logo.png ocupa un proceso entero con el intérprete cargado.

PHP-FPM: PHP como servicio separado

En PHP-FPM (FastCGI Process Manager), PHP se ejecuta en un servicio propio, con pools de procesos. El servidor web (Apache con mod_proxy_fcgi o Nginx) solo reenvía las peticiones .php al pool, por socket Unix o TCP.

Cada pool tiene su usuario, su límite de procesos y sus configuraciones. La documentación de configuración de FPM incluye las directivas que marcan la diferencia en la práctica: user/group, pm.max_children, pm.max_requests, request_terminate_timeout y php_admin_value, esta última con valores que el script no puede sobrescribir con ini_set().

¿Y quién usa Nginx?

Nginx no tiene módulo de PHP embebido. Si tu sitio se ejecuta en Nginx, ya estás en PHP-FPM (o en otro proceso FastCGI) y la pregunta mod_php o PHP-FPM no se aplica. Esto no te exime de PHP2Shell: FPM también está en la lista de afectados.

Seguridad: por qué mod_php es el peor escenario

El punto central en la comparación mod_php o PHP-FPM no es que FPM sea inmune. No lo es. El punto es que mod_php junta tres factores negativos en el mismo proceso.

El ataque se ejecuta antes de la aplicación

El informe técnico de Datadog Security Labs detalla lo que PHP confirmó. En la ruta de subida de CVE-2026-109619, hubo ejecución de comandos tanto en mod_php como en PHP-FPM, pero en FPM el ataque también exige que la aplicación realice una operación adecuada después de que la inicialización haya terminado. En mod_php, no existe esa condición.

Para CVE-2026-109618, Huntress registra que PHP validó RCE completo vía HTTP contra mod_php con los límites estándar de entrada y memoria y configuraciones prefork normales. El laboratorio de Datadog también usó Apache prefork con mod_php.

Un usuario para todos los sitios

En mod_php, todo sitio del servidor se ejecuta como el usuario de Apache (www-data en Debian/Ubuntu, apache en Red Hat). Si un atacante ejecuta un comando a través de un sitio, tiene el permiso de Apache, que normalmente lee los archivos de todos los sitios de ese servidor, incluyendo wp-config.php y config.php con contraseña de base de datos.

En PHP-FPM, el estándar saludable es un pool por sitio, cada uno con su usuario Linux. La invasión de un sitio queda contenida en los permisos de ese usuario. Es el mismo principio de menor privilegio que defiendo en la lista de verificación de hardening de servidor Linux.

Lo que PHP-FPM no resuelve

Aquí reside la trampa comercial: hay gente vendiendo "migra a FPM y estarás seguro". No es cierto.

  • FPM sigue siendo vulnerable sin el parche. El aviso oficial lista a PHP-FPM entre los afectados.
  • Datadog explica que un pool más grande reduce la probabilidad de éxito por intento, pero no elimina el fallo, y que los atacantes intentan ocupar todos los procesos menos uno para caer siempre en el mismo.
  • El exploit exitoso no necesariamente derriba el proceso. El ataque puede generar solo dos a cuatro errores de memoria y luego quedar en silencio.

El orden es siempre: parche primero, arquitectura después.

Rendimiento y consumo de memoria

Prefork contra event

Con mod_php, Apache se ejecuta en prefork: un proceso por conexión, cada uno con PHP cargado. Conexiones lentas, keep-alive y archivos estáticos ocupan procesos pesados.

Con PHP-FPM, Apache puede usar el MPM event, que es el estándar desde Apache 2.4, como recuerda el manual de PHP. Event atiende muchas conexiones con pocos hilos ligeros, y solo lo que es PHP va al pool. El manual llega a llamar al MPM con hilos "recomendado para un mejor rendimiento bajo carga".

Dónde FPM gana en la operación

  • Reiniciar PHP sin reiniciar Apache. Aplicar un parche, cambiar php.ini o matar un proceso bloqueado afecta solo a PHP. Esto cuenta mucho en una semana como esta, en la que PHP pide reiniciar todos los workers después del parche.
  • Límites por sitio. Un sitio con un plugin problemático no consume todos los procesos del servidor, porque su pool tiene un límite propio en pm.max_children.
  • Diagnóstico. request_slowlog_timeout registra el backtrace de peticiones lentas, y pm.status_path muestra en tiempo real cuántos procesos están ocupados.
  • Varias versiones de PHP lado a lado. Un pool en 8.3 y otro en 8.4 en el mismo servidor, algo que mod_php no hace.

Dónde mod_php aún "gana"

Siendo justos: en la balanza mod_php o PHP-FPM, mod_php tiene menos piezas. No hay socket, no hay segundo servicio que monitorizar y el .htaccess con php_value funciona directamente. En un servidor de laboratorio con un único sitio y tráfico mínimo, la diferencia de rendimiento es pequeña. Solo que, después de PHP2Shell, la simplicidad no compensa permanecer en el escenario de mayor riesgo.

Tabla comparativa: mod_php o PHP-FPM

El resumen de la comparación mod_php o PHP-FPM en los criterios que pesan en la operación de un servidor de producción:

Criterio mod_php (Apache) PHP-FPM
Dónde se ejecuta PHP Dentro de cada proceso de Apache Servicio separado, con pools
MPM de Apache Prefork (sin ZTS) Event (estándar de Apache 2.4)
Riesgo en PHP2Shell, según PHP El mayor: exploit antes del código de la aplicación Afectado; en la ruta de subida de la 109619, depende de la operación de la aplicación
Usuario de ejecución Uno solo (usuario de Apache) para todos los sitios Uno por pool, aislando sitios
Reiniciar PHP Reinicia Apache entero Reinicia solo FPM (o un pool)
Varias versiones de PHP No Sí, un pool por versión
Límite de procesos por sitio No pm.max_children por pool
Configuración por directorio php_value en el .htaccess .user.ini o php_admin_value en el pool
Recomendación del manual de PHP No usar en instalaciones nuevas Recomendado para implementaciones modernas
Soporte en Nginx No existe Estándar

Cómo descubrir qué usa tu servidor hoy

Antes de decidir entre mod_php o PHP-FPM en tu caso, confirma el modo actual. Tres formas, de la más rápida a la más completa:

  1. Por Apache. Ejecuta apachectl -M | grep -i php. Si aparece php_module (o php7_module, php8_module), mod_php está cargado.
  2. Por el MPM. Ejecuta apachectl -V | grep -i mpm. prefork es un fuerte indicio de mod_php; event indica que PHP está fuera de Apache.
  3. Con un phpinfo() temporal. El campo Server API muestra Apache 2.0 Handler (mod_php) o FPM/FastCGI. Borra el archivo inmediatamente después: un phpinfo() expuesto entrega la versión y las rutas a quien esté escaneando internet.

Aprovecha para anotar la versión exacta de PHP. Si está por debajo de 8.5.12, 8.4.27, 8.3.36 o 8.2.35 (o del paquete corregido de tu distribución), el problema urgente es ese, no la arquitectura.

Cómo migrar de mod_php a PHP-FPM sin tumbar el sitio

Si la respuesta para mod_php o PHP-FPM en tu servidor es migrar, la hoja de ruta a continuación es válida para Debian y Ubuntu con Apache 2.4, que es el escenario más común que encontramos. Sustituye 8.3 por tu versión.

Paso 0: parche antes de nada

Actualiza PHP a la versión corregida de tu distribución y reinicia Apache. Esto ya cierra la ventana de PHP2Shell en mod_php. La migración viene después, en una ventana planificada. Si aún no lo has hecho, este es el único paso que importa hoy. La duda mod_php o PHP-FPM puede esperar unos días; el parche no. En la publicación sobre cómo proteger el servidor web contra ataques explico las capas de WAF y rate limit que ayudan mientras tanto, sin sustituir la corrección.

Paso 1: instalar FPM y crear el pool

Instala php8.3-fpm con las mismas extensiones que usa mod_php (compara php -m antes y después). Crea un pool por sitio en /etc/php/8.3/fpm/pool.d/, con usuario propio, socket propio y límites:

  • user y group del sitio, no www-data
  • listen = /run/php/site1.sock
  • pm = dynamic y pm.max_children calculado por la memoria (siguiente paso)
  • pm.max_requests = 500, para reciclar procesos periódicamente
  • request_terminate_timeout coherente con el max_execution_time
  • php_admin_value[session.upload_progress.enabled] = 0, si la aplicación no usa barra de progreso de subida

Este último elemento procede del aviso oficial: bloquea las rutas de sesión confirmadas de CVE-2026-109619, pero no otras variantes. PHP pide aplicar en php.ini, en el pool de FPM o en la configuración del servidor, y para no confiar en ini_set() ni en .user.ini.

Paso 2: dimensionar el pool

La fórmula básica es pm.max_children = memoria disponible para PHP ÷ memoria media por proceso. Mide el consumo real con ps durante la hora punta, no uses memory_limit como estimación. Un pool demasiado pequeño genera cola y error 503; uno demasiado grande lleva al servidor a usar swap.

Si el servidor ejecuta Moodle, la cuenta cambia bastante entre horario normal y semana de exámenes. Los números de referencia están en la guía de requisitos de servidor para Moodle 5.x.

Paso 3: cambiar el modo de Apache

En Debian/Ubuntu, la secuencia típica es:

  1. a2enmod proxy_fcgi setenvif
  2. a2enconf php8.3-fpm (o un SetHandler "proxy:unix:/run/php/site1.sock|fcgi://localhost" en el vhost, para pool por sitio)
  3. a2dismod php8.3
  4. a2dismod mpm_prefork y a2enmod mpm_event
  5. apachectl configtest y, si todo está correcto, systemctl restart apache2

El cambio interrumpe las conexiones por unos pocos segundos. Hazlo en horario de bajo tráfico y ten anotado el camino de vuelta: los mismos comandos, invertidos.

Paso 4: las trampas que rompen el sitio

  • php_value y php_flag en el .htaccess. Estas directivas solo existen en mod_php. En FPM, Apache devuelve un error 500. Mueve los valores al pool (php_admin_value) o a .user.ini.
  • Permisos de archivo. Con el nuevo usuario del pool, las carpetas de subida y caché que pertenecían a www-data dejan de ser escribibles. Ajusta el propietario antes del cambio.
  • Variables de entorno y autenticación HTTP. Algunas aplicaciones leen las cabeceras de autorización de una manera que cambia en FastCGI. Prueba el inicio de sesión e integraciones de API.
  • Timeouts. El ProxyTimeout de Apache necesita ser mayor que el tiempo de las tareas largas, como informes e importaciones.

Paso 5: validar y monitorizar

Confirma en el phpinfo() temporal que el Server API se ha convertido en FPM, ejecuta los flujos críticos (inicio de sesión, checkout, subida, cron) y acompaña el slowlog y la página de estado del pool en las primeras 48 horas. Solo considera el cambio de mod_php o PHP-FPM concluido después de ese período sin alertas.

Cuándo no migrar ahora

En la decisión mod_php o PHP-FPM, la respuesta a largo plazo es FPM, pero hay momentos en que hacerlo ahora es un error.

  • Cuando PHP aún no ha sido actualizado. Migrar un PHP vulnerable a FPM cambia el escenario de mayor riesgo por uno aún vulnerable. Parche primero.
  • Cuando el servidor ejecuta PHP 7.3 o más antiguo. Para las versiones 5.4 a 7.3 no hay corrección del proyecto, y para 7.4 a 8.1 solo existen backports mínimos de código fuente, según Tenable. Aquí el plan es actualizar PHP y la aplicación. La CISA recomienda desconectar sistemas PHP que no pueden ser actualizados o que ejecutan 8.1 o anterior.
  • Cuando el servidor es gestionado por un panel. cPanel, Plesk y HestiaCP tienen su propia forma de cambiar el modo PHP por dominio. Modificar Apache manualmente puede ser deshecho en la próxima actualización del panel. Usa la opción del panel.
  • En medio de un período crítico. Semana de matrícula, campaña de ventas o examen en el AVA no es momento de cambiar la arquitectura. Actualiza PHP, aplica las mitigaciones oficiales y agenda la migración.

Y un punto sobre las mitigaciones: memory_limit=-1, session.upload_progress.enabled=0 y, en PHP 8.5, fatal_error_backtraces=0 reducen rutas de ataque específicas. PHP dice textualmente que no hacen que la instalación sea segura y que actualizar es la única opción segura. El 23/10 salió un tercer aviso, CVE-2026-115560, relacionado con el upload progress de sesión. El vector es improbable: exige que la aplicación cambie el session.serialize_handler en tiempo de ejecución. La corrección viene en la próxima versión regular, y la recomendación es mantener session.upload_progress.enabled=0 hasta entonces.

Cómo Agathas Web resuelve esto

Administro servidores Linux con PHP desde 2008: Moodle de instituciones educativas, WordPress, Laravel y sistemas que se comunican con la API oficial de WhatsApp. La pregunta mod_php o PHP-FPM, para mí, tiene una respuesta estándar desde hace años: FPM, un pool por aplicación, cada uno con su usuario. PHP2Shell solo dejó claro por qué esto importa.

En el alojamiento gestionado de Agathas Web, lo que está incluido para un escenario como este:

  • Actualización del runtime de PHP en ventana combinada, con pruebas en staging antes, como parte de la rutina de parches de sistema operativo, PHP y base de datos.
  • Arquitectura revisada: diagnóstico del modo actual (mod_php, FPM, panel), plan de migración a PHP-FPM con pools aislados, dimensionamiento de pm.max_children basado en el consumo real y plan de reversión.
  • Capas de protección ya previstas en el servicio: WAF de Cloudflare, fail2ban y hardening del sistema operativo, para reducir la exposición mientras se aplica un parche.
  • Monitorización 24/7 con alertas en el WhatsApp del equipo técnico, para detectar señales como ráfagas de error de memoria y procesos de PHP abriendo shell.
  • Respuesta a incidentes con plan documentado, y copias de seguridad verificadas con restauración probada.

Cómo funciona: nos das acceso al servidor (o empezamos por una migración a una infraestructura nueva), hacemos el inventario de versiones y modos de PHP, presentamos el plan con ventana y rollback, y ejecutamos con tu aprobación por escrito. La migración del proveedor actual entra en la configuración. Para contratar, habla con el equipo a través de la página de alojamiento gestionado y dinos cuántos sitios y qué aplicaciones se ejecutan en el servidor.

Conclusión: mod_php o PHP-FPM, la respuesta se ha vuelto sencilla

Para un servidor que atiende a internet, la respuesta para mod_php o PHP-FPM es PHP-FPM, con un pool aislado por sitio. El manual de PHP ya desaconsejaba mod_php en instalaciones nuevas por rendimiento y operación, y PHP2Shell añadió el argumento de seguridad: es el escenario que el propio equipo de PHP señala como el de mayor riesgo.

Solo no inviertas el orden. Primero, PHP actualizado a 8.5.12, 8.4.27, 8.3.36, 8.2.35 o el paquete corregido de la distribución, con todos los workers reiniciados. Después, la migración planificada, con pruebas y camino de vuelta. Y recuerda que PHP 8.2 llega al fin de vida el 31/12/2026: si tu servidor está en él, vale la pena casar la migración a FPM con la actualización de versión.

Si no tienes un sysadmin para hacer esto ahora, el alojamiento gestionado de Agathas Web asume el parche, la migración y la monitorización.

Fuentes: aviso de seguridad y archivo de noticias de PHP, manual de PHP sobre Apache, configuración de PHP-FPM, Tenable, Unit 42, Datadog Security Labs, Huntress, watchTowr y Help Net Security.