Nginx es uno de los proxies inversos más ampliamente desplegados en el mundo. Ya sea que estés enrutando tráfico a un backend Node.js, balanceando carga entre múltiples servidores de aplicaciones o terminando SSL frente a un contenedor Docker, Nginx lo maneja todo con un uso mínimo de recursos y un rendimiento excepcional. Esta guía cubre todo, desde directivas básicas de proxy_pass hasta configuraciones avanzadas incluyendo soporte WebSocket, caché y verificaciones de salud.

¿Qué es un proxy inverso?

Un proxy inverso se sitúa entre los clientes (navegadores) y tus servidores backend. En lugar de que los clientes se conecten directamente a tu aplicación, se conectan a Nginx, que reenvía la solicitud al backend apropiado.

Los beneficios de usar un proxy inverso incluyen:

  • Terminación SSL — Manejar HTTPS a nivel del proxy para que las aplicaciones backend usen HTTP simple
  • Balanceo de carga — Distribuir el tráfico entre múltiples instancias backend
  • Caché — Servir contenido solicitado frecuentemente sin consultar al backend
  • Seguridad — Ocultar detalles del servidor backend y filtrar solicitudes maliciosas
  • Compresión — Comprimir respuestas antes de enviarlas a los clientes
  • Registro centralizado — Agregar logs de acceso en un solo lugar

Configuración básica de proxy inverso

La configuración de proxy inverso más simple reenvía todas las solicitudes a una aplicación backend:

server {
    listen 80;
    server_name app.knowledgexchange.xyz;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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;
    }
}

Esto le indica a Nginx que reenvíe todas las solicitudes que lleguen a app.knowledgexchange.xyz a un backend ejecutándose en el puerto 3000.

Importante: Siempre incluye proxy_set_header Host $host; — sin esto, el backend recibe la dirección del upstream en lugar del hostname original, lo que rompe el hosting virtual y muchos frameworks web.

Preservar la información de IP del cliente

Cuando Nginx actúa como proxy de una solicitud, el backend ve la dirección IP de Nginx como el cliente. Para preservar la IP real del cliente, establece estos encabezados:

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;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;

Configurar tu backend para confiar en los encabezados del proxy

Tu aplicación debe estar configurada para confiar en estos encabezados. Por ejemplo, en Express.js:

app.set('trust proxy', '127.0.0.1');

En Django:

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
USE_X_FORWARDED_HOST = True

Advertencia: Solo confía en los encabezados del proxy desde direcciones IP conocidas. Si tu aplicación confía ciegamente en X-Forwarded-For, un atacante puede falsificar su IP estableciendo el encabezado por sí mismo.

Terminación SSL

Maneja HTTPS a nivel de Nginx para que tus aplicaciones backend no necesiten gestionar certificados:

server {
    listen 443 ssl http2;
    server_name app.knowledgexchange.xyz;

    ssl_certificate /etc/letsencrypt/live/app.knowledgexchange.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.knowledgexchange.xyz/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # Encabezado HSTS
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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;
    }
}

# Redirigir HTTP a HTTPS
server {
    listen 80;
    server_name app.knowledgexchange.xyz;
    return 301 https://$host$request_uri;
}

Esta configuración termina SSL en Nginx y reenvía HTTP simple al backend. El backend solo necesita escuchar en 127.0.0.1:3000 — nunca maneja TLS directamente. Para una inmersión más profunda en la configuración SSL, consulta nuestra guía sobre certificados SSL y longitudes de clave.

Proxy WebSocket

Las conexiones WebSocket requieren manejo especial porque se actualizan de HTTP a una conexión bidireccional persistente:

location /ws/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # Prevenir tiempo de espera en conexiones WebSocket inactivas
    proxy_read_timeout 86400s;
    proxy_send_timeout 86400s;
}

Los encabezados críticos son Upgrade y Connection — le indican a Nginx que pase la solicitud de actualización WebSocket al backend. Los tiempos de espera extendidos previenen que Nginx cierre conexiones WebSocket inactivas (el predeterminado es 60 segundos).

Si tu aplicación usa WebSocket y HTTP en la misma ruta, usa un map para establecer condicionalmente el encabezado Connection:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
    }
}

Balanceo de carga

Distribuye el tráfico entre múltiples servidores backend usando el bloque upstream:

upstream backend_pool {
    server 10.0.1.10:3000;
    server 10.0.1.11:3000;
    server 10.0.1.12:3000;
}

server {
    listen 80;
    server_name app.knowledgexchange.xyz;

    location / {
        proxy_pass http://backend_pool;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Algoritmos de balanceo de carga

Nginx soporta varios métodos de balanceo de carga:

Round Robin (predeterminado): Las solicitudes se distribuyen uniformemente entre los servidores en orden.

upstream backend_pool {
    server 10.0.1.10:3000;
    server 10.0.1.11:3000;
    server 10.0.1.12:3000;
}

Least Connections (Menos conexiones): Envía solicitudes al servidor con menos conexiones activas. Mejor para backends con tiempos de respuesta variables.

upstream backend_pool {
    least_conn;
    server 10.0.1.10:3000;
    server 10.0.1.11:3000;
    server 10.0.1.12:3000;
}

IP Hash: Enruta solicitudes de la misma IP del cliente al mismo servidor backend. Útil para persistencia de sesión.

upstream backend_pool {
    ip_hash;
    server 10.0.1.10:3000;
    server 10.0.1.11:3000;
    server 10.0.1.12:3000;
}

Distribución ponderada: Asigna pesos para enviar más tráfico a servidores más potentes.

upstream backend_pool {
    server 10.0.1.10:3000 weight=5;
    server 10.0.1.11:3000 weight=3;
    server 10.0.1.12:3000 weight=2;
}

Verificaciones de salud y failover

Configura cómo Nginx maneja backends fallidos:

upstream backend_pool {
    server 10.0.1.10:3000 max_fails=3 fail_timeout=30s;
    server 10.0.1.11:3000 max_fails=3 fail_timeout=30s;
    server 10.0.1.12:3000 backup;  # Solo se usa cuando todos los servidores primarios están caídos
}
  • max_fails=3 — Marca el servidor como no disponible después de 3 fallos consecutivos
  • fail_timeout=30s — Espera 30 segundos antes de intentar de nuevo con el servidor fallido
  • backup — Solo enruta tráfico aquí cuando todos los servidores primarios están caídos

Caché de respuestas del proxy

Almacena en caché las respuestas del backend para reducir la carga y mejorar los tiempos de respuesta:

# Definir la zona de caché (colocar en bloque http)
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app_cache:10m
                 max_size=1g inactive=60m use_temp_path=off;

server {
    listen 80;
    server_name app.knowledgexchange.xyz;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_cache app_cache;
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503;

        # Agregar encabezado de estado de caché para depuración
        add_header X-Cache-Status $upstream_cache_status;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # Omitir caché para solicitudes autenticadas
    location /api/ {
        proxy_pass http://127.0.0.1:3000;
        proxy_cache app_cache;
        proxy_cache_bypass $http_authorization $cookie_session;
        proxy_no_cache $http_authorization $cookie_session;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

El encabezado X-Cache-Status ayuda con la depuración: HIT significa que la respuesta se sirvió desde caché, MISS significa que se obtuvo del backend, y BYPASS significa que se omitió el caché.

Consejo: Usa proxy_cache_use_stale para servir contenido en caché obsoleto cuando el backend está caído. Esto proporciona una red de seguridad durante interrupciones del backend.

Limitación de tasa

Protege tu backend del abuso y ataques DDoS:

# Definir zona de límite de tasa (colocar en bloque http)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/s;

server {
    listen 80;
    server_name app.knowledgexchange.xyz;

    location /api/ {
        limit_req zone=api_limit burst=20 nodelay;
        limit_req_status 429;

        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /auth/login {
        limit_req zone=login_limit burst=5;
        limit_req_status 429;

        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Esto permite 10 solicitudes por segundo a la API con un búfer de ráfaga de 20, y limita los intentos de inicio de sesión a 1 por segundo con una ráfaga de 5.

Buffering del proxy

Nginx almacena en búfer las respuestas del backend por defecto. Esto libera la conexión del backend rápidamente, incluso si el cliente tiene una conexión lenta:

location / {
    proxy_pass http://127.0.0.1:3000;

    # Habilitar buffering (predeterminado)
    proxy_buffering on;
    proxy_buffer_size 4k;
    proxy_buffers 8 16k;
    proxy_busy_buffers_size 32k;

    # Para descargas de archivos grandes
    proxy_max_temp_file_size 1024m;

    proxy_set_header Host $host;
}

Para streaming en tiempo real o Server-Sent Events (SSE), deshabilita el buffering:

location /events/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_buffering off;
    proxy_cache off;

    # Configuración específica para SSE
    proxy_set_header Connection '';
    proxy_http_version 1.1;
    chunked_transfer_encoding off;

    proxy_set_header Host $host;
}

Proxy a contenedores Docker

Cuando ejecutas aplicaciones en Docker, redirige al contenedor a través de la red interna:

# Opción 1: Proxy a un puerto publicado
upstream webapp {
    server 127.0.0.1:8080;
}

# Opción 2: Proxy a la red interna de Docker
# (cuando Nginx también está en un contenedor Docker en la misma red)
upstream webapp {
    server app-container:3000;
}

server {
    listen 80;
    server_name app.knowledgexchange.xyz;

    location / {
        proxy_pass http://webapp;
        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;
    }
}

Si estás usando Docker Compose, define ambos servicios en la misma red:

# docker-compose.yml
services:
  nginx:
    image: nginx:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    networks:
      - webnet

  app:
    build: .
    expose:
      - "3000"
    networks:
      - webnet

networks:
  webnet:

Observa que la aplicación usa expose (solo interno) en lugar de ports (publicado al host). Nginx maneja todo el tráfico de cara al exterior.

Múltiples aplicaciones en diferentes subdominios

Enruta el tráfico a diferentes aplicaciones backend basándote en el subdominio:

# Aplicación web principal
server {
    listen 80;
    server_name app.knowledgexchange.xyz;

    location / {
        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;
    }
}

# Servidor API
server {
    listen 80;
    server_name api.knowledgexchange.xyz;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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;
    }
}

# Panel de administración
server {
    listen 80;
    server_name admin.knowledgexchange.xyz;

    # Restringir acceso por IP
    allow 10.0.0.0/8;
    allow 192.168.1.0/24;
    deny all;

    location / {
        proxy_pass http://127.0.0.1:9000;
        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;
    }
}

# Herramientas de monitoreo (Grafana, Prometheus)
server {
    listen 80;
    server_name monitoring.knowledgexchange.xyz;

    location / {
        proxy_pass http://127.0.0.1:3001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /prometheus/ {
        proxy_pass http://127.0.0.1:9090/;
        proxy_set_header Host $host;
    }
}

Consideraciones de seguridad

Endurece tu proxy inverso con estos encabezados de seguridad y prácticas:

server {
    listen 443 ssl http2;
    server_name app.knowledgexchange.xyz;

    # Ocultar la versión de Nginx
    server_tokens off;

    # Encabezados de seguridad
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Content-Security-Policy "default-src 'self'" always;

    # Limitar tamaño del cuerpo de la solicitud
    client_max_body_size 10m;

    # Tiempos de espera para prevenir ataques slowloris
    client_body_timeout 10s;
    client_header_timeout 10s;
    send_timeout 10s;

    # Bloquear rutas comunes de exploits
    location ~* \.(git|env|bak|sql|log)$ {
        deny all;
        return 404;
    }

    location / {
        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;

        # No pasar encabezados internos sensibles al backend
        proxy_set_header X-Powered-By "";
    }
}

Consejo: Usa server_tokens off; en tu bloque http para evitar que Nginx revele su número de versión en páginas de error y encabezados de respuesta. Esta es una medida de endurecimiento simple pero efectiva.

Ajuste de rendimiento

Optimiza Nginx para cargas de trabajo de proxy inverso de alto tráfico:

# Colocar en el bloque http
# Pool de conexiones al upstream
upstream backend {
    server 127.0.0.1:3000;
    keepalive 64;
    keepalive_timeout 60s;
    keepalive_requests 1000;
}

server {
    listen 80;
    server_name app.knowledgexchange.xyz;

    # Habilitar compresión gzip
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_types
        text/plain
        text/css
        text/javascript
        application/json
        application/javascript
        application/xml
        image/svg+xml;

    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";  # Habilitar keepalive al upstream
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # Tiempos de espera de conexión
        proxy_connect_timeout 5s;
        proxy_read_timeout 30s;
        proxy_send_timeout 30s;
    }

    # Servir archivos estáticos directamente (omitir proxy)
    location /static/ {
        alias /var/www/app/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
        access_log off;
    }
}

Consejos clave de rendimiento:

  • Conexiones keep-alive a los backends reducen la sobrecarga del handshake TCP
  • Servir archivos estáticos directamente desde Nginx en lugar de redirigirlos al backend
  • Habilitar gzip para respuestas basadas en texto para reducir el ancho de banda
  • Establecer tiempos de espera apropiados para liberar conexiones de backends lentos o muertos rápidamente

Probar tu configuración

Siempre valida tu configuración de Nginx antes de recargar:

# Probar sintaxis de la configuración
sudo nginx -t

# Recargar sin tiempo de inactividad
sudo systemctl reload nginx

# Observar el log de errores para problemas
sudo tail -f /var/log/nginx/error.log

Para probar los encabezados del proxy, usa curl:

# Verificar encabezados de respuesta
curl -I https://app.knowledgexchange.xyz

# Verificar X-Cache-Status para el caché
curl -s -o /dev/null -w "%{http_code}" -H "Host: app.knowledgexchange.xyz" http://127.0.0.1

Resumen

Nginx como proxy inverso proporciona una capa confiable y de alto rendimiento entre tus usuarios y tus aplicaciones backend. Comienza con una configuración básica de proxy_pass, luego agrega capas de terminación SSL, caché, limitación de tasa y balanceo de carga a medida que tus necesidades crezcan. Las configuraciones en esta guía forman una base sólida para despliegues en producción.

Para más sobre la configuración de Nginx, explora nuestros artículos de Nginx. Para configurar certificados SSL para tu proxy, consulta nuestra guía sobre crear certificados autofirmados o aprende sobre versiones de compilación y módulos de Nginx.