Cachear Respuestas HTTP con Nginx Reverse Proxy
Configura Nginx como reverse proxy con cache para almacenar respuestas HTTP upstream con zonas TTL, claves de cache y purge condicional.
Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.
Descripcion general
Nginx puede cachear respuestas de servidores upstream, reduciendo la carga en aplicaciones backend y cortando la latencia para peticiones repetidas. La directiva proxy_cache almacena respuestas en una zona de cache configurable en disco, con TTLs por codigo de respuesta, claves de cache basadas en atributos de la peticion, y opciones para bypass de cache para peticiones especificas. A continuacion: configurar un reverse proxy con cache, ajustar zonas de cache, manejar invalidacion de cache y caching condicional.
Cuando Usar Esto
-
For alternatives, see Complete Guide to CDN Caching Strategy.
-
Endpoints de API con respuestas GET cacheables (catalogos de productos, resultados de busqueda, contenido estatico)
-
Reducir la carga en servidores de aplicacion backend durante picos de trafico
-
Cachear respuestas de servicios upstream lentos
-
Agregar una capa de caching sin modificar codigo de aplicacion
Prerrequisitos
- Nginx 1.20+
- Un servidor de aplicacion backend ejecutandose en localhost o red interna
- Espacio suficiente en disco para la zona de cache
Solucion
1. Definir una Zona de Cache
En el bloque http de nginx.conf:
http {
# Definir zona de cache: path, levels (jerarquia de directorios), size, max_size, inactive
proxy_cache_path /var/cache/nginx/api
levels=1:2
keys_zone=api_cache:10m
max_size=1g
inactive=60m
use_temp_path=off;
# Otra zona para assets estaticos
proxy_cache_path /var/cache/nginx/assets
levels=1:2
keys_zone=asset_cache:50m
max_size=5g
inactive=24h
use_temp_path=off;
}
levels=1:2: Jerarquia de directorios de dos niveles para archivos de cache (evita demasiados archivos en un directorio)keys_zone=api_cache:10m: 10MB de memoria compartida para claves (aproximadamente 80,000 claves)max_size=1g: Tamano maximo de cache en discoinactive=60m: Remover archivos no accedidos en 60 minutos (incluso si el TTL no ha expirado)
2. Habilitar Caching en un Server Block
server {
listen 80;
server_name api.example.com;
location /api/ {
proxy_pass http://backend:3000;
proxy_cache api_cache;
# Clave de cache: metodo + host + URI + query string
proxy_cache_key "$request_method$request_uri";
# TTL por codigo de respuesta
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_valid 500 10s; # Cachear errores brevemente para proteger backend
# Agregar headers mostrando estado de cache
add_header X-Cache-Status $upstream_cache_status;
# No cachear respuestas con estos headers
proxy_no_cache $http_authorization;
proxy_cache_bypass $http_authorization;
}
location /assets/ {
proxy_pass http://backend:3000;
proxy_cache asset_cache;
proxy_cache_key "$request_uri";
proxy_cache_valid 200 24h;
add_header X-Cache-Status $upstream_cache_status;
}
}
3. Caching Condicional
Saltar cache para peticiones autenticadas, POST, o query params especificos:
server {
listen 80;
server_name api.example.com;
# Saltar cache para POST, PUT, DELETE
set $skip_cache 0;
if ($request_method != GET) {
set $skip_cache 1;
}
# Saltar cache para peticiones autenticadas
if ($http_authorization) {
set $skip_cache 1;
}
# Saltar cache para query params especificos
if ($args ~* "no_cache=1") {
set $skip_cache 1;
}
location /api/ {
proxy_pass http://backend:3000;
proxy_cache api_cache;
proxy_cache_key "$request_method$request_uri";
proxy_cache_valid 200 10m;
proxy_no_cache $skip_cache;
proxy_cache_bypass $skip_cache;
add_header X-Cache-Status $upstream_cache_status;
}
}
4. Cache Locking (Prevenir Cache Stampede)
Cuando multiples peticiones llegan para la misma clave no cacheada, Nginx puede bloquear para que solo una peticion llegue al backend:
location /api/ {
proxy_pass http://backend:3000;
proxy_cache api_cache;
proxy_cache_key "$request_method$request_uri";
proxy_cache_valid 200 10m;
# Solo una peticion popula el cache
proxy_cache_lock on;
proxy_cache_lock_timeout 5s; # Esperar hasta 5s para llenar cache
proxy_cache_lock_age 30s; # Liberar lock despues de 30s
# Servir contenido stale mientras refresca
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_revalidate on;
add_header X-Cache-Status $upstream_cache_status;
}
5. Cache Purge (con modulo nginx-cache-purge)
location ~ /purge(/.*) {
allow 127.0.0.1;
allow 10.0.0.0/8;
deny all;
proxy_cache_purge api_cache "$request_method$1$is_args$args";
}
Purgar una URL especifica:
curl -X GET http://api.example.com/purge/api/products?page=1
# Retorna: 200 OK - Purge successful
6. Headers de Cache Control del Upstream
Nginx respeta los headers Cache-Control del backend por defecto. Sobrescribirlos:
location /api/ {
proxy_pass http://backend:3000;
proxy_cache api_cache;
proxy_cache_key "$request_uri";
# Ignorar headers Cache-Control del backend
proxy_ignore_headers Cache-Control Expires Set-Cookie;
# Forzar caching independientemente de los headers del backend
proxy_cache_valid 200 10m;
# Ocultar headers del backend al cliente
proxy_hide_header Set-Cookie;
proxy_hide_header Cache-Control;
}
Como Funciona
- Clave de cache: Nginx hashea el valor de
proxy_cache_key(ej.,$request_method$request_uri) y lo usa como identificador de archivo de cache. Claves identicas hit el mismo cache entry. - Zona de cache: El parametro
keys_zoneasigna memoria compartida para la tabla de lookup de claves. Las respuestas cacheadas reales se almacenan en disco bajo el directorioproxy_cache_path. - Resolucion de TTL: Nginx verifica
proxy_cache_validpara el codigo de respuesta. Si el backend enviaCache-Control: max-age, Nginx usa eso a menos queproxy_ignore_headerslo sobrescriba. $upstream_cache_status: Valores posibles:HIT,MISS,BYPASS,EXPIRED,STALE,UPDATING,REVALIDATED,NONE.- Cache lock: Con
proxy_cache_lock on, solo la primera peticion para una clave no cacheada llega al backend. Otras esperan hastaproxy_cache_lock_timeouty luego reciben la respuesta cacheada.
Variantes
Cache con Header Vary
Cachear diferentes versiones de la misma URL basado en el header Accept:
proxy_cache_key "$request_method$request_uri$http_accept";
Cache con Conciencia de Cookies
# Cachear diferentemente basado en cookie de rol de usuario
proxy_cache_key "$request_method$request_uri$cookie_user_role";
Microcaching (Cache de 1 Segundo)
Cachear por un TTL muy corto para absorber picos de trafico:
location /api/ {
proxy_pass http://backend:3000;
proxy_cache api_cache;
proxy_cache_key "$request_method$request_uri";
proxy_cache_valid 200 1s; # TTL muy corto
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
}
Cache Warming
Pre-poblar el cache obteniendo URLs despues de un deploy:
#!/bin/bash
# warm-cache.sh
URLS=(
"https://api.example.com/api/products?page=1"
"https://api.example.com/api/products?page=2"
"https://api.example.com/api/categories"
)
for url in "${URLS[@]}"; do
status=$(curl -s -o /dev/null -w "%{http_code}" "$url")
echo "$url -> $status"
done
Mejores Practicas
- Dimensionar
keys_zonecorrectamente: 1MB de memoria compartida contiene aproximadamente 8,000 claves de cache. Para 100K items cacheados, asigna 13MB. - Usar
proxy_cache_lock: Previene cache stampede cuando muchas peticiones hit la misma clave no cacheada simultaneamente. - Habilitar
proxy_cache_use_stale: Sirve contenido stale durante fallos del backend, mejorando resiliencia. - Monitorear hit rate del cache: Verifica headers
X-Cache-Statuso parsea access logs de Nginx. - Establecer
inactivemenor quemax_age: Remueve archivos accedidos infrecuentemente incluso si su TTL no ha expirado, manteniendo el uso de disco manejable. - No cachear respuestas autenticadas: Usa
proxy_cache_bypasscon$http_authorizationpara saltar cache para usuarios logueados.
Errores Comunes
- Cachear POST/PUT/DELETE: Solo GET y HEAD deberian cachearse. Usa
if ($request_method != GET)para saltar otros. - Olvidar
proxy_cache_key: Sin una clave explicita, Nginx usa un default que puede no incluir query strings, causando cache hits incorrectos. - No manejar Set-Cookie: Si el backend setea cookies, cachear la respuesta filtra la sesion de un usuario a otro. Usa
proxy_hide_header Set-Cookie. - Zona de cache demasiado pequena: Si
keys_zonees undersized, Nginx evicta entradas prematuramente. Monitorea connginx -Vy estadisticas de cache. - Sin
proxy_cache_lock: Sin locking, un cache miss dispara N peticiones concurrentes al backend — el cache stampede.
FAQ
Cuanto espacio en disco usa el cache de Nginx?
Hasta max_size por zona de cache. Nginx usa un proceso cache manager en background para remover archivos least recently used cuando el cache excede max_size.
Puedo cachear respuestas HTTPS?
Si. Nginx termina TLS y hace proxy al backend sobre HTTP. El cache funciona sobre la respuesta desencriptada — TLS es transparente para la capa de cache.
Como purgo todo el cache?
Elimina el contenido del directorio de cache: rm -rf /var/cache/nginx/api/*. Nginx reconstruira el cache en peticiones subsecuentes. Para purges dirigidos, usa la directiva proxy_cache_purge.
Nginx cachea compresion (gzip)?
Nginx cachea la respuesta no comprimida del backend y la comprime por peticion con gzip on. Para cachear respuestas comprimidas, habilita gzip_proxied y configura el backend para enviar contenido pre-comprimido.
Que es microcaching?
Una estrategia de caching con un TTL muy corto (1-5 segundos). Absorbe picos de trafico cacheando respuestas brevemente, reduciendo la carga del backend durante picos sin servir datos stale por mucho tiempo.
¿Esta solución está lista para producción?
Sí. Los ejemplos de código arriba muestran implementaciones probadas. Adapta el manejo de errores y la configuración a tu entorno específico antes de desplegar.
¿Cuáles son las características de rendimiento?
El rendimiento depende de tu volumen de datos e infraestructura. Las soluciones mostradas priorizan claridad. Para escenarios de alto throughput, añade caching, batching y connection pooling según sea necesario.
¿Cómo depuro problemas con este enfoque?
Empieza con el ejemplo mínimo de arriba. Añade logging en cada paso. Prueba con entradas pequeñas primero, luego escala. Usa el debugger de tu lenguaje para revisar los edge cases.
¿Cómo cachear solo códigos de respuesta específicos?
Usa proxy_cache_valid con códigos de status explícitos: proxy_cache_valid 200 302 10m; cachea solo respuestas 200 y 302 por 10 minutos. Añade proxy_cache_valid 404 1m; para cachear 404s brevemente. Respuestas con otros códigos (500, 502, 503) no se cachean a menos que los listes explícitamente.
¿Qué es la variable upstream_cache_status?
$upstream_cache_status te dice si la respuesta vino del caché o del origin: HIT (servida desde caché), MISS (no en caché, obtenida del origin), EXPIRED (entrada expirada, re-obtenida), BYPASS (caché saltado), REVALIDATED (stale pero revalidada). Añádelo como header de respuesta con add_header X-Cache-Status $upstream_cache_status; para debuggear el comportamiento del caché.
Errores Comunes
- No setear
proxy_cache_key— la clave por defecto puede no incluir todas las variantes del request (método, headers) - Cachear respuestas con headers
Set-Cookie— filtra datos de sesión entre usuarios - Olvidar
proxy_cache_bypasspara requests autenticados — cachea contenido personalizado - No monitorear el cache hit ratio — un ratio bajo significa que la config de caché necesita ajustes
- Cachear requests POST por defecto — los bodies de POST no son parte de la cache key y nunca deberían cachearse
- No setear
proxy_cache_lock on— las thundering herd requests abruman el origin cuando las entradas de caché expiran simultáneamente - No definir un
proxy_cache_pathconmax_sizeadecuado — el caché evicta entradas prematuramente si el espacio en disco es muy pequeño
Recursos Relacionados
Cache Function Results with Redis and TTL in Python
Build a Python decorator that caches function return values in Redis with configurable TTL, key generation, and cache invalidation
RecipeCDN Cache Invalidation Strategies and Patterns
Implement CDN cache invalidation using purge APIs, surrogate keys, tag-based invalidation, and versioned URLs to keep content fresh
GuideComplete Guide to API Versioning Strategies
Version REST and GraphQL APIs with URI, header, query param, and content negotiation strategies. Covers deprecation, sunset, and migration patterns.
RecipeImplement Redis Cache Invalidation in Node.js
Invalidate Redis cache entries in Node.js with TTL expiry, explicit deletion, pattern-based clearing, and pub/sub-based distributed invalidation.
RecipeCache HTTP Responses with httpx and CacheControl in Python
Cache HTTP responses in Python using httpx with CacheControl for HTTP-compliant caching, ETag handling, and conditional requests.