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 →

El ciclo de vida de la petición: inicio, ejecución, apagado

Cómo inicia PHP los procesos, atiende las peticiones y limpia entre ellas. Con las fases de MINIT a MSHUTDOWN y la relación con CVE-2026-109619.

Actualizado 6 min de lecturaConocimiento de PHP

En resumen: el ciclo de vida de la petición en PHP tiene dos niveles. Una vez por proceso, PHP carga sus extensiones (fase MINIT) y las descarga al final (MSHUTDOWN). Para cada petición individual hay una fase propia de inicio y de limpieza (RINIT y RSHUTDOWN). En el funcionamiento web, un worker atiende muchas peticiones una tras otra, por lo que PHP debe restablecer de forma fiable todo el estado de la petición entre dos de ellas. Justo en ese límite estaba CVE-2026-109619, la segunda vulnerabilidad de PHP2Shell.

Dos niveles: el proceso y la petición

Para quien no es técnico ayuda una imagen: un worker de PHP es como una ventanilla en una oficina pública. La ventanilla se prepara una vez por la mañana, con formularios, sellos y ordenador. Después van llegando uno tras otro muchos visitantes. Tras cada visitante, la mesa debe quedar vacía para que el siguiente no vea documentos ajenos.

PHP funciona igual. Al iniciar un proceso carga extensiones y ajustes que son iguales para todas las peticiones. Cada petición recibe entonces su propia memoria, sus propias variables y sus propios datos de petición, como $_GET, $_POST o $_FILES. Al final de la petición, PHP libera esa memoria. El PHP Internals Book lo resume brevemente: PHP olvida la memoria de la petición al final de cada petición.

Las fases del ciclo de vida de la petición en PHP de un vistazo

Los nombres proceden del interior de PHP; cada extensión puede engancharse a estas fases.

Fase Cuándo se ejecuta Con qué frecuencia Tarea típica
MINIT (Module Init) al iniciar el proceso una vez por proceso registrar extensiones, crear entradas ini, datos permanentes
GINIT al crear los globales por proceso; con hilos, por hilo preparar las variables globales de una extensión
RINIT (Request Init) al comienzo de cada petición por petición preparar la memoria y el estado de la petición
Ejecución entre medias por petición se ejecuta su código PHP
RSHUTDOWN al final de cada petición por petición liberar los recursos de la petición
PRSHUTDOWN después de RSHUTDOWN por petición, rara vez usada últimas tareas de limpieza
MSHUTDOWN al terminar el proceso una vez por proceso liberar los datos permanentes

Una frase importante del PHP Internals Book para quienes desarrollan extensiones: los globales no se vacían automáticamente después de cada petición. Lo que debe estar limpio en cada petición se restablece en RINIT. Esta regla muestra por qué el límite entre dos peticiones es un punto crítico para la seguridad.

Al final de una petición, el orden también está establecido: primero se ejecutan las funciones de apagado que usted haya registrado con register_shutdown_function(), después los destructores y luego se vacía el búfer de salida. Solo después viene RSHUTDOWN.

Por qué se reutilizan los workers

En la línea de comandos, un proceso atiende exactamente una petición y termina. En el funcionamiento web sería demasiado lento. PHP-FPM, Apache con mod_php, LiteSpeed y FrankenPHP mantienen por eso workers disponibles que atienden muchas peticiones seguidas. MINIT se ejecuta entonces una vez; RINIT y RSHUTDOWN, miles de veces. Cómo controla FPM sus workers lo explica PHP-FPM explicado.

En PHP-FPM, pm.max_requests establece tras cuántas peticiones se reinicia un worker. El valor por defecto es 0, es decir, ilimitado. El manual presenta el ajuste como remedio frente a las fugas de memoria en bibliotecas de terceros:

; Pool de FPM: reiniciar el worker tras 500 peticiones (contra fugas de memoria)
pm = dynamic
pm.max_children = 20
pm.max_requests = 500

Va aún más lejos el modo worker de FrankenPHP: allí toda la aplicación arranca una vez y permanece en memoria. El estado de la aplicación sobrevive entonces también en el nivel de PHP. El ejemplo muestra la diferencia:

<?php
function anfrageZaehler(): int
{
    static $zaehler = 0;   // en el FPM clásico vuelve a ser 0 en cada petición
    return ++$zaehler;     // en el modo worker el valor sigue contando entre peticiones
}
echo anfrageZaehler();

En el funcionamiento clásico, la llamada siempre muestra 1, porque PHP descarta la memoria de la petición. En el modo worker, el valor crece mientras viva el worker. La documentación de FrankenPHP aconseja, por eso, restablecer de forma consciente el estado propio de la petición entre peticiones.

Dónde incide CVE-2026-109619

CVE-2026-109619 no afecta al código de su aplicación, sino al propio PHP. Según php.net, un estado interno perteneciente a la petición podía sobrevivir a la liberación de su memoria. Una petición posterior en el mismo worker podía utilizarlo antes de que la inicialización lo restableciera. En la imagen: en la mesa de la ventanilla quedó un papel que en realidad ya estaba retirado. No publicamos a propósito detalles de la explotación; la valoración figura en CVE-2026-109619.

Las medidas provisionales de php.net sobre esta vulnerabilidad actúan sobre vías confirmadas y no son una protección completa:

  1. session.upload_progress.enabled=0 en php.ini, en el pool de FPM o en la configuración del servidor, y después reiniciar todos los workers; no mediante ini_set() ni .user.ini.
  2. En PHP 8.5, además, fatal_error_backtraces=0 y reinicio.
  3. Eliminar los ajustes no válidos u obsoletos en cada petición que generen mensajes antes de la inicialización. Según php.net, limitarse a ocultar los mensajes no sirve.
  4. Corregir en la aplicación los errores fatales y los tiempos de espera agotados que puedan provocarse desde fuera, también en callbacks y en el procesamiento de subidas e importaciones.

El remedio real es la actualización a PHP 8.5.12, 8.4.27, 8.3.36 o 8.2.35. Como el fallo está en el worker en ejecución, solo surte efecto tras reiniciar todos los procesos; los pasos figuran en Reiniciar PHP-FPM y todos los workers.

Consejo

Después del reinicio, compruebe con ps -eo pid,lstart,cmd | grep php que todos los procesos de PHP se hayan iniciado después del momento de la actualización.

Qué significa el ciclo de vida para la operación y el desarrollo

Del modelo se deducen reglas claras. Para administradores: una actualización solo es eficaz cuando ya no queda ningún proceso con el código antiguo. Todo modo de funcionamiento con workers de larga vida necesita un reinicio documentado. Para desarrolladores: el código que se ejecuta en modos worker no debe dejar estado propio de la petición, como datos de usuario o permisos, en variables estáticas ni en singletons. Para ambos rige: conservar los logs desde el 20/10/2026, para poder valorar más adelante si antes del parche ocurrió algo.

¿Cómo ayuda PHP SOS en concreto?

Con el servicio de parches de PHP llevamos el modelo a la práctica:

  • Determinar el modo de funcionamiento: comprobamos si se ejecuta PHP-FPM, mod_php, LiteSpeed, FrankenPHP o php-cgi y cómo se gestionan los workers.
  • Actualización y reinicio: aplicamos la versión corregida y reiniciamos cada worker de forma controlada.
  • Revisión de procesos: demostramos con las horas de inicio y las versiones que no queda ningún proceso antiguo.
  • Revisar los modos worker: en aplicaciones de larga vida, examinamos si el estado propio de la petición se restablece correctamente.

Preguntas frecuentes

¿Inicia PHP un proceso nuevo para cada petición?

En la línea de comandos, sí; en el funcionamiento web, normalmente no. PHP-FPM, Apache con mod_php, LiteSpeed o FrankenPHP dejan que un proceso atienda muchas peticiones seguidas. Entre peticiones, PHP limpia el estado de la petición.

¿Qué diferencia hay entre MINIT y RINIT?

MINIT se ejecuta una vez, cuando arranca el proceso de PHP, y crea estructuras permanentes. RINIT se ejecuta al comienzo de cada petición y prepara lo que solo necesita esa petición.

¿Ayuda pm.max_requests frente a CVE-2026-109619?

php.net no menciona los reinicios más frecuentes de workers como medida provisional. El ajuste solo limita cuántas peticiones atiende un worker antes de reiniciarse. Frente a la vulnerabilidad ayuda la actualización con el posterior reinicio de todos los workers.

¿Qué es el modo worker de FrankenPHP?

En el modo worker, la aplicación arranca una vez y permanece en memoria. Esto ahorra tiempo, pero las variables estáticas, los globales y las cachés se conservan entre peticiones. Según la documentación de FrankenPHP, por eso hay que restablecer de forma consciente el estado propio de la petició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