Fundamentos de la Reescritura de URLs en Nginx
La directiva rewrite en Nginx permite interceptar solicitudes HTTP entrantes y redirigirlas o reescribirlas hacia nuevas rutas internas o externas. Esste proceso es esencial para la gestión de tráfico, la optimización para motores de búsqueda (SEO) y la implementación de URLs amigables.
Principales Casos de Uso
- Redirección de Dominios: Mover tráfico de un dominio antiguo a uno nuevo (ej. de
dominio-antiguo.comanuevo-dominio.com). - Forzar Protocolo Seguro: Redirigir todo el tráfico HTTP no cifrado hacia HTTPS.
- URLs Pseudo-estáticas: Transformar URLs dinámicas con parámetros de consulta en rutas estáticas legibles para mejorar el SEO y la seguridad.
- Mantenimiento: Redirigir temporalmente a los usuarios a una página de mantenimiento durante actualizaciones del sistema.
Sintaxis y Banderas (Flags) de Reescritura
La directiva se aplica dentro de los contextos server, location e if. Su sintaxis básica es:
rewrite regex replacement [flag];
Las banderas (flag) dictan cómo Nginx debe procesar la solicitud después de aplicar la regla:
| Flag | Descripción y Comportamiento |
|---|---|
last |
Detiene el procesamiento de las reglas rewrite actuales y reinicia la búsqueda de coincidencias en el bloque server con la nueva URI. |
break |
Detiene el procesamiento de las reglas rewrite y continúa buscando coincidencias en el bloque location actual con la nueva URI. |
redirect |
Devuelve un código de estado HTTP 302 (Redirección temporal). El navegador del usuario realiza una nueva solicitud a la URL proporcionada. |
permanent |
Devuelve un código de estado HTTP 301 (Redirección permanente). El navegador cachea esta redirección para futuras solicitudes. |
Comparación Técnica: last vs break
La diferencia prnicipal radica en el alcance de la reevaluación de la URI.
server {
listen 80;
server_name api.ejemplo.local;
root /var/www/miapp;
location ~ ^/ruta-break {
# Se detiene y busca /destino/ dentro del root actual (/var/www/miapp/destino/)
rewrite ^/ruta-break /destino/ break;
}
location ~ ^/ruta-last {
# Reinicia el proceso en el bloque server, buscando un location que coincida con /destino/
rewrite ^/ruta-last /destino/ last;
}
location /destino/ {
default_type application/json;
return 200 '{"status": "exito"}';
}
}
Si la carpeta física /destino/ no existe, break devolverá un error 404 inmediatamente. Por el contrario, last reevaluará la URI /destino/ contra las directivas location, encontrando el bloque que devuelve el JSON.
Comparación Técnica: redirect vs permanent
server {
listen 80;
server_name api.ejemplo.local;
location /temporal {
# El navegador volverá a preguntar al servidor en la próxima visita
rewrite ^(.*)$ https://www.google.com redirect;
}
location /definitivo {
# El navegador guardará en caché la redirección y no preguntará al servidor
rewrite ^(.*)$ https://www.google.com permanent;
}
}
Activación de Logs para Depuración
Para depurar reglas complejas, es crucial habilitar el registro de reescritura en el contexto http de nginx.conf:
error_log /var/log/nginx/error.log notice;
http {
rewrite_log on;
# ... otras directivas ...
}
Escenarios Prácticos de Reescritura
Caso 1: Reestructuración de Directorios
Redirigir solicitudes de una ruta antigua a una nueva estructura de carpetas.
# http://api.ejemplo.local/blog/2023/post.html -> /articulos/2023/post.html
server {
listen 80;
server_name api.ejemplo.local;
root /var/www/miapp;
location /blog {
rewrite ^/blog/(.*)$ /articulos/$1 last;
}
}
Caso 2: URLs Pseudo-estáticas con Parámetros
Transformar una URL con guiones en una ruta de directorios profunda para el backend.
# http://api.ejemplo.local/curso-11-22-33.html -> /cursos/11/22/33/curso_33.html
server {
listen 80;
server_name api.ejemplo.local;
root /var/www/miapp;
location / {
rewrite ^/curso-(\d+)-(\d+)-(\d+)\.html$ /cursos/$1/$2/$3/curso_$3.html last;
}
}
Caso 3: Forzar Redirección HTTP a HTTPS
La forma más eficiente de asegurar todo el tráfico del dominio.
server {
listen 80;
server_name secure.ejemplo.local;
# Uso de return en lugar de rewrite para mayor rendimiento
return 301 https://$host$request_uri;
}
Implementación de URLs Amigables en CMS (WordPress)
Los sistemas de gestión de contenidos requieren que Nginx maneje las rutas internas. La directiva try_files es el estándar para esto, verificando la existencia de archivos y delegando al controlador frontal si no se encuentran.
server {
listen 80;
server_name blog.ejemplo.local;
root /var/www/wordpress;
index index.php index.html;
client_max_body_size 64M;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires max;
log_not_found off;
}
}
Referencia Rápida: Variables y Expresiones Regulares
Operadores de Coincidencia
~: Coincidencia sensible a mayúsculas.~*: Coincidencia insensible a mayúsculas.!~y!~*: Negación de las coincidencias anteriores.
Validaciones de Sistema de Archivos
-fy!-f: Verifica si existe (o no) un archivo regular.-dy!-d: Verifica si existe (o no) un directorio.-ey!-e: Verifica si existe un archivo o directorio.-xy!-x: Verifica si un archivo tiene permisos de ejecución.
Variables Internas Clave
$uri: La URI actual (puede cambiar durante el procesamiento interno).$request_uri: La URI original completa, incluyendo argumentos.$args: Los argumentos de la consulta (query string).$host: El nombre del host de la solicitud.$scheme: El protocolo utilizado (http o https).
Patrones de Configuración Comunes
Protección contra Hotlinking (Robo de Ancho de Banda)
location ~* \.(jpg|jpeg|png|gif|webp)$ {
valid_referers none blocked server_names *.ejemplo.local;
if ($invalid_referer) {
return 403;
# O redirigir a una imagen de aviso: rewrite ^/ /imagenes/aviso-robado.png break;
}
expires 30d;
access_log off;
}
Bloqueo de Archivos Ocultos y Directorios Sensibles
# Bloquear archivos que comienzan con punto (ej. .htaccess, .git)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Bloquear directorios específicos
location ~ ^/(wp-admin|wp-includes|vendor)/ {
deny all;
}
Añadir Barra Invertida Final Automáticamente
if (-d $request_filename) {
rewrite ^/(.*)([^/])$ https://$host/$1$2/ permanent;
}
Prioridad de Ejecución de Reglas
Es fundamental entender el orden en que Nginx procesa las directivas para evitar comportamientos inesperados:
- Se ejecutan las reglas
rewritedefinidas en el bloqueserver. - Se evalúa la coincidencia de
locationbasada en la URI resultante. - Se ejecutan las reglas
rewritedentro del bloquelocationcoincidente. - Se ejecutan las directivas
rewritedentro de bloquesifubicados en ellocation.
Mejores Prácticas y Formas Recomendadas
El uso excesivo de la directiva if dentro de bloques location está desaconsejado en Nginx debido a su comportamiento impredecible en ciertos contextos (conocido como "if is evil").
Enfoque Obsoleto (No recomendado):
server {
listen 80;
server_name ejemplo.local www.ejemplo.local;
# Uso innecesario de if para canonicalización
if ($host = 'ejemplo.local') {
rewrite ^(.*)$ http://www.ejemplo.local$1 permanent;
}
}
Enfoque Moderno y Recomendado:
Separar las configuraciones en bloques server distintos o utilizar la directiva return, que es más rápida y segura que rewrite para redirecciones simples.
# Bloque para el dominio sin www
server {
listen 80;
server_name ejemplo.local;
return 301 http://www.ejemplo.local$request_uri;
}
# Bloque para el dominio canónico
server {
listen 80;
server_name www.ejemplo.local;
# Configuración principal del sitio aquí...
}