--- title: "Caché en el servidor: Nginx, Varnish, Redis o Memcached" description: "Compara la caché de Nginx, Varnish, Redis y Memcached en Ubuntu 24.04 con ejemplos probados, patrones de invalidación, TTL recomendados y cuándo usar cada una." category: "Rendimiento Web" language: es source: https://cubepath.com/es/docs/rendimiento-web/comparacion-de-estrategias-de-cache-del-servidor updated: 2026-09-25 --- # Caché en el servidor: Nginx, Varnish, Redis o Memcached La caché del lado del servidor evita repetir trabajo caro (renderizar una página, consultar la base de datos) en cada petición. Nginx, Varnish, Redis y Memcached resuelven ese problema en capas distintas: las dos primeras guardan **respuestas HTTP completas** delante de la aplicación, y las dos últimas guardan **datos** que la aplicación pide de forma explícita. En esta guía verás qué hace cada una, un ejemplo funcional de cada una en Ubuntu 24.04 y cómo decidir cuál usar. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Una aplicación web escuchando en local para las pruebas de caché HTTP. En los ejemplos se supone que responde en `http://127.0.0.1:3000`. ## Comparación rápida | | Caché de Nginx | Varnish | Redis | Memcached | |---|---|---|---|---| | Qué guarda | Respuestas HTTP | Respuestas HTTP | Datos de la aplicación (claves con tipos) | Datos de la aplicación (clave-valor) | | Dónde vive | Disco (con índice en RAM) | RAM | RAM, persistencia opcional | Solo RAM | | Cambios en la aplicación | Ninguno | Ninguno (reglas en VCL) | Hay que programar la lectura y escritura | Hay que programar la lectura y escritura | | Invalidación | Por TTL; purga con módulo externo o borrando ficheros | TTL, `ban` y `purge` muy flexibles | `DEL`, `EXPIRE`, TTL por clave | `delete`, TTL por clave | | TLS | Sí | No (necesita un terminador delante) | Opcional | No | | Ideal para | Sitios con Nginx ya delante, WordPress, APIs públicas | Mucho tráfico HTML con reglas de caché complejas | Resultados de consultas, sesiones, contadores, colas | Caché de objetos simple y distribuida | Las cachés HTTP son transparentes para la aplicación pero solo sirven para respuestas iguales para todos los usuarios. Las cachés de datos funcionan también en páginas personalizadas, a cambio de tocar el código. ## Caché de Nginx (proxy_cache y fastcgi_cache) Si Nginx ya hace de proxy inverso, su caché es la opción con menos piezas nuevas. `proxy_cache` sirve para backends HTTP (Node.js, Python, Go) y `fastcgi_cache` para PHP-FPM; la configuración es equivalente. Define la zona de caché en el contexto `http`: ```bash sudo nano /etc/nginx/conf.d/cache.conf ``` ```nginx proxy_cache_path /var/cache/nginx/app levels=1:2 keys_zone=app_cache:10m max_size=1g inactive=60m use_temp_path=off; ``` `keys_zone=app_cache:10m` reserva 10 MB de RAM para el índice (unas 80.000 claves), `max_size` limita el disco e `inactive` borra lo que nadie pide en 60 minutos. Nginx crea el directorio por sí mismo. Usa la zona en tu sitio, saltando la caché para peticiones con cookie de sesión: ```bash sudo nano /etc/nginx/sites-available/your_domain ``` ```nginx server { listen 80; server_name your_domain; location / { proxy_cache app_cache; proxy_cache_key "$scheme$host$request_uri"; proxy_cache_valid 200 301 10m; proxy_cache_valid 404 1m; proxy_cache_use_stale error timeout updating http_500 http_502 http_503; proxy_cache_lock on; # No cachear ni servir de caché a usuarios con sesión proxy_cache_bypass $cookie_sessionid; proxy_no_cache $cookie_sessionid; add_header X-Cache-Status $upstream_cache_status always; proxy_set_header Host $host; proxy_pass http://127.0.0.1:3000; } } ``` Cambia `sessionid` por el nombre real de la cookie de sesión de tu aplicación. `proxy_cache_use_stale` sirve la copia antigua si el backend falla, y `proxy_cache_lock` evita que diez peticiones simultáneas a la misma URL lleguen las diez al backend. ```bash sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx ``` Comprueba el comportamiento pidiendo dos veces la misma URL: ```bash curl -s -o /dev/null -D - http://your_domain/ | grep X-Cache-Status curl -s -o /dev/null -D - http://your_domain/ | grep X-Cache-Status ``` ```output X-Cache-Status: MISS X-Cache-Status: HIT ``` > Nota: Nginx respeta las cabeceras `Cache-Control: private`, `no-store` y `Set-Cookie` del backend y no cachea esas respuestas. Si siempre ves `MISS`, revisa qué cabeceras envía tu aplicación. Para medir la tasa de aciertos, añade el estado de la caché al log. En `/etc/nginx/nginx.conf`, dentro de `http`: ```nginx log_format cache '$remote_addr [$time_local] "$request" $status $upstream_cache_status'; ``` Y en el bloque `server`: `access_log /var/log/nginx/your_domain-cache.log cache;`. Tras recargar, cuenta los estados: ```bash awk '{print $NF}' /var/log/nginx/your_domain-cache.log | sort | uniq -c | sort -rn ``` ```output 8421 HIT 612 MISS 233 BYPASS 41 EXPIRED ``` Nginx de código abierto no incluye un comando de purga por URL. Para vaciar toda la caché borra el contenido del directorio: `sudo find /var/cache/nginx/app -type f -delete`. Si necesitas invalidar URLs concretas a menudo, Varnish es mejor opción. ## Varnish Varnish es un proxy de caché HTTP en memoria con un lenguaje de configuración propio (VCL). Encaja cuando tienes mucho tráfico y necesitas reglas finas: limpiar cookies de analítica, TTL distintos por ruta o invalidar grupos de URLs al publicar contenido. No termina TLS, así que en producción se coloca detrás de Nginx o HAProxy. ```bash sudo apt install varnish ``` En Ubuntu 24.04 Varnish escucha en el puerto 6081 y lee `/etc/varnish/default.vcl`. Edita ese fichero: ```bash sudo nano /etc/varnish/default.vcl ``` ```vcl vcl 4.1; backend default { .host = "127.0.0.1"; .port = "3000"; } acl purgers { "127.0.0.1"; } sub vcl_recv { if (req.method == "PURGE") { if (client.ip !~ purgers) { return (synth(405, "Not allowed")); } return (purge); } # Quitar cookies de analítica que no afectan al contenido if (req.http.Cookie) { set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_ga[^=]*|_gid)=[^;]*", ""); set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", ""); if (req.http.Cookie == "") { unset req.http.Cookie; } } } sub vcl_backend_response { # Servir contenido caducado hasta 1 hora si el backend cae set beresp.grace = 1h; } sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; } else { set resp.http.X-Cache = "MISS"; } } ``` Varnish no cachea por defecto peticiones con cookies ni respuestas con `Set-Cookie`, por eso se eliminan las cookies que no importan. El TTL sale de las cabeceras `Cache-Control` del backend, o 120 segundos si no las hay. ```bash sudo systemctl restart varnish curl -s -o /dev/null -D - http://127.0.0.1:6081/ | grep -E 'X-Cache|Age' curl -s -o /dev/null -D - http://127.0.0.1:6081/ | grep -E 'X-Cache|Age' ``` ```output X-Cache: MISS Age: 0 X-Cache: HIT Age: 3 ``` Invalidar es donde Varnish destaca. Una URL concreta con `PURGE` desde el propio servidor, o todas las que cumplan una expresión con `ban`: ```bash curl -X PURGE http://127.0.0.1:6081/blog/mi-articulo sudo varnishadm 'ban req.url ~ ^/blog/' ``` Para ver la tasa de aciertos: ```bash varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss ``` Para cambiar el puerto o el tamaño de memoria (256 MB por defecto) crea un override con `sudo systemctl edit varnish` y redefine `ExecStart`. ## Redis como caché de aplicación Redis guarda datos en memoria con tipos (cadenas, hashes, listas, conjuntos) y TTL por clave. Se usa para cachear resultados de consultas, fragmentos de HTML, sesiones o límites de peticiones. A diferencia de las cachés HTTP, funciona también con contenido personalizado, pero la aplicación decide qué guarda y cuándo lo invalida. ```bash sudo apt install redis-server ``` Configúralo como caché pura: límite de memoria, expulsión de las claves menos usadas y sin volcados a disco. Edita `/etc/redis/redis.conf` y ajusta estas directivas (ya existen comentadas en el fichero): ```bash sudo nano /etc/redis/redis.conf ``` ```ini maxmemory 256mb maxmemory-policy allkeys-lru save "" appendonly no ``` ```bash sudo systemctl restart redis-server redis-cli CONFIG GET maxmemory-policy ``` ```output 1) "maxmemory-policy" 2) "allkeys-lru" ``` Sin `maxmemory`, Redis crece hasta agotar la RAM del servidor. Con `allkeys-lru`, al llegar al límite descarta las claves menos usadas en lugar de rechazar escrituras. El patrón más habitual es **cache-aside**: la aplicación busca en Redis, y si no está, consulta la fuente y guarda el resultado con un TTL. Un ejemplo en Python: ```bash sudo apt install python3-redis nano ~/cache_aside.py ``` ```python import json import time import redis r = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True) def load_product_from_db(product_id): time.sleep(0.5) # simula una consulta lenta return {"id": product_id, "name": "Producto de ejemplo", "price": 19.9} def get_product(product_id, ttl=300): key = f"product:{product_id}" cached = r.get(key) if cached is not None: return json.loads(cached), "HIT" product = load_product_from_db(product_id) r.set(key, json.dumps(product), ex=ttl) return product, "MISS" def update_product(product_id, data): # guardar en la base de datos y después invalidar r.delete(f"product:{product_id}") if __name__ == "__main__": for _ in range(2): start = time.perf_counter() _, status = get_product(42) print(f"{status} en {(time.perf_counter() - start) * 1000:.1f} ms") ``` ```bash python3 ~/cache_aside.py ``` ```output MISS en 502.3 ms HIT en 0.4 ms ``` Comprueba la clave y el tiempo que le queda: ```bash redis-cli TTL product:42 ``` ```output (integer) 297 ``` ## Memcached Memcached es un almacén clave-valor en memoria, multihilo y sin persistencia ni tipos de datos. Hace una sola cosa y consume muy pocos recursos. Tiene sentido si tu framework ya lo soporta (PHP, Django) o necesitas repartir una caché simple entre varios nodos; si no, Redis cubre lo mismo y más. ```bash sudo apt install memcached ``` La configuración está en `/etc/memcached.conf`, una opción por línea. Ajusta la memoria (64 MB por defecto) y comprueba que solo escucha en local: ```bash sudo nano /etc/memcached.conf ``` ```text -m 256 -p 11211 -u memcache -l 127.0.0.1 ``` > Advertencia: nunca expongas Memcached ni Redis a Internet sin autenticación. Memcached expuesto se ha usado en ataques de amplificación DDoS. ```bash sudo systemctl restart memcached printf 'stats\r\nquit\r\n' | nc 127.0.0.1 11211 | grep -E 'limit_maxbytes|get_hits|get_misses' ``` ```output STAT get_hits 0 STAT get_misses 0 STAT limit_maxbytes 268435456 ``` ## Patrones de invalidación La parte difícil de cualquier caché es saber cuándo borrar. Estos son los patrones que funcionan en la práctica: - **Solo TTL.** El dato caduca solo. Es lo más sencillo y válido cuando se tolera ver datos con unos minutos de antigüedad (listados, contadores públicos). - **Borrar al escribir.** Al actualizar un registro, la aplicación borra su clave (`DEL product:42`). La siguiente lectura la regenera. Mantén también un TTL como red de seguridad. - **Claves con versión.** Guarda un número de versión (`INCR catalog:version`) e inclúyelo en las claves (`catalog:v7:page:1`). Al subir la versión, todas las claves antiguas quedan huérfanas y caducan por TTL, sin tener que buscarlas. - **Invalidación por patrón en caché HTTP.** En Varnish, `ban` con una expresión regular sobre la URL al publicar contenido. Evita borrar con comodines en Redis (`KEYS product:*` seguido de `DEL`): `KEYS` bloquea el servidor mientras recorre todas las claves. Si lo necesitas, usa claves con versión o `redis-cli --scan --pattern`. ## TTL orientativos | Tipo de dato | TTL orientativo | |---|---| | Configuración y datos de referencia | 1 a 24 horas | | Páginas públicas y listados | 1 a 10 minutos | | Fichas de producto o artículos | 10 a 60 minutos, con borrado al editar | | Respuestas de APIs externas (divisas, clima) | Lo que indique su límite de uso, por ejemplo 1 hora | | Stock, precios en vivo | 5 a 30 segundos, o no cachear | | Estáticos con hash en el nombre | 1 año, `Cache-Control: immutable` | Un TTL corto con un volumen de tráfico alto sigue ahorrando mucho: 10 segundos de caché en una página que recibe 100 peticiones por segundo evitan 999 de cada 1.000 llamadas al backend. ## Qué opción elegir - **Ya usas Nginx y quieres cachear páginas públicas sin tocar código:** caché de Nginx. - **Mucho tráfico HTML, muchas cookies de terceros o necesitas purgar URLs al publicar:** Varnish detrás de Nginx (Nginx para TLS, Varnish para caché). - **La página es personalizada pero hay consultas caras que se repiten:** Redis con cache-aside. - **Tu framework ya trae soporte de Memcached y solo necesitas clave-valor:** Memcached. En un proyecto nuevo, elige Redis. Lo habitual en producción es combinar una caché HTTP para visitantes anónimos con Redis para los datos de la aplicación. Antes de añadir una capa, mide: si la tasa de aciertos está por debajo del 50 %, revisa las claves y las cabeceras antes de sumar más memoria. ## Conclusión Has visto las cuatro cachés más usadas en Linux, cada una configurada y verificada en Ubuntu 24.04. Como siguientes pasos, define cabeceras `Cache-Control` correctas en tu aplicación para que Nginx o Varnish sepan qué pueden guardar, monitoriza la tasa de aciertos y el uso de memoria de Redis con `redis-cli INFO stats`, y protege Redis con contraseña o ACL si otros servidores van a conectarse.