Cuando gestionamos un servidor Nginx que actúa como proxy inverso para varios sitios web y backends, es habitual implementar reglas de caché para mejorar el rendimiento. Sin embargo, no todas las peticiones deben ser cacheadas: hay URIs sensibles, peticiones POST
, parámetros de tracking o recursos dinámicos que deben excluirse.
En Nginx, estas condiciones suelen gestionarse con la directiva map
. Es fundamental entender que los map
declarados en la configuración de Nginx tienen un alcance global dentro del bloque http { ... }
. Esto significa que:
- Los
map
se declaran una sola vez a nivel global (http). - Una vez definidos, sus variables destino (ej.
$skip_cache
) están disponibles en todos los servidores virtuales (server {}
). - Si intentamos definir dos
map
con la misma variable destino en diferentes vhosts, obtendremos un error de configuración (duplicate variable name
).
Contexto: Proxy inverso con varios sitios
Imaginemos que tenemos un Nginx con varios sitios configurados en /etc/nginx/sites-enabled/
, por ejemplo:
undominio.com
→ backend en192.168.2.14
7otrodominio.com
→ backend en192.168.2.143
Cada sitio puede tener políticas de caché, pero la lógica de qué debe o no debe cachearse se repite:
- No cachear
POST
- No cachear peticiones con
query string
- No cachear accesos a
/wp-admin/
,xmlrpc.php
, sitemaps, trackers (utm
,gclid
,fbclid
, etc.) - No cachear ciertos scripts de terceros como Google Analytics o reCAPTCHA
En lugar de duplicar esta lógica en cada sitio, lo ideal es centralizar los map
en un único archivo.
Declaración de los map
globales
Creamos el archivo /etc/nginx/conf.d/cache-maps.conf
con el siguiente contenido:
# /etc/nginx/conf.d/cache-maps.conf
# Reglas globales para exclusión de caché
# 1. Detectar si la petición es POST
map $request_method $is_post {
POST 1;
default 0;
}
# 2. Detectar si hay query string
map $query_string $has_query_string {
"" 0;
default 1;
}
# 3. Detectar URIs sensibles (WordPress admin, xmlrpc, sitemaps, trackers…)
map $request_uri $is_sensitive_uri {
~*/wp-admin/|xmlrpc.php|wp-.*\.php|index.php|.*sitemap.*\.(xml|xsl|hmtl)|.*_ga.*|.*utm.*|.*gclid.*|.*fbclid.*|.*gdpr.* 1;
default 0;
}
# 4. Detectar scripts que nunca deben cachearse (reCAPTCHA, GA, GTM…)
map $request_uri $is_excluded_js {
~*^/recaptcha/api\.js(\?.*)?$|^/analytics\.js(\?.*)?$|^/gtag/js$|^/gtm\.js(\?.*)?$|^/tag/js/gpt\.js(\?.*)?$|^/dist/scripts/main\.js(\?.*)?$|^/assets/js/gdpr_cc_addon\.js(\?.*)?$|^/gdpr-cookie-compliance/dist/scripts/main\.js(\?.*)?$ 1;
default 0;
}
# 5. Variable final: si cualquiera de las condiciones anteriores se cumple, saltar caché
map "$is_post$has_query_string$is_sensitive_uri$is_excluded_js" $skip_cache {
~*1 1;
default 0;
}
Este archivo se incluye automáticamente porque la directiva por defecto en /etc/nginx/nginx.conf
suele tener:
http {
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Uso de $skip_cache
en cada vhost
Ahora, dentro de cada sitio en /etc/nginx/sites-enabled/
, podemos usar directamente la variable $skip_cache
sin necesidad de volver a declarar los map
.
Ejemplo para undominio.com:
server {
listen 443 ssl;
http2 on;
server_name undomino.com www.undomino.com;
ssl_certificate /etc/letsencrypt/live/undomino.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/undomino.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
access_log /var/log/nginx/access_undomino.log;
location / {
proxy_pass https://192.168.2.147:443;
include proxy_params;
proxy_cache undomino;
proxy_cache_key "$scheme$host$request_uri";
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
proxy_cache_bypass $skip_cache;
proxy_no_cache $skip_cache;
proxy_cache_valid 200 4h;
proxy_cache_revalidate on;
proxy_cache_background_update on;
proxy_cache_min_uses 1;
add_header X-Skip-Cache $skip_cache;
add_header X-Cache-Status $upstream_cache_status;
}
}
El backend recibirá normalmente todas las peticiones, pero Nginx sólo cacheará aquellas que no cumplan las condiciones de exclusión.
Validación y pruebas
- Validar configuración:
sudo nginx -t
- Recargar servicio:
sudo systemctl reload nginx
- Probar peticiones:
curl -I https://undomino.com/
→ debería devolverX-Skip-Cache: 0
(cacheable).curl -I https://undomino.com/?utm_source=test
→ debería devolverX-Skip-Cache: 1
(no cacheable).curl -I -X POST https://undomino.com/
→ tambiénX-Skip-Cache: 1
.
Conclusiones
- Los
map
en Nginx son globales a todo el bloquehttp
, no a un únicoserver
. - Declararlos dentro de cada vhost provoca conflictos si se repiten variables.
- La práctica recomendada es centralizar la lógica de exclusión de caché en un archivo común (
/etc/nginx/conf.d/cache-maps.conf
). - Una vez definidos, las variables como
$skip_cache
se pueden reutilizar en todos los sitios, simplificando y unificando la gestión de la caché en un entorno multi-sitio.
Compartir: