Saltar al contenido
infraestructura / · 4 min

Snippets de nginx que siempre acabo necesitando

Siete bloques que copio de un proyecto a otro: los buferes del 502, las subidas grandes, el webhook con GET y POST en la misma URL, WebSockets y las cabeceras de seguridad.

Administrador · Desarrollador Laravel
Snippets de nginx que siempre acabo necesitando

Estos son los bloques de nginx que acabo copiando de un proyecto a otro. Todos salieron de un problema real en producción; ninguno estaba en la configuración inicial.

1. El 502 que solo aparece con sesión iniciada

Síntoma: la aplicación registra 200, el navegador ve 502, y anónimo funciona. En el log de nginx: upstream sent too big header.

Los búferes de cabeceras vienen en 4 u 8 KB. Un usuario autenticado arrastra cookies que no caben.

# En el nginx que habla con php-fpm
fastcgi_buffer_size       32k;
fastcgi_buffers           16 32k;
fastcgi_busy_buffers_size 64k;

# En el reverse proxy. Como archivo suelto en conf.d/, sin bloque server,
# aplica a TODOS los sitios y los vacuna de una vez.
# /etc/nginx/conf.d/00-proxy-buffers.conf
proxy_buffer_size       32k;
proxy_buffers           16 32k;
proxy_busy_buffers_size 64k;

Si tienes dos capas de nginx, necesitas las dos. El mensaje de error es idéntico y es fácil creer que ya quedó.

2. Subidas grandes que se cortan en 1 MB

Para almacenamiento tipo S3, o cualquier endpoint que reciba archivos pesados:

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

    client_max_body_size       0;    # 0 = sin limite
    proxy_request_buffering    off;  # transmite mientras recibe
    proxy_buffering            off;
    chunked_transfer_encoding  off;
}

Sin esto nginx acumula el cuerpo entero en disco antes de pasarlo: lento con archivos grandes y, pasado el límite por omisión, un 413 seco.

3. Un webhook que necesita GET y POST en la misma URL

Es el caso de WhatsApp: la verificación llega por GET y los mensajes por POST, a la misma dirección, pero cada uno lo atiende un flujo distinto.

location = /webhook/entrada {
    if ($request_method = GET) {
        proxy_pass http://127.0.0.1:5678/webhook/verificacion;
    }
    proxy_pass http://127.0.0.1:5678/webhook/mensajes;
}

Ojo con dos cosas. El if dentro de location es célebre por su mala fama; funciona para esto, no lo estires. Y si nginx protesta con "proxy_pass cannot have URI part in location given by regular expression", es porque tu location es una expresión regular: usa = o un prefijo literal como arriba.

4. WebSockets a través del proxy

Los editores de automatizaciones y cualquier cosa con recarga en vivo lo piden. La parte que se olvida es el map, que va fuera del bloque server:

# /etc/nginx/conf.d/upgrade-map.conf
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

location / {
    proxy_pass http://127.0.0.1:5678;
    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
}

5. Cabeceras de proxy que Laravel necesita

Sin estas, $request->ip() te devuelve la IP del proxy y las URLs generadas salen en http:// aunque el sitio esté en HTTPS:

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;

Del lado de Laravel hay que confiar en el proxy, o las ignora.

6. Cabeceras de seguridad, para todos los sitios

# /etc/nginx/conf.d/_security_headers.inc  (se incluye en cada vhost)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

El always no es adorno: sin él, las cabeceras no se agregan en respuestas de error, que es justo donde más da información un sitio.

7. Negar el acceso a archivos ocultos

location ~ /\.(?!well-known).* {
    deny all;
}

Bloquea .env y .git dejando pasar .well-known, que certbot necesita para renovar. Compruébalo de verdad después de ponerlo:

curl -s -o /dev/null -w "%{http_code}\n" https://tusitio.com/.env    # 403

Y la regla de siempre

sudo nginx -t && sudo systemctl reload nginx

Nunca restart si puedes reload, y nunca recargues sin probar primero. Un nginx -t que falla y un restart a ciegas dejan todos tus sitios abajo, no solo el que estabas editando.

#seguridad #despliegue
Comentarios 0

Nadie ha comentado todavía. Estrena la sección.

Deja tu comentario
Tu correo no se publica. Reviso los comentarios antes de publicarlos.

Seguir leyendo