Varnish Cache es un proxy inverso HTTP que guarda en memoria las respuestas de tu servidor web y las sirve directamente en las siguientes peticiones, sin volver a ejecutar PHP, consultas a la base de datos ni el resto de la aplicación. En este tutorial pondrás Varnish en el puerto 80 de un servidor Ubuntu 24.04, delante de Nginx, que pasará a escuchar solo en local en el puerto 8080. Escribirás una configuración VCL con reglas de TTL, cookies, purga y chequeos de salud, y comprobarás los aciertos de caché con curl, varnishstat y varnishlog.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 1 GB de RAM. Varnish guarda la caché en memoria, así que cuanta más RAM le dediques, más objetos podrá almacenar.
- Un usuario no root con privilegios
sudo. - Nginx instalado y sirviendo tu sitio en el puerto 80. Si aún no lo tienes, instálalo con
sudo apt install nginx. - UFW activo con SSH permitido, si usas firewall.
NotaVarnish no gestiona HTTPS. Esta guía cubre el tráfico HTTP en el puerto 80. Para servir HTTPS, lo habitual es terminar TLS en Nginx (puerto 443) y reenviar a Varnish, que a su vez consulta al backend.
Paso 1: Instalar Varnish
Ubuntu 24.04 incluye Varnish 7.1 en sus repositorios, una versión estable y suficiente para todo lo que verás aquí. Instálalo:
sudo apt update
sudo apt install varnish
Comprueba la versión instalada:
varnishd -V
varnishd (varnish-7.1.1 revision ...)
Copyright (c) 2006 Verdens Gang AS
Copyright (c) 2006-2022 Varnish Software
Tras la instalación Varnish ya está en marcha, escuchando en el puerto 6081 para el tráfico HTTP y en localhost:6082 para la interfaz de administración. Los archivos que vas a tocar son:
/etc/varnish/default.vcl: la configuración VCL, que define el backend y las reglas de caché.- La unidad systemd
varnish.service: los parámetros de arranque (puerto de escucha y tamaño de la caché).
Paso 2: Mover Nginx al puerto 8080
Varnish tiene que recibir las peticiones en el puerto 80, así que antes hay que liberar ese puerto. Nginx pasará a escuchar en 127.0.0.1:8080, de modo que solo Varnish podrá acceder a él.
Abre el sitio por defecto de Nginx (o el archivo de tu sitio en /etc/nginx/sites-available/):
sudo nano /etc/nginx/sites-available/default
Busca las líneas listen del bloque server:
listen 80 default_server;
listen [::]:80 default_server;
Y sustitúyelas por:
listen 127.0.0.1:8080 default_server;
Repite el cambio en cada archivo de sitio habilitado que escuche en el puerto 80. Después comprueba la sintaxis y reinicia Nginx:
sudo nginx -t
sudo systemctl restart nginx
Verifica que Nginx responde en el nuevo puerto:
curl -I http://127.0.0.1:8080
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Content-Type: text/html
A partir de aquí, y hasta que termines el paso 4, el sitio no responderá en el puerto 80.
Paso 3: Escribir la configuración VCL
VCL (Varnish Configuration Language) define a qué backend se reenvían las peticiones y qué se guarda en caché. Varnish incluye una VCL integrada (builtin.vcl) que se ejecuta después de tus subrutinas si estas no terminan con return. Esa lógica ya hace lo correcto por defecto: no cachea peticiones POST, ni peticiones con cabecera Authorization o Cookie, ni respuestas con Set-Cookie. Por eso la mejor VCL suele ser corta y retocar solo lo necesario.
Abre el archivo:
sudo nano /etc/varnish/default.vcl
Sustituye su contenido por lo siguiente:
vcl 4.1;
backend default {
.host = "127.0.0.1";
.port = "8080";
.connect_timeout = 5s;
.first_byte_timeout = 60s;
.between_bytes_timeout = 10s;
.probe = {
.url = "/";
.timeout = 2s;
.interval = 5s;
.window = 5;
.threshold = 3;
}
}
acl purge {
"localhost";
"127.0.0.1";
"::1";
}
sub vcl_recv {
# Permitir purgar una URL solo desde el propio servidor
if (req.method == "PURGE") {
if (!client.ip ~ purge) {
return (synth(405, "Not allowed"));
}
return (purge);
}
# Nunca cachear zonas privadas
if (req.url ~ "^/(admin|wp-admin|wp-login\.php|login|cart|checkout)") {
return (pass);
}
# Los estáticos no necesitan cookies
if (req.url ~ "\.(css|js|png|jpe?g|gif|svg|ico|webp|woff2?)(\?.*)?$") {
unset req.http.Cookie;
}
# Quitar cookies de analítica, que no cambian el contenido
if (req.http.Cookie) {
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_ga[^=]*|_gid|_gat|_fbp)=[^;]*", "");
set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");
if (req.http.Cookie ~ "^\s*$") {
unset req.http.Cookie;
}
}
# Sin return: se ejecuta la lógica de builtin.vcl
}
sub vcl_backend_response {
# Los estáticos se guardan un día y sin cookies
if (bereq.url ~ "\.(css|js|png|jpe?g|gif|svg|ico|webp|woff2?)(\?.*)?$") {
unset beresp.http.Set-Cookie;
set beresp.ttl = 1d;
}
# Si el backend cae, servir contenido caducado hasta 6 horas
set beresp.grace = 6h;
}
sub vcl_deliver {
# Cabecera para comprobar si la respuesta vino de la caché
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
}
Qué hace cada bloque:
backend defaultapunta a Nginx en127.0.0.1:8080. Laprobepide/cada 5 segundos y marca el backend como sano si al menos 3 de las últimas 5 respuestas son correctas. Si tu sitio tiene una URL de salud más ligera, úsala en.url.acl purgey el bloquePURGEpermiten invalidar una URL, pero solo desde el propio servidor.vcl_recvexcluye las zonas privadas de la caché y limpia cookies que impedirían cachear. Como no termina conreturn, después se aplicabuiltin.vcl, que sigue sin cachear peticiones con cookies de sesión.vcl_backend_responseasigna un TTL de un día a los estáticos. Para el resto, Varnish respeta las cabecerasCache-ControlyExpiresdel backend y, si no hay, usa un TTL por defecto de 120 segundos.beresp.gracepermite servir contenido caducado mientras se obtiene una copia nueva, o mientras el backend no responde.
Comprueba que la VCL compila antes de cargarla. Si hay un error, varnishd indica la línea exacta:
sudo varnishd -C -f /etc/varnish/default.vcl > /dev/null && echo "VCL OK"
VCL OK
Paso 4: Poner Varnish en el puerto 80
El puerto de escucha y el tamaño de la caché se pasan como argumentos a varnishd en la unidad systemd. En Ubuntu 24.04 el archivo /etc/default/varnish no se usa, así que el cambio se hace con un override de systemd. Primero mira la línea ExecStart actual:
systemctl cat varnish | grep -A3 ExecStart
ExecStart=/usr/sbin/varnishd \
-j unix,user=vcache \
-F \
-a :6081 \
Crea un override con el editor de systemd:
sudo systemctl edit varnish
Escribe lo siguiente entre los comentarios que indica el editor. La primera línea ExecStart= vacía borra la original; la segunda es la nueva. Copia los argumentos que te mostró el comando anterior y cambia solo -a (puerto) y -s (tamaño de la caché):
[Service]
ExecStart=
ExecStart=/usr/sbin/varnishd \
-j unix,user=vcache \
-F \
-a :80 \
-T localhost:6082 \
-f /etc/varnish/default.vcl \
-S /etc/varnish/secret \
-s malloc,512m
-s malloc,512m reserva 512 MB de RAM para la caché. Ajústalo a tu servidor dejando memoria libre para Nginx, la aplicación y el sistema; Varnish usa algo más de memoria que la indicada por la gestión interna de cada objeto.
Guarda el archivo y reinicia Varnish:
sudo systemctl restart varnish
Comprueba que varnishd escucha en el puerto 80 y Nginx en el 8080:
sudo ss -tlnp | grep -E ':(80|8080)\s'
LISTEN 0 1024 0.0.0.0:80 0.0.0.0:* users:(("varnishd",pid=4321,fd=3))
LISTEN 0 511 127.0.0.1:8080 0.0.0.0:* users:(("nginx",pid=1234,fd=5))
LISTEN 0 1024 [::]:80 [::]:* users:(("varnishd",pid=4321,fd=4))
Si usas UFW, permite el tráfico HTTP. No abras el 8080: Nginx ya solo escucha en local.
sudo ufw allow 80/tcp
Paso 5: Comprobar que la caché funciona
Haz dos peticiones seguidas a la página principal y fíjate en la cabecera X-Cache:
curl -sI http://localhost/ | grep -iE '^(x-cache|age)'
curl -sI http://localhost/ | grep -iE '^(x-cache|age)'
X-Cache: MISS
Age: 0
X-Cache: HIT
Age: 3
La primera petición va al backend (MISS) y la segunda se sirve desde memoria (HIT). La cabecera Age indica cuántos segundos lleva el objeto en caché.
Comprueba también que Varnish ve el backend como sano:
sudo varnishadm backend.list
Backend name Admin Probe Health Last change
boot.default probe 5/5 healthy Thu, 25 Sep 2026 10:20:11 GMT
Paso 6: Purgar contenido de la caché
Cuando publicas un cambio, no hace falta esperar a que caduque el TTL. Con la regla PURGE del paso 3 puedes invalidar una URL concreta desde el propio servidor:
curl -X PURGE http://localhost/
<!DOCTYPE html>
<html>
<head>
<title>200 Purged</title>
La siguiente petición a / volverá a ser un MISS. Para invalidar muchas URLs a la vez según un patrón, usa un ban desde varnishadm. Este ejemplo invalida todo lo que empiece por /blog/:
sudo varnishadm "ban req.url ~ ^/blog/"
Y para vaciar la caché por completo:
sudo varnishadm "ban req.url ~ ."
Otra opción es reiniciar Varnish, que también vacía la caché porque vive en memoria.
Paso 7: Monitorizar la caché
varnishstat muestra los contadores internos. Para ver de un vistazo los aciertos, fallos y objetos almacenados:
varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss -f MAIN.n_object
MAIN.cache_hit 1520 0.52 Cache hits
MAIN.cache_miss 312 0.11 Cache misses
MAIN.n_object 287 . object structs made
La tasa de aciertos es cache_hit / (cache_hit + cache_miss), en este ejemplo un 83 %. Sin argumentos, varnishstat abre una vista interactiva que se actualiza en tiempo real.
varnishlog muestra cada petición en detalle. Es la herramienta para averiguar por qué algo no se cachea. Por ejemplo, para seguir las peticiones a la portada agrupadas por petición:
sudo varnishlog -g request -q 'ReqURL eq "/"'
En la salida, las líneas VCL_call y VCL_return indican por qué subrutinas pasó la petición (HIT, MISS o PASS), y las cabeceras ReqHeader y BerespHeader muestran si había cookies o un Cache-Control que impidió cachear.
Solución de problemas
Todas las respuestas son MISS: lo más habitual es que el navegador o la aplicación envíen cookies de sesión, o que el backend responda con Set-Cookie o Cache-Control: private o no-cache. Revisa las cabeceras con sudo varnishlog -g request -q 'ReqURL eq "/"'. Si la aplicación usa cookies de sesión en todas las páginas, tendrás que decidir qué cookies ignorar en vcl_recv.
Error 503 Backend fetch failed: Varnish no puede hablar con Nginx. Comprueba que Nginx responde con curl -I http://127.0.0.1:8080, que varnishadm backend.list muestra el backend como healthy y revisa los logs con sudo journalctl -u varnish -n 50. Si la probe falla porque / devuelve un error o una redirección, cambia .url por una ruta que devuelva 200.
Varnish no arranca tras el override: normalmente es un error en la VCL o un argumento mal copiado. Ejecuta sudo journalctl -u varnish -n 30 y valida la VCL con sudo varnishd -C -f /etc/varnish/default.vcl.
Los logs de Nginx muestran siempre 127.0.0.1 como cliente: es normal, porque ahora todas las peticiones llegan desde Varnish. Varnish añade la IP real en la cabecera X-Forwarded-For; configura el módulo real_ip de Nginx con set_real_ip_from 127.0.0.1; y real_ip_header X-Forwarded-For; para recuperarla.
Conclusión
Has instalado Varnish Cache en Ubuntu 24.04 delante de Nginx, has escrito una VCL que cachea los estáticos, respeta las zonas privadas, sirve contenido caducado si el backend cae y permite purgas locales, y has comprobado los aciertos con curl, varnishstat y varnishlog.
Como siguientes pasos puedes:
- Terminar HTTPS en Nginx en el puerto 443 y reenviar el tráfico a Varnish.
- Ajustar las reglas de cookies y TTL a tu aplicación (por ejemplo WordPress o una API) observando
varnishlog. - Enviar
varnishncsaa tus logs de acceso para tener registros en formato Apache de las peticiones servidas por Varnish.
