Configuración Avanzada de Reescritura de URLs en Nginx

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.com a nuevo-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

  • -f y !-f : Verifica si existe (o no) un archivo regular.
  • -d y !-d : Verifica si existe (o no) un directorio.
  • -e y !-e : Verifica si existe un archivo o directorio.
  • -x y !-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:

  1. Se ejecutan las reglas rewrite definidas en el bloque server.
  2. Se evalúa la coincidencia de location basada en la URI resultante.
  3. Se ejecutan las reglas rewrite dentro del bloque location coincidente.
  4. Se ejecutan las directivas rewrite dentro de bloques if ubicados en el location.

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í...
}

Etiquetas: Nginx url-rewriting web-server reverse-proxy seo-optimization

Publicado el 7-10 23:08