---
title: "Caché de proxy en Nginx como reverse proxy en Ubuntu 24.04"
description: "Configura proxy_cache en Nginx sobre Ubuntu 24.04 para cachear las respuestas del backend, servir contenido obsoleto si falla y medir la tasa de aciertos."
category: "Caché y CDN"
language: es
source: https://cubepath.com/es/docs/caché-y-cdn/cache-de-proxy-nginx-para-reverse-proxy
updated: 2026-09-25
---
# Caché de proxy en Nginx como reverse proxy en Ubuntu 24.04
La caché de proxy de Nginx guarda en disco las respuestas de tu aplicación y las sirve directamente a las siguientes peticiones, sin volver a llamar al backend. Funciona con cualquier servicio HTTP (Node.js, Python, Go, Java) y reduce tanto la latencia como la carga del servidor de aplicación. En este tutorial configurarás Nginx en Ubuntu 24.04 como reverse proxy con caché, lo harás resistente a caídas del backend, excluirás a los usuarios autenticados y medirás la tasa de aciertos.
## Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios `sudo`.
- Nginx instalado (`sudo apt install nginx`).
- Una aplicación HTTP escuchando en `127.0.0.1:3000`. Si todavía no tienes una, el paso 1 levanta un backend de prueba.
- Opcional para HTTPS: un dominio (**your_domain**) con un registro DNS A apuntando a la IP del servidor.
## Paso 1: Levantar un backend de prueba
Si ya tienes tu aplicación en el puerto 3000, salta este paso. Para seguir la guía sin una aplicación real, sirve un directorio con el servidor HTTP incluido en Python:
```bash
mkdir -p ~/backend-demo
echo '
Hola desde el backend
' > ~/backend-demo/index.html
python3 -m http.server 3000 --bind 127.0.0.1 --directory ~/backend-demo
```
Deja esta terminal abierta y abre otra sesión SSH para el resto de pasos. Comprueba que responde:
```bash
curl -s http://127.0.0.1:3000/
```
```output
Hola desde el backend
```
## Paso 2: Definir la zona de caché
La zona de caché se declara una sola vez en el contexto `http`. En Ubuntu, todo lo que pongas en `/etc/nginx/conf.d/` se carga dentro de ese contexto, así que crea allí un archivo:
```bash
sudo nano /etc/nginx/conf.d/proxy-cache.conf
```
```nginx
proxy_cache_path /var/cache/nginx/app
levels=1:2
keys_zone=app_cache:20m
max_size=5g
inactive=24h
use_temp_path=off;
log_format cache '$remote_addr [$time_local] "$request" $status '
'$body_bytes_sent cache=$upstream_cache_status rt=$request_time';
```
Qué significa cada parámetro:
- `levels=1:2` reparte los archivos en dos niveles de subdirectorios para no tener miles de archivos en una misma carpeta.
- `keys_zone=app_cache:20m` reserva 20 MB de memoria compartida para las claves. Cada megabyte almacena unas 8.000 claves.
- `max_size=5g` es el tamaño máximo en disco. Al superarlo, Nginx borra las entradas menos usadas.
- `inactive=24h` elimina las entradas que nadie pide en 24 horas, aunque sigan siendo válidas.
- `use_temp_path=off` escribe directamente en el directorio de caché, sin copiar desde un directorio temporal.
El formato de log `cache` añade el estado de caché de cada petición, que usarás en el paso 6 para calcular la tasa de aciertos. Nginx crea el directorio `/var/cache/nginx/app` al arrancar.
## Paso 3: Configurar el reverse proxy con caché
Crea el bloque de servidor del sitio:
```bash
sudo nano /etc/nginx/sites-available/app
```
```nginx
upstream app_backend {
server 127.0.0.1:3000;
keepalive 16;
}
server {
listen 80;
listen [::]:80;
server_name your_domain;
access_log /var/log/nginx/app-access.log cache;
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache app_cache;
proxy_cache_key "$scheme$host$request_uri";
proxy_cache_valid 200 301 302 10m;
proxy_cache_valid 404 1m;
add_header X-Cache-Status $upstream_cache_status always;
}
}
```
Los puntos importantes:
- `proxy_cache_key` define qué peticiones se consideran la misma. Incluir `$host` evita mezclar respuestas si el mismo Nginx sirve varios dominios. `$request_uri` incluye la query string, así que `/productos?page=1` y `/productos?page=2` se cachean por separado.
- `proxy_cache_valid` fija cuánto tiempo se guarda cada código de estado **cuando el backend no envía** `Cache-Control` o `Expires`. Si el backend los envía, Nginx los respeta.
- Nginx no cachea por defecto las respuestas que contienen `Set-Cookie` ni las que llevan `Cache-Control: private`, `no-store` o `no-cache`.
- La cabecera `X-Cache-Status` te permite ver desde el cliente si la respuesta vino de la caché.
Activa el sitio, desactiva el sitio por defecto si no lo usas, valida la configuración y recarga:
```bash
sudo ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
```
```output
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
```
Pide la misma URL dos veces. Sustituye `your_domain` por tu dominio, o usa `localhost` con `-H 'Host: your_domain'`:
```bash
curl -s -o /dev/null -D - http://your_domain/ | grep -i x-cache-status
curl -s -o /dev/null -D - http://your_domain/ | grep -i x-cache-status
```
```output
X-Cache-Status: MISS
X-Cache-Status: HIT
```
La primera petición llega al backend (`MISS`) y la segunda se sirve desde disco (`HIT`). Si paras el backend de prueba con `Ctrl+C`, la página seguirá respondiendo con `HIT` durante los 10 minutos de validez.
## Paso 4: No cachear usuarios autenticados
Cachear una página personalizada y servírsela a otro usuario es el error más grave que se puede cometer con una caché. Nginx **no** mira la cabecera `Authorization` ni las cookies por sí solo, así que debes decirle cuándo saltarse la caché.
Edita el bloque `location /` del sitio:
```bash
sudo nano /etc/nginx/sites-available/app
```
Añade estas líneas debajo de `proxy_cache_valid`. Sustituye `sessionid` por el nombre de la cookie de sesión de tu aplicación:
```nginx
proxy_cache_bypass $http_authorization $cookie_sessionid;
proxy_no_cache $http_authorization $cookie_sessionid;
```
`proxy_cache_bypass` hace que esas peticiones no se sirvan desde la caché y `proxy_no_cache` impide que su respuesta se guarde. Cualquier valor no vacío en las variables activa la condición. Las peticiones que no son `GET` ni `HEAD` nunca se cachean.
Si tu aplicación tiene zonas que nunca deben cachearse, como `/admin/` o `/api/`, lo más claro es darles su propio `location` sin `proxy_cache`:
```nginx
location /admin/ {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
```
Valida y recarga con `sudo nginx -t && sudo systemctl reload nginx`. Comprueba que una petición con cabecera de autorización ya no usa la caché:
```bash
curl -s -o /dev/null -D - -H 'Authorization: Bearer test' http://your_domain/ | grep -i x-cache-status
```
```output
X-Cache-Status: BYPASS
```
## Paso 5: Servir contenido obsoleto y evitar avalanchas al backend
Dos problemas típicos de una caché en producción son las caídas del backend y la avalancha de peticiones idénticas cuando una entrada muy popular caduca. Nginx resuelve ambos con unas pocas directivas.
Edita de nuevo el sitio:
```bash
sudo nano /etc/nginx/sites-available/app
```
Añade al bloque `location /`, debajo de las líneas anteriores:
```nginx
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_revalidate on;
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
```
- `proxy_cache_use_stale` sirve la copia caducada si el backend devuelve error, no responde o la entrada se está actualizando.
- `proxy_cache_background_update on` actualiza en segundo plano las entradas caducadas mientras el cliente recibe la copia anterior (`STALE` o `UPDATING`), sin esperar al backend.
- `proxy_cache_revalidate on` usa peticiones condicionales (`If-Modified-Since`, `If-None-Match`) para renovar entradas, de modo que el backend puede responder `304` sin reenviar el cuerpo.
- `proxy_cache_lock on` hace que, cuando varias peticiones buscan la misma entrada que no está en caché, solo una llegue al backend y las demás esperen hasta 5 segundos a que se rellene.
Valida y recarga:
```bash
sudo nginx -t && sudo systemctl reload nginx
```
Para probarlo, reduce temporalmente `proxy_cache_valid 200` a `10s`, recarga, pide la página, para el backend y espera 15 segundos. La siguiente petición seguirá devolviendo la página:
```output
X-Cache-Status: STALE
```
Vuelve a dejar el valor original cuando termines la prueba.
## Paso 6: Medir la tasa de aciertos
El log del sitio registra el estado de caché de cada petición gracias al formato `cache` del paso 2. Cuenta cuántas peticiones hay de cada tipo:
```bash
sudo grep -o 'cache=[A-Z-]*' /var/log/nginx/app-access.log | sort | uniq -c | sort -rn
```
```output
8412 cache=HIT
1175 cache=MISS
301 cache=BYPASS
42 cache=-
17 cache=STALE
```
`cache=-` corresponde a peticiones que no pasaron por la caché (por ejemplo, las de `/admin/`). Una tasa de `HIT` baja suele indicar que el backend envía `Set-Cookie` o `Cache-Control: private` en páginas que podrían cachearse, o que la clave incluye parámetros que cambian en cada petición, como identificadores de campañas.
Para ver el espacio ocupado en disco:
```bash
sudo du -sh /var/cache/nginx/app
```
## Paso 7: Refrescar el contenido cacheado
La versión libre de Nginx no incluye una orden para purgar una URL concreta. Tienes dos alternativas sin módulos adicionales.
**Vaciar toda la caché.** Borra los archivos; Nginx reconstruye las entradas a medida que llegan peticiones:
```bash
sudo find /var/cache/nginx/app -type f -delete
```
**Forzar la actualización de una URL.** Permite que ciertas IP de confianza envíen una cabecera que salta la caché y guarda la respuesta nueva. Añade al archivo `/etc/nginx/conf.d/proxy-cache.conf`:
```nginx
geo $refresh_allowed {
default 0;
127.0.0.1 1;
}
map "$refresh_allowed:$http_x_cache_refresh" $cache_refresh {
default 0;
"1:1" 1;
}
```
Y en el bloque `location /` del sitio, añade `$cache_refresh` a la línea existente de `proxy_cache_bypass`:
```nginx
proxy_cache_bypass $http_authorization $cookie_sessionid $cache_refresh;
```
Recarga Nginx. Desde el propio servidor, esta petición obtiene la página del backend y sobrescribe la entrada en caché:
```bash
curl -s -o /dev/null -D - -H 'Host: your_domain' -H 'X-Cache-Refresh: 1' http://127.0.0.1/ | grep -i x-cache-status
```
```output
X-Cache-Status: BYPASS
```
La petición siguiente, sin la cabecera, devolverá `HIT` con el contenido nuevo. Las peticiones con esa cabecera desde cualquier otra IP se ignoran.
## Paso 8: Añadir HTTPS
Con el dominio apuntando al servidor, abre los puertos web y obtén un certificado de Let's Encrypt con Certbot, que modifica el bloque `server` automáticamente:
```bash
sudo ufw allow 'Nginx Full'
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d your_domain
```
La caché funciona igual sobre HTTPS. Como la clave incluye `$scheme`, las respuestas HTTP y HTTPS se guardan por separado; tras la redirección a HTTPS que añade Certbot, solo se usará la segunda.
## Solución de problemas
**Siempre aparece `MISS`.** Revisa las cabeceras que envía el backend con `curl -sI http://127.0.0.1:3000/`. `Set-Cookie`, `Cache-Control: private`, `no-store` o `no-cache` y `Vary: *` impiden el cacheo. Corrige el backend si esas páginas son públicas; si no puedes, `proxy_ignore_headers Cache-Control Set-Cookie;` fuerza el cacheo, pero úsalo solo en rutas donde tengas claro que la respuesta es igual para todos los usuarios.
**No aparece la cabecera `X-Cache-Status`.** Si otro `add_header` está definido en un nivel más interno (por ejemplo en `location`), Nginx ignora los `add_header` de niveles superiores. Repite la línea en ese bloque.
**Error `mkdir() "/var/cache/nginx/app" failed`.** El directorio padre no existe o no es accesible. Créalo con `sudo mkdir -p /var/cache/nginx` y vuelve a ejecutar `sudo nginx -t`.
**Se muestra contenido de un usuario a otro.** Detén el problema vaciando la caché (paso 7) y revisa las condiciones del paso 4: la cookie de sesión debe coincidir exactamente con el nombre que usa tu aplicación.
## Conclusión
Ahora Nginx cachea las respuestas públicas de tu backend, excluye a los usuarios autenticados, sigue sirviendo contenido cuando la aplicación falla y te permite medir y refrescar la caché. Como siguientes pasos puedes hacer que la aplicación envíe `Cache-Control: public, max-age=...` en cada ruta para controlar la duración desde el código, añadir un segundo servidor al bloque `upstream` para balancear carga o combinar esta caché con una CDN delante para los recursos estáticos.