Cómo Nginx decide qué location usar
Un bloque location define cómo Nginx maneja solicitudes que coinciden con una ruta (URI) como /, /api o /assets/logo.png. Cuando llega una petición, Nginx evalúa los location del server y elige uno según reglas de coincidencia (exacta, prefijo o expresión regular). Entender este “ruteo” es clave para servir contenido, proteger rutas y aplicar redirecciones sin sorpresas.
Tipos de coincidencia: exacta, prefijo y regex
Exact match (
location = /ruta): coincide solo con esa ruta exacta. Útil para casos especiales como/o/health.Prefijo (
location /ruta): coincide si el URI empieza con ese prefijo. Entre varios prefijos, gana el más largo (más específico). Ej.:/assets/es más específico que/.Prefijo “final” (
location ^~ /ruta): coincide por prefijo y además evita que Nginx pruebe regex si este prefijo fue el mejor. Útil cuando quieres que una ruta como/assets/nunca sea capturada por una regex genérica.Regex (
location ~ patronolocation ~* patron): coincide por expresión regular.~es sensible a mayúsculas/minúsculas;~*no. Se evalúan en el orden en que aparecen en el archivo.Continúa en nuestra aplicación.- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
Orden real de selección (regla práctica)
La selección puede resumirse así:
Si existe un
location =que coincide exactamente, se usa ese y termina.Se buscan coincidencias por prefijo (
location /algo). Se elige el prefijo más largo (más específico).Si el prefijo ganador tiene
^~, se usa ese y termina (no se prueban regex).Si no, Nginx evalúa los
location ~/~*en orden de aparición. Si alguno coincide, se usa el primero que coincida.Si ninguna regex coincide, se usa el prefijo ganador.
Consecuencia importante: una regex colocada arriba puede “robar” tráfico a un prefijo, a menos que el prefijo use ^~ o exista un = exacto.
Ejemplo rápido de colisión típica
location /assets/ { expires 30d; } # prefijo específico (gana sobre /) location ~* \.(png|jpg|css|js)$ { add_header Cache-Control "public"; }Si pides /assets/app.js, el prefijo ganador es /assets/, pero como no tiene ^~, Nginx aún probará regex; si la regex coincide, se usará la regex (por aparecer y coincidir). Si quieres asegurar que /assets/ mande siempre:
location ^~ /assets/ { expires 30d; }Patrones comunes con location
1) Servir rutas específicas con comportamientos distintos
Separar rutas por responsabilidad reduce errores: /assets para estáticos con caché, /api para proxy a un backend, /admin para acceso restringido.
2) Restringir acceso a directorios o rutas
Con allow/deny puedes limitar por IP. También es común bloquear archivos sensibles por patrón (por ejemplo, .env o backups) usando regex.
3) Redirecciones controladas
Las redirecciones se suelen implementar con return (simple y predecible) o rewrite (más flexible). Para ruteo básico, return 301/302 suele ser suficiente.
Práctica guiada: /assets, /api y /admin con comportamientos distintos
Objetivo: configurar tres rutas con reglas claras y luego verificar qué location se está usando observando logs.
Paso 1: preparar un server de laboratorio
En el bloque server de tu sitio (por ejemplo, en un archivo dentro de sites-enabled), añade o ajusta estas directivas base:
server { listen 80; server_name ejemplo.local; # Logs para observar ruteo access_log /var/log/nginx/locations_access.log; error_log /var/log/nginx/locations_error.log notice; # Raíz general (si ya existe en tu sitio, mantenla) root /var/www/ejemplo; # Location por defecto location / { try_files $uri $uri/ =404; } }Nota: si tu sitio ya tiene root y location /, integra los fragmentos sin duplicar directivas incompatibles.
Paso 2: crear la ruta /assets (estáticos + caché)
Queremos que todo lo que empiece por /assets/ se sirva como archivo estático y tenga cabeceras de caché. Además, queremos evitar que una regex genérica capture estos archivos, así que usaremos ^~.
location ^~ /assets/ { # Opcional: si assets vive en otro directorio, usa alias # alias /var/www/ejemplo_assets/; expires 30d; add_header Cache-Control "public, max-age=2592000"; try_files $uri =404; }Si usas alias, recuerda que cambia cómo se resuelve la ruta en disco (no concatena con root). Si no estás seguro, empieza sin alias y coloca tus archivos en /var/www/ejemplo/assets/.
Paso 3: crear la ruta /api (proxy a backend)
Ahora enruta /api/ hacia un servicio backend (por ejemplo, en http://127.0.0.1:3000). Mantendremos el prefijo y pasaremos cabeceras típicas.
location /api/ { proxy_pass http://127.0.0.1:3000; 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; }Detalle importante: con location /api/ y proxy_pass http://127.0.0.1:3000; (sin barra final), Nginx conserva el URI completo. Si quisieras “recortar” el prefijo /api/, la forma de escribir proxy_pass cambia. Mantén esta versión para un comportamiento directo y fácil de comprobar.
Paso 4: crear la ruta /admin (restricción allow/deny)
Restringe el acceso a /admin/ para que solo una IP (o red) pueda entrar. Ejemplo: permitir solo localhost y denegar el resto.
location /admin/ { allow 127.0.0.1; deny all; try_files $uri $uri/ =404; }Si necesitas permitir una red corporativa, podrías usar allow 192.168.1.0/24;. El orden importa: Nginx evalúa allow/deny en el orden en que aparecen y aplica la primera coincidencia.
Paso 5: añadir una redirección controlada con return
Ejemplo: redirigir /admin (sin barra) a /admin/ para evitar duplicidad de rutas.
location = /admin { return 301 /admin/; }Este = exacto evita ambigüedades y hace la redirección predecible.
Paso 6: añadir un patrón de bloqueo por regex (opcional pero común)
Bloquea archivos ocultos o sensibles (por ejemplo, .env, .git). Aquí una regex típica:
location ~ /\. { deny all; }Si tienes /assets/ con ^~, seguirá ganando para rutas bajo /assets/ aunque esta regex coincida; sin ^~, la regex podría imponerse.
Registrar y observar qué location se está usando
Crear un formato de log que revele el ruteo
Nginx no expone directamente “qué bloque location ganó”, pero puedes inferirlo registrando variables y/o añadiendo una cabecera de respuesta por ruta. Dos técnicas prácticas:
Técnica A: cabecera de depuración por location
Añade una cabecera distinta en cada ruta para identificarla al hacer curl -I:
location ^~ /assets/ { add_header X-Debug-Location assets always; expires 30d; try_files $uri =404; } location /api/ { add_header X-Debug-Location api always; proxy_pass http://127.0.0.1:3000; } location /admin/ { add_header X-Debug-Location admin always; allow 127.0.0.1; deny all; try_files $uri $uri/ =404; } location / { add_header X-Debug-Location default always; try_files $uri $uri/ =404; }Luego prueba:
curl -I http://ejemplo.local/assets/app.css curl -I http://ejemplo.local/api/status curl -I http://ejemplo.local/admin/Busca X-Debug-Location en la respuesta.
Técnica B: log_format con variables útiles
Define un formato de log que incluya URI, estado, upstream y tiempos. Esto ayuda especialmente en /api:
log_format route_debug '$remote_addr - $host "$request" ' 'status=$status uri=$uri args="$args" ' 'upstream="$upstream_addr" ustatus=$upstream_status ' 'rt=$request_time urt=$upstream_response_time'; access_log /var/log/nginx/locations_access.log route_debug;Con esto, una petición a /api debería mostrar upstream poblado; una a /assets normalmente tendrá upstream vacío.
Depuración: por qué una location no coincide
1) Confundir URI con ruta en disco
location coincide contra el URI (lo que va después del host), no contra el path real del sistema de archivos. Si tu archivo existe en disco pero el URI no coincide con el prefijo esperado, no entrará al bloque.
2) Falta o sobra la barra final
/admin y /admin/ son URIs distintos. Si solo tienes location /admin/, una petición a /admin no coincide con ese prefijo (porque no termina en /). Solución típica: añadir location = /admin { return 301 /admin/; }.
3) Una regex está ganando inesperadamente
Recuerda: si no usas ^~, tras elegir el mejor prefijo Nginx aún evalúa regex y la primera que coincida gana. Si una regex genérica captura demasiado (por ejemplo, extensiones), puede desplazar a un prefijo. Soluciones:
Usar
^~en prefijos que deben ser prioritarios (como/assets/).Reordenar regex para que la más específica aparezca antes.
Hacer la regex más estricta (anclar con
^y$cuando aplique).
4) El prefijo “más largo” no es el que creías
Si tienes location /api y location /api/, la coincidencia puede variar según el URI. En general, para rutas tipo “directorio”, usa /api/ y decide explícitamente qué hacer con /api (redirección o manejo separado).
5) try_files y respuestas 404 que parecen “mala coincidencia”
A veces sí se entra al location, pero try_files termina en =404. Eso se percibe como “no coincidió”, pero en realidad coincidió y falló la resolución de archivo. Para comprobarlo, usa la cabecera X-Debug-Location o revisa el error_log con nivel notice o info.
6) Usar herramientas de validación y recarga
Antes de probar, valida sintaxis y recarga:
nginx -tLuego recarga el servicio según tu sistema (sin reiniciar si no es necesario). Si tras recargar no ves cambios, confirma que estás editando el archivo correcto incluido por tu configuración.
7) Activar logging más detallado temporalmente
Para investigar coincidencias y reescrituras, puedes subir el nivel del error_log temporalmente:
error_log /var/log/nginx/locations_error.log info;Haz unas pocas pruebas, revisa el log y vuelve a un nivel menos verboso para producción.