Módulo 3: Reverse Proxy & Load Balancing

Nginx fundamentals: configuración para FastAPI

Esta cápsula te muestra Nginx funcional para FastAPI. Configuration mínima, escalable, production-ready. No mastery — lo que necesitas como backend dev.


Anatomía de nginx.conf

# /etc/nginx/nginx.conf

# Configuración global
worker_processes auto;
events {
    worker_connections 1024;
}

# HTTP block — toda config de HTTP
http {
    # MIME types, defaults
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    # Logs
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log warn;

    # Performance
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;

    # Servers
    include /etc/nginx/conf.d/*.conf;
}

Lo importante para ti: el bloque http { ... } contiene servers. Cada server es un site/domain.


Server block para FastAPI

# /etc/nginx/conf.d/my-api.conf

upstream backend {
    server 127.0.0.1:8000;
}

server {
    listen 80;
    server_name api.example.com;

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

        # Timeouts
        proxy_connect_timeout 30s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;
    }
}

Anatomy:

  • upstream backend: define backend(s). En este caso, un solo uvicorn en port 8000.
  • server { ... }: configuración para api.example.com.
  • listen 80: escuchar HTTP en puerto 80 (HTTPS lo veremos en cap 4).
  • location /: matchear todas las URLs.
  • proxy_pass http://backend: forward al upstream.
  • proxy_set_header: headers críticos.
  • Timeouts: prevenir requests colgados.

location blocks: routing

server {
    listen 80;
    server_name api.example.com;

    # API routes
    location /api {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        # ... headers ...
    }

    # Static files
    location /static {
        alias /var/www/static;
        expires 30d;
        access_log off;
    }

    # Frontend (SPA)
    location / {
        root /var/www/frontend;
        try_files $uri /index.html;
    }
}

location blocks definen handling per-path:

  • /api/* → forward a backend.
  • /static/* → serve files from disk.
  • /* → serve frontend SPA.

Headers para FastAPI

Set siempre estos headers al proxy_pass:

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;

Host: original host header. App lo necesita para generar URLs absolutas.

X-Real-IP: IP cliente directo (después del último proxy).

X-Forwarded-For: lista de IPs en el path (cliente, proxies intermedios). $proxy_add_x_forwarded_for agrega current IP a la lista existente.

X-Forwarded-Proto: protocolo original (http o https). Útil cuando proxy termina SSL.

En FastAPI:

from fastapi import Request

@app.middleware("http")
async def use_forwarded_headers(request: Request, call_next):
    forwarded_proto = request.headers.get("x-forwarded-proto", "http")
    if forwarded_proto == "https":
        request.scope["scheme"] = "https"
    return await call_next(request)

O usar proxy_headers_middleware de uvicorn:

uvicorn app.main:app --host 0.0.0.0 --port 8000 --proxy-headers --forwarded-allow-ips='*'

--proxy-headers hace que uvicorn read X-Forwarded-* headers correctamente.


Timeouts: prevenir colgados

location /api {
    proxy_pass http://backend;

    # Tiempo para conectar a backend
    proxy_connect_timeout 30s;

    # Tiempo para enviar request a backend
    proxy_send_timeout 30s;

    # Tiempo para recibir response
    proxy_read_timeout 30s;

    # Para uploads grandes
    client_max_body_size 100M;
    client_body_timeout 60s;
}

Sin timeouts, requests pueden quedar colgados forever, leaking workers.

Para endpoints específicos (e.g., uploads largos):

location /upload {
    proxy_pass http://backend;
    proxy_read_timeout 300s;  # 5 minutes
    client_max_body_size 1G;
}

Buffering

proxy_buffering on;          # default
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;

proxy_buffering on: Nginx buffer entire response from backend antes de enviar a client. Útil para clients lentos.

proxy_buffering off: streaming directo. Útil para Server-Sent Events o downloads grandes.

location /stream {
    proxy_pass http://backend;
    proxy_buffering off;  # SSE necesita streaming
    proxy_cache off;
}

Logging

Default access log:

192.168.1.100 - - [10/May/2026:14:32:11 +0000] "GET /api/posts HTTP/1.1" 200 1234 "-" "curl/7.68.0"

Custom format con info útil:

log_format custom '$remote_addr - $remote_user [$time_local] '
                  '"$request" $status $body_bytes_sent '
                  '"$http_referer" "$http_user_agent" '
                  '$request_time $upstream_response_time';

access_log /var/log/nginx/access.log custom;

Agrega $request_time (total time) y $upstream_response_time (tiempo del backend). Útil para debuggear performance.


WebSocket support

FastAPI puede tener WebSocket endpoints. Nginx requiere config especial:

location /ws {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    # WebSockets pueden ser long-lived
    proxy_read_timeout 86400;  # 24h
}

Upgrade y Connection headers son lo que hace que WebSocket funcione.


Configuración completa para FastAPI

# /etc/nginx/conf.d/api.conf

upstream fastapi_backend {
    server 127.0.0.1:8000;

    # Si tienes múltiples instances:
    # server 127.0.0.1:8001;
    # server 127.0.0.1:8002;

    keepalive 32;  # connection pool
}

server {
    listen 80;
    server_name api.example.com;

    # Logs
    access_log /var/log/nginx/api-access.log;
    error_log /var/log/nginx/api-error.log warn;

    # Body size para uploads
    client_max_body_size 50M;

    # Health check público (Nginx maneja, no llega a backend)
    location /nginx-health {
        access_log off;
        return 200 "ok\n";
    }

    # Static files
    location /static/ {
        alias /var/www/static/;
        expires 30d;
        access_log off;
    }

    # API
    location / {
        proxy_pass http://fastapi_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";

        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;

        proxy_connect_timeout 30s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;
    }

    # WebSocket
    location /ws {
        proxy_pass http://fastapi_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
    }
}

Esto es production-ready. ~50 líneas. Cubre: routing, headers, timeouts, static files, WebSocket.


Setup local con Docker

Para experimentar sin tocar tu deploy real:

# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "127.0.0.1:8000:8000"  # solo accessible internamente
    command: uvicorn app.main:app --host 0.0.0.0 --port 8000

  nginx:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app
docker-compose up
curl http://localhost:8080/health

Tu app accessible solo via Nginx. Replicar production setup localmente.


Reload sin downtime

Cambias config:

# Test config (NO aplicar)
sudo nginx -t

# Si OK, reload (sin downtime)
sudo nginx -s reload

Nginx hace graceful reload: nuevos workers con config nueva, viejos terminan requests in-flight.


Trampas y errores comunes

1. Olvidar proxy_set_header Host $host.

Sin esto, FastAPI recibe Host: 127.0.0.1 (Nginx). Genera URLs absolutas raras.

2. client_max_body_size muy bajo.

Default Nginx: 1MB. Uploads >1MB fail con 413. Aumentar según necesidad.

3. Timeout muy bajo en endpoint largo.

proxy_read_timeout 30s;

Endpoint de export de 5min timeout. Aumentar para endpoints específicos.

4. Sin proxy_http_version 1.1.

Default es HTTP/1.0. No keep-alive con backend. Performance peor.

5. keepalive en upstream sin proxy_set_header Connection "".

Sin override, Nginx envía Connection: close con keepalive. Conflict.

proxy_http_version 1.1;
proxy_set_header Connection "";

6. Logs no rotated.

/var/log/nginx/access.log puede crecer infinitamente. Configurar logrotate:

/var/log/nginx/*.log {
    daily
    rotate 14
    compress
    delaycompress
    postrotate
        nginx -s reopen
    endscript
}

7. Reload sin testing.

nginx -s reload con config inválida → server muere. Siempre nginx -t primero.


Ejercicio: setup Nginx local

Paso 1: crear nginx.conf mínimo.

upstream app_backend {
    server app:8000;
}

server {
    listen 80;

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

Paso 2: crear docker-compose.yml.

(Como ejemplo arriba.)

Paso 3: levantar.

docker-compose up

Paso 4: test.

curl http://localhost:8080/health
# Hit Nginx → proxies a uvicorn → returns response

Paso 5: test headers.

Endpoint en FastAPI:

@app.get("/headers")
async def show_headers(request: Request):
    return dict(request.headers)
curl http://localhost:8080/headers
# Esperas: x-forwarded-for, x-real-ip, host, etc.

Paso 6: experimentar con proxy_pass.

Cambiar header:

proxy_set_header X-Custom-Header "test-value";

Reload:

docker-compose exec nginx nginx -t
docker-compose exec nginx nginx -s reload

Verificar que header llega.


Resumen y siguiente paso

Lo que aprendiste:

  • Anatomy de Nginx config: http → server → location.
  • upstream para definir backends.
  • proxy_pass para forward.
  • Headers críticos: Host, X-Forwarded-*.
  • Timeouts para prevenir colgados.
  • WebSocket requiere Upgrade headers.
  • nginx -t para test, nginx -s reload para apply sin downtime.

En la siguiente cápsula vamos a SSL termination. HTTPS configurado en Nginx, comunicación interna HTTP. Concepto crítico.


Recursos

  1. Nginx — proxy_pass — referencia.
  2. Nginx Beginner's Guide — official.
  3. Nginx + WebSockets — específico.
  4. DigitalOcean — Nginx as reverse proxy — tutorial.

Cápsula 03 de 08 — Módulo 3 — Deployment & System Design Guide