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 →

OPcache explicado: la caché de bytecode de PHP

OPcache mantiene el código PHP ya traducido en memoria compartida. Ahorra tiempo de cálculo, pero exige cuidado en los despliegues, los reinicios y en servidores con varios clientes.

Actualizado 5 min de lecturaConocimiento de PHP

En resumen: OPcache es la caché de bytecode integrada de PHP. Según el manual, guarda bytecode precompilado en la memoria compartida, de modo que PHP no tiene que cargar y traducir los scripts en cada petición. Forma parte de la distribución desde PHP 5.5.0. OPcache hace que PHP sea más rápido, pero no sustituye una actualización: tras un parche de PHP deben reiniciarse todos los procesos de PHP, no basta con vaciar la caché.

¿Qué hace exactamente OPcache?

En cada petición, PHP debe leer un script, descomponerlo y traducirlo a opcodes, es decir, a las instrucciones para la máquina virtual de la Zend Engine. En un CMS con cientos de archivos es mucho trabajo que se repite constantemente.

OPcache guarda el resultado de esta traducción en la memoria compartida (shared memory). Todos los workers de un servicio PHP-FPM o todos los procesos de Apache con mod_php acceden a ella. Si un archivo no cambia, su bytecode se reutiliza directamente.

OPcache se carga como extensión Zend, no como extensión normal:

; Linux (ruta según el paquete)
zend_extension=opcache.so
; Windows
; zend_extension=php_opcache.dll

Según el manual, OPcache debe cargarse antes que Xdebug si ambos están activos.

Es importante saber a quién pertenece la memoria compartida. En PHP-FPM la crea el proceso maestro y la utilizan todos los workers de todos los pools de ese servicio. En mod_php pertenece al proceso principal de Apache. Por eso, reiniciar el servicio vacía por completo la caché, mientras que un solo worker nuevo no lo hace. En la línea de comandos no hay memoria compartida entre invocaciones, porque cada invocación es un proceso propio; allí OPcache está desactivado por defecto.

Para quienes toman decisiones basta una regla práctica: OPcache casi siempre tiene sentido en servidores de producción, pero exige un procedimiento claro para despliegues y actualizaciones.

¿Qué ajustes de OPcache importan?

Los siguientes valores proceden de la página de configuración del manual de PHP. La columna «Modificable» indica dónde puede fijarse un ajuste: INI_SYSTEM solo en php.ini o en la configuración del servidor, INI_ALL en cualquier lugar.

Directiva Valor por defecto Modificable Significado
opcache.enable 1 INI_ALL caché activada o desactivada; en tiempo de ejecución solo se puede desactivar
opcache.enable_cli 0 INI_SYSTEM caché para la línea de comandos
opcache.memory_consumption 128 INI_SYSTEM tamaño de la memoria compartida en MB
opcache.interned_strings_buffer 8 INI_SYSTEM memoria para cadenas de caracteres compartidas en MB
opcache.max_accelerated_files 10000 INI_SYSTEM número máximo de scripts en caché
opcache.validate_timestamps 1 INI_ALL comprueba si los archivos han cambiado
opcache.revalidate_freq 2 INI_ALL intervalo de comprobación en segundos, 0 = en cada petición
opcache.restrict_api vacío INI_SYSTEM permite las funciones de la API solo desde una ruta
opcache.validate_permission 0 INI_SYSTEM comprueba los permisos de archivo frente al usuario actual
opcache.validate_root 0 INI_SYSTEM evita colisiones de nombres con chroot
opcache.file_cache vacío INI_SYSTEM segundo nivel de caché en el disco

Una configuración de producción típica podría ser así. Compruebe antes los valores con su aplicación:

opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.restrict_api=/var/www/admin-tools

Quien fija opcache.validate_timestamps=0 gana algo de rendimiento. Pero entonces OPcache ya no detecta en absoluto los archivos modificados y cada despliegue requiere restablecer la caché o reiniciar.

Observar la caché y vaciarla de forma selectiva

OPcache incluye funciones propias, entre ellas opcache_get_status(), opcache_get_configuration(), opcache_invalidate() y opcache_reset(). Una breve mirada de solo lectura al nivel de llenado:

<?php
$status = opcache_get_status(false);
if ($status === false) {
    exit("OPcache ist nicht aktiv.\n");
}
printf("Gecachte Skripte: %d\n", $status['opcache_statistics']['num_cached_scripts']);
printf("Freier Speicher: %.1f MB\n", $status['memory_usage']['free_memory'] / 1048576);

En servidores con varios sitios web, no toda aplicación debería poder vaciar o leer la caché de todas las demás. Para eso sirve opcache.restrict_api: solo los scripts situados bajo la ruta indicada pueden llamar a las funciones de la API.

JIT: el compilador de código máquina en OPcache

Desde PHP 8.0, OPcache incluye un compilador JIT que traduce opcodes a código máquina. Según el RFC del JIT, es una parte casi independiente de OPcache y guarda el código generado en una zona propia de la memoria compartida. Se controla mediante opcache.jit y opcache.jit_buffer_size. Desde PHP 8.4.0, opcache.jit está por defecto en disable. Cuándo compensa el JIT lo explica el artículo JIT y rendimiento en PHP 8.

OPcache y seguridad: qué rige tras PHP2Shell

OPcache no es una herramienta de seguridad, pero tiene consecuencias para ella. Tras PHP2Shell son importantes tres puntos:

  1. Restablecer la caché no es actualizar. opcache_reset() solo vacía los scripts en caché. El intérprete con las vulnerabilidades CVE-2026-109618 y CVE-2026-109619 permanece en memoria hasta que todos los procesos se hayan reiniciado. Cómo hacerlo se explica en Reiniciar PHP-FPM y todos los workers.
  2. El código introducido puede seguir vivo en la caché. Si un archivo se manipuló y luego se sustituyó, con la comprobación de marcas de tiempo desactivada puede seguir ejecutándose la versión antigua. Por eso, tras una limpieza forma parte del proceso un reinicio.
  3. La memoria compartida necesita separación. En servidores con varios clientes ayudan los pools de PHP-FPM propios por cada sitio web, así como opcache.validate_permission y opcache.restrict_api. Un directorio para opcache.file_cache solo debe poder ser escrito por el usuario de PHP.
Consejo

Después de cada actualización, compruebe si la hora de inicio de los procesos de PHP es posterior a la actualización. Una OPcache llena y con una tasa de aciertos alta no demuestra que se esté ejecutando el intérprete nuevo.

¿Cómo ayuda PHP SOS en concreto?

  • Revisar la configuración: leemos la configuración activa de OPcache para cada modo de ejecución y la contrastamos con su aplicación.
  • Proteger los despliegues: establecemos un procedimiento fijo de restablecimiento de la caché y reinicio para que no siga ejecutándose ninguna versión antigua del código.
  • Separar clientes: en el marco del refuerzo del servidor configuramos pools separados, restrict_api y los permisos adecuados.
  • Controlar tras las actualizaciones: demostramos mediante las horas de inicio de los procesos que el intérprete actualizado está realmente activo.

Preguntas frecuentes

¿Está OPcache activo por defecto?

OPcache se distribuye con PHP desde PHP 5.5.0 y, según el manual, funciona con opcache.enable=1 por defecto en cuanto la extensión está cargada. Que esté cargada depende de su paquete o de su proveedor de hosting. En la línea de comandos, en cambio, OPcache está desactivado por defecto (opcache.enable_cli=0).

¿Por qué sigo viendo el código antiguo después de un despliegue?

Probablemente OPcache comprueba los cambios en los archivos solo a intervalos o no los comprueba nunca. Con opcache.revalidate_freq espera por defecto dos segundos y con opcache.validate_timestamps=0 nunca comprueba. Entonces solo ayuda restablecer la caché o reiniciar PHP-FPM o Apache.

¿Basta con opcache_reset() tras una actualización de PHP?

No. opcache_reset() solo vacía la caché de scripts traducidos. Una actualización de PHP sustituye el propio intérprete, que está cargado en cada proceso en ejecución. Para ello hay que reiniciar todos los procesos de PHP.

¿Está el JIT activado en PHP 8.4?

Según el manual, opcache.jit tiene por defecto el valor disable desde PHP 8.4.0, mientras que opcache.jit_buffer_size es de 64M. Antes era al revés: jit en tracing y búfer en 0. En ambos casos el JIT no está activo sin una configuración deliberada.

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