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:
- 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. - 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.
- 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_permissionyopcache.restrict_api. Un directorio paraopcache.file_cachesolo debe poder ser escrito por el usuario de PHP.
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_apiy 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.