La caché FastCGI de Nginx guarda en disco las respuestas que genera PHP-FPM y las sirve directamente en las siguientes peticiones, sin ejecutar PHP ni consultar la base de datos. En una web como WordPress, eso convierte páginas que tardan cientos de milisegundos en respuestas de pocos milisegundos. En este tutorial configurarás la caché en Ubuntu 24.04 con Nginx y PHP 8.3, excluirás a los usuarios logueados y las zonas privadas, comprobarás que funciona con la cabecera de estado y aprenderás a purgarla.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudo. - Nginx y PHP-FPM 8.3 instalados y funcionando (
sudo apt install nginx php8.3-fpm), con una aplicación PHP como WordPress en/var/www/your_domain. - Un dominio (
your_domain) apuntando al servidor. Los ejemplos usan HTTP en el puerto 80; si ya tienes HTTPS con Certbot, los bloqueslocationson los mismos.
Cuándo usar la caché FastCGI
La caché FastCGI almacena la respuesta completa de una URL y se la sirve a todos los visitantes. Funciona muy bien para contenido que es igual para todo el mundo: páginas y entradas de un blog, fichas de producto, páginas de aterrizaje. No debe usarse para respuestas personalizadas: áreas de cliente, carritos, paneles de administración o cualquier página que dependa de la sesión. Por eso la mitad de la configuración consiste en decidir qué no se cachea.
Paso 1: Definir la zona de caché
La zona de caché se declara una vez en el contexto http y después la usan los sitios que quieras. En Ubuntu, cualquier archivo de /etc/nginx/conf.d/ se incluye dentro de http, así que no hace falta tocar nginx.conf:
sudo nano /etc/nginx/conf.d/fastcgi-cache.conf
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=PHPCACHE:100m max_size=2g inactive=60m use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
Qué hace cada parámetro:
levels=1:2reparte los archivos en dos niveles de subdirectorios para no tener miles de archivos en una carpeta.keys_zone=PHPCACHE:100mreserva 100 MB de memoria compartida para las claves y metadatos. Un megabyte guarda unas 8.000 claves, así que da para unas 800.000 URLs.max_size=2glimita el tamaño en disco; al superarlo, Nginx borra las entradas menos usadas.inactive=60melimina las entradas que nadie ha pedido en 60 minutos, aunque sigan siendo válidas.fastcgi_cache_keydefine qué distingue una entrada de otra. Incluir$request_methodevita que una peticiónHEADse sirva como si fueraGET.
Crea el directorio con el propietario del proceso de Nginx:
sudo mkdir -p /var/cache/nginx/fastcgi
sudo chown www-data:www-data /var/cache/nginx/fastcgi
Paso 2: Definir qué peticiones no se cachean
En lugar de encadenar bloques if, usa map para calcular variables que valen 1 cuando una petición no debe usar la caché. Añade este bloque al mismo archivo:
sudo nano /etc/nginx/conf.d/fastcgi-cache.conf
map $request_method $skip_cache_method {
default 1;
GET 0;
HEAD 0;
}
map $args $skip_cache_args {
default 1;
"" 0;
}
map $request_uri $skip_cache_uri {
default 0;
~*^/wp-admin/ 1;
~*^/wp-(login|cron|signup|activate)\.php 1;
~*^/xmlrpc\.php 1;
~*^/wp-json/ 1;
~*/(cart|checkout|my-account)/ 1;
~*sitemap.*\.xml$ 1;
}
map $http_cookie $skip_cache_cookie {
default 0;
~*wordpress_logged_in_ 1;
~*wp-postpass_ 1;
~*comment_author_ 1;
~*woocommerce_items_in_cart 1;
~*PHPSESSID 1;
}
Con esto no se cachean:
- Peticiones que no sean
GEToHEAD, como el envío de formularios. - URLs con parámetros (
?s=busqueda,?add-to-cart=), que suelen ser únicas o tener efectos. - El panel y el login de WordPress, la API REST, el carrito y el proceso de pago de WooCommerce y los sitemaps.
- Visitantes con sesión iniciada, que han comentado o que tienen productos en el carrito.
Si tu aplicación no es WordPress, cambia las rutas de $skip_cache_uri y el nombre de la cookie de sesión por los de tu aplicación.
Paso 3: Activar la caché en el sitio
Abre el bloque server de tu sitio:
sudo nano /etc/nginx/sites-available/your_domain
Deja el bloque que procesa PHP así. Las líneas de fastcgi_cache* son las que activan la caché:
server {
listen 80;
server_name your_domain www.your_domain;
root /var/www/your_domain;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_cache PHPCACHE;
fastcgi_cache_valid 200 301 302 60m;
fastcgi_cache_valid 404 1m;
fastcgi_cache_bypass $skip_cache_method $skip_cache_args $skip_cache_uri $skip_cache_cookie;
fastcgi_no_cache $skip_cache_method $skip_cache_args $skip_cache_uri $skip_cache_cookie;
fastcgi_cache_use_stale error timeout updating http_500 http_503;
fastcgi_cache_background_update on;
fastcgi_cache_lock on;
add_header X-FastCGI-Cache $upstream_cache_status;
}
}
Las directivas nuevas:
fastcgi_cache PHPCACHEusa la zona del paso 1.fastcgi_cache_validfija cuánto tiempo es válida cada respuesta según su código. Los errores 404 se guardan solo un minuto.fastcgi_cache_bypasshace que la petición no lea de la caché yfastcgi_no_cacheque su respuesta no se guarde. Ambas se activan si alguna de las variables vale algo distinto de vacío o0.fastcgi_cache_use_stalesirve la copia caducada si PHP-FPM falla o está regenerando la página, yfastcgi_cache_background_updateregenera en segundo plano mientras tanto.fastcgi_cache_lockhace que, si llegan muchas peticiones a la vez a una URL que no está en caché, solo una llegue a PHP.- La cabecera
X-FastCGI-Cachete permite ver el estado de cada respuesta.
Comprueba la sintaxis y recarga Nginx:
sudo nginx -t
sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Paso 4: Comprobar que la caché funciona
Pide la misma página dos veces y mira la cabecera. La primera petición la genera PHP y se guarda (MISS); la segunda sale de la caché (HIT):
curl -sI http://your_domain/ | grep -i x-fastcgi-cache
curl -sI http://your_domain/ | grep -i x-fastcgi-cache
X-FastCGI-Cache: MISS
X-FastCGI-Cache: HIT
Comprueba que las exclusiones funcionan. El login de WordPress debe devolver BYPASS:
curl -sI http://your_domain/wp-login.php | grep -i x-fastcgi-cache
X-FastCGI-Cache: BYPASS
Y lo mismo una petición con la cookie de un usuario logueado:
curl -sI -H "Cookie: wordpress_logged_in_test=1" http://your_domain/ | grep -i x-fastcgi-cache
X-FastCGI-Cache: BYPASS
Mide la diferencia de tiempo hasta el primer byte entre una respuesta de PHP y una de la caché:
curl -so /dev/null -w "%{time_starttransfer}\n" "http://your_domain/?nocache=1"
curl -so /dev/null -w "%{time_starttransfer}\n" http://your_domain/
0.284512
0.004127
La primera URL lleva un parámetro, así que se salta la caché y la genera PHP; la segunda sale de la caché.
Los valores posibles de la cabecera son HIT, MISS, BYPASS, EXPIRED (estaba caducada y se ha regenerado), STALE (se ha servido caducada porque el backend ha fallado) y UPDATING (caducada y en proceso de regeneración).
Paso 5: Purgar la caché
Cuando publicas o editas contenido, la versión en caché sigue sirviéndose hasta que caduca (60 minutos con esta configuración). Tienes dos formas de forzar la actualización.
Vaciar toda la caché
Es la opción más simple y segura. Nginx vuelve a llenar la caché con las siguientes visitas:
sudo find /var/cache/nginx/fastcgi -type f -delete
Purgar una URL concreta
Cada entrada es un archivo cuyo nombre es el hash MD5 de la clave definida en fastcgi_cache_key. Con levels=1:2, el primer directorio es el último carácter del hash y el segundo, los dos caracteres anteriores. Para purgar http://your_domain/mi-entrada/, calcula el hash de la clave exacta:
KEY="httpGETyour_domain/mi-entrada/"
HASH=$(printf '%s' "$KEY" | md5sum | cut -d' ' -f1)
sudo rm -v "/var/cache/nginx/fastcgi/${HASH: -1}/${HASH: -3:2}/$HASH"
removed '/var/cache/nginx/fastcgi/c/29/b7e0f5...a29c'
La clave debe coincidir carácter a carácter: esquema, método, host tal como lo envía el navegador (con o sin www) y URI.
Si usas WordPress y quieres que la caché se purgue sola al publicar, el plugin Nginx Helper puede borrar los archivos de caché directamente del disco. Indícale la ruta de la caché definiendo en wp-config.php la constante RT_WP_NGINX_HELPER_CACHE_PATH con el valor /var/cache/nginx/fastcgi/, y asegúrate de que PHP-FPM (usuario www-data) tiene permiso de escritura en ella.
Solución de problemas
La cabecera siempre muestra MISS. Nginx no guarda respuestas que incluyen Set-Cookie ni las que envían Cache-Control: private, no-cache o no-store. Revisa las cabeceras que devuelve PHP con curl -sI http://your_domain/. Si la aplicación abre una sesión PHP en todas las páginas, tendrás que corregirlo en la aplicación.
No aparece la cabecera X-FastCGI-Cache. La petición no pasa por el location ~ \.php$ que has editado, o hay otro add_header en ese mismo bloque. Comprueba con sudo nginx -T | grep -n fastcgi_cache que la configuración cargada es la esperada.
Los usuarios ven contenido de otros. La respuesta personalizada se está cacheando. Identifica la cookie de sesión de tu aplicación con las herramientas del navegador, añádela a $skip_cache_cookie, vacía la caché y vuelve a probar.
Conclusión
Has configurado la caché FastCGI de Nginx con PHP-FPM 8.3 en Ubuntu 24.04, con exclusiones para usuarios logueados y zonas privadas, cabecera de diagnóstico, verificación de HIT y BYPASS y dos formas de purgar. Como siguientes pasos, puedes añadir $upstream_cache_status al formato de access_log para medir la tasa de aciertos, ajustar fastcgi_cache_valid según la frecuencia con la que publiques y activar OPcache en PHP para acelerar también las peticiones que no salen de la caché.
