Qué significa realmente un error 502 y por qué aparece tan a menudo
Pocas pantallas generan tanta frustración como la que muestra un 502 Bad Gateway. Para quien visita una web, es un muro sin explicación. Para quien la administra, suele ser la señal de que algo se ha roto en una capa que no se ve a simple vista. La buena noticia es que este error tiene un significado muy concreto y, entendiendo bien de dónde viene, casi siempre se puede diagnosticar en pocos minutos.
Un 502 indica que un servidor que actúa como intermediario, ya sea un proxy, una pasarela o un balanceador, recibió una respuesta no válida del servidor al que reenvió la petición. Es decir, el componente que te responde no es el que ha fallado: solo está informando de que el servicio que hay detrás de él no le devolvió algo utilizable.
Hoy prácticamente ninguna aplicación web se sirve directamente desde un único proceso. Entre el navegador y tu código suele haber una CDN, un balanceador de carga en la nube, un proxy inverso como Nginx y, al final, el servidor de aplicaciones: PHP-FPM, un proceso de Node.js, Gunicorn o un contenedor en Kubernetes. Cada salto añade un punto donde la cadena puede romperse, y por eso el 502 es uno de los errores más habituales en producción.
Esta guía aborda el problema desde las dos perspectivas: primero, las comprobaciones que puede hacer cualquier usuario en su equipo; después, el diagnóstico completo en el servidor, con configuraciones actualizadas para Nginx 1.30, la rama estable actual, y PHP 8.5.
El 502 dentro de la familia de errores 5xx
Los códigos de estado HTTP 5xx indican que la petición del cliente era correcta, pero el servidor no pudo completarla. Confundirlos entre sí lleva a buscar el problema en el sitio equivocado, así que conviene tener clara la diferencia:
| Código | Nombre | Qué indica |
|---|---|---|
| 500 | Internal Server Error | La aplicación falló al procesar la petición |
| 501 | Not Implemented | El servidor no soporta el método solicitado |
| 502 | Bad Gateway | El intermediario recibió una respuesta inválida o ninguna del servicio de origen |
| 503 | Service Unavailable | El servicio está saturado o en mantenimiento de forma deliberada |
| 504 | Gateway Timeout | El intermediario esperó demasiado y el origen nunca respondió a tiempo |
| 505 | HTTP Version Not Supported | El servidor no admite la versión de HTTP usada |
La distinción más importante es entre 502 y 504. En un 504 el servicio de origen existe y acepta la conexión, pero tarda demasiado. En un 502 la conexión se rechaza, se corta a mitad o la respuesta llega malformada. Esa diferencia orienta todo el diagnóstico posterior.
Puedes ver el código exacto, y quién lo genera, con curl:
curl -sI https://tienda.ejemplo.com/
HTTP/2 502
server: nginx
date: Tue, 27 Oct 2026 14:33:13 GMT
content-type: text/html
content-length: 157
La cabecera server revela qué componente emitió el error. Si dice nginx, el problema está entre Nginx y tu aplicación. Si dice cloudflare, awselb/2.0 o el nombre de otra CDN o balanceador, el fallo está un nivel más arriba.
Las causas más frecuentes, de un vistazo
Antes de entrar en detalle, este es el mapa general de lo que suele provocar un 502:
- El proceso de la aplicación está detenido o se ha bloqueado.
- El proxy apunta a un puerto, una dirección o un socket incorrectos.
- Problemas de permisos al acceder a un socket Unix.
- La aplicación se queda sin trabajadores disponibles y rechaza conexiones.
- El servicio se cae por falta de memoria y el kernel lo termina.
- Cabeceras de respuesta demasiado grandes para los búferes del proxy.
- Desajustes de protocolo, como HTTP frente a HTTPS o HTTP/1.1 frente a HTTP/2.
- Cortafuegos o grupos de seguridad que bloquean el tráfico entre capas.
- Registros DNS recién cambiados que aún no se han propagado.
- En el lado del cliente, cachés corruptas, extensiones o DNS locales defectuosos.
La gran mayoría de los casos pertenece al servidor. Aun así, empezar descartando el equipo local evita perder tiempo cuando el fallo solo lo ve una persona.
Primeras comprobaciones desde el lado del usuario
Si eres usuario y te encuentras con un 502 en una web ajena, tu margen de acción es limitado, pero estas comprobaciones resuelven los casos que sí dependen de tu equipo.
Espera un minuto y recarga. Muchos 502 son transitorios: un despliegue en curso, un reinicio del servicio o un pico momentáneo de tráfico. Usa Ctrl+F5 en Windows y Linux, o Cmd+Shift+R en macOS, para forzar una recarga sin caché.
Prueba en otro navegador o en una ventana privada. Si la página carga, el problema está en tu navegador: alguna extensión, un bloqueador de anuncios o datos almacenados corruptos. Conviene probar con un navegador de otro motor, por ejemplo Firefox si usas uno basado en Chromium.
Cambia de red o de dispositivo. Abrir la web desde el móvil con datos, en lugar del wifi de casa, descarta problemas del router, de la red corporativa o del proveedor de internet. Reiniciar el router también resuelve más casos de los que parece.
Borra la caché del navegador. En Chrome y Edge, el atajo Ctrl+Shift+Supr abre directamente el diálogo para eliminar imágenes y archivos almacenados en caché; en Firefox el mismo atajo abre la limpieza del historial reciente.
Consulta si el sitio está caído para todos. Herramientas públicas de monitorización o las páginas de estado de cada servicio te dirán si el problema es general.
Cómo limpiar la caché DNS en cada sistema
Cuando una web cambia de servidor, tu equipo puede seguir guardando la dirección IP antigua y enviar las peticiones a una máquina que ya no aloja el sitio. Saber limpiar la caché DNS local resuelve estos casos en segundos.
En Windows 10 y Windows 11, abre una terminal como administrador:
ipconfig /flushdns
Configuración IP de Windows
Se vació correctamente la caché de resolución de DNS.
En macOS:
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder
En distribuciones Linux con systemd-resolved, como Ubuntu o Fedora:
resolvectl flush-caches
resolvectl statistics | grep -i "cache"
Los navegadores basados en Chromium mantienen además su propia caché DNS interna. Puedes vaciarla escribiendo chrome://net-internals/#dns en Chrome o edge://net-internals/#dns en Edge y pulsando el botón para borrar la caché de hosts.
Si sospechas que el problema es tu proveedor de DNS, cambia temporalmente a un resolvedor público y compara qué IP devuelve cada uno:
dig +short tienda.ejemplo.com @1.1.1.1
dig +short tienda.ejemplo.com @8.8.8.8
Cómo se forma un 502 en una arquitectura moderna
Para diagnosticar desde el servidor hay que visualizar el camino completo de una petición. Un despliegue típico en 2026 tiene una estructura parecida a esta:
Navegador
│
▼
CDN / WAF (Cloudflare, CloudFront...)
│
▼
Balanceador de carga (ALB, Load Balancer de GCP...)
│
▼
Proxy inverso (Nginx, Caddy, Traefik)
│
▼
Servidor de aplicaciones (PHP-FPM, Node.js, Gunicorn, Puma)
│
▼
Base de datos, caché, APIs externas
Cada flecha es una conexión de red independiente. Un 502 aparece cuando una de las capas intermedias no obtiene una respuesta válida de la siguiente. La estrategia de diagnóstico consiste en recorrer la cadena desde fuera hacia dentro, preguntando en cada salto si el componente siguiente está vivo y responde correctamente.
Diagnóstico en Nginx: leer el log de errores
Nginx es el punto de partida más habitual, porque casi siempre es la capa que convierte un fallo del backend en un 502 visible. Su log de errores dice exactamente qué ocurrió:
sudo tail -n 50 /var/log/nginx/error.log
Estos son los mensajes más frecuentes y lo que significa cada uno:
connect() failed (111: Connection refused) while connecting to upstream,
client: 203.0.113.24, server: tienda.ejemplo.com, upstream: "http://127.0.0.1:3000/"
Nadie escucha en ese puerto. La aplicación está caída o escucha en otro puerto.
connect() to unix:/run/php/php8.5-fpm.sock failed (2: No such file or directory)
El socket no existe: PHP-FPM está detenido o la ruta configurada no coincide con la real.
connect() to unix:/run/php/php8.5-fpm.sock failed (13: Permission denied)
El socket existe, pero el usuario de Nginx no tiene permiso para usarlo.
upstream prematurely closed connection while reading response header from upstream
El backend aceptó la conexión pero la cerró antes de responder, normalmente porque el proceso se bloqueó o fue terminado.
upstream sent too big header while reading response header from upstream
La aplicación devolvió cabeceras, como cookies muy grandes, que no caben en los búferes configurados.
Antes de tocar nada, verifica también que la configuración actual es válida:
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Comprobar que el backend está vivo
Una vez que el log apunta al backend, el siguiente paso es comprobar si el proceso está realmente en ejecución y escuchando donde Nginx espera encontrarlo.
# Estado de los servicios
systemctl status php8.5-fpm
systemctl status api-pedidos
# Puertos y sockets en escucha
sudo ss -ltnp | grep -E ':3000|:8000'
sudo ss -lxp | grep php
# Probar el backend directamente, sin pasar por Nginx
curl -i http://127.0.0.1:3000/salud
Si la petición directa al backend funciona pero a través de Nginx falla, el problema está en la configuración del proxy. Si la petición directa también falla, el problema está en la aplicación y Nginx solo lo está reportando.
Revisa también si el sistema terminó el proceso por falta de memoria, una causa muy común en servidores pequeños:
journalctl -k --since "1 hour ago" | grep -i "out of memory"
journalctl -u api-pedidos --since "30 min ago" --no-pager | tail -n 40
Solucionar el 502 con PHP-FPM
En aplicaciones PHP, como WordPress, Laravel o Drupal, la combinación de Nginx y PHP-FPM es la más extendida y también una de las que más 502 genera. La versión actual de la rama estable de PHP es la 8.5, y en Debian o Ubuntu su socket suele ubicarse en /run/php/php8.5-fpm.sock. Lo primero es comprobar que la directiva de Nginx apunta a la ruta correcta:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.5-fpm.sock;
}
Un error muy habitual aparece tras actualizar PHP: el sistema instala php8.5-fpm, pero la configuración de Nginx sigue apuntando al socket de la versión anterior, que ya no existe.
Si el problema son los permisos, revisa el pool de PHP-FPM, normalmente en /etc/php/8.5/fpm/pool.d/www.conf:
[www]
user = www-data
group = www-data
listen = /run/php/php8.5-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
El usuario con el que se ejecuta Nginx debe pertenecer al grupo indicado en listen.group. En la familia Red Hat, Nginx corre como usuario nginx, así que hay que ajustar estos valores en consecuencia.
Cuando el 502 aparece solo bajo carga, la causa suele ser que PHP-FPM se queda sin procesos trabajadores. El log de PHP-FPM lo avisa con claridad:
WARNING: [pool www] server reached pm.max_children setting (5), consider raising it
La solución es dimensionar el pool según la memoria disponible. Si cada proceso consume unos 60 MB y puedes dedicar 2 GB a PHP, un valor razonable para pm.max_children ronda los 30:
pm = dynamic
pm.max_children = 30
pm.start_servers = 6
pm.min_spare_servers = 4
pm.max_spare_servers = 10
pm.max_requests = 800
request_terminate_timeout = 60s
El parámetro pm.max_requests recicla cada proceso tras cierto número de peticiones, lo que mitiga fugas de memoria en código antiguo. Aplica los cambios y vigila el resultado:
sudo php-fpm8.5 -t
sudo systemctl reload php8.5-fpm
Solucionar el 502 con Node.js, Python y otros backends HTTP
Cuando el backend es un servidor HTTP propio, como una aplicación de Node.js con Express o Fastify, o una de Python con Gunicorn o Uvicorn, Nginx actúa como proxy con proxy_pass. Una configuración sólida para este escenario sería:
upstream api_pedidos {
server 127.0.0.1:3000 max_fails=3 fail_timeout=15s;
server 127.0.0.1:3001 max_fails=3 fail_timeout=15s;
keepalive 32;
}
server {
listen 443 ssl;
http2 on;
server_name api.ejemplo.com;
location / {
proxy_pass http://api_pedidos;
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 5s;
proxy_read_timeout 60s;
proxy_next_upstream error timeout http_502;
proxy_next_upstream_tries 2;
}
}
Fíjate en la directiva http2 on: en las versiones actuales de Nginx es la forma recomendada de activar HTTP/2, ya que el antiguo parámetro http2 dentro de listen está obsoleto y genera un aviso.
Hay varios fallos típicos en este escenario. El primero es que la aplicación escuche solo en una interfaz concreta. Si tu servidor de Node.js escucha en localhost y el sistema resuelve ese nombre a la dirección IPv6 ::1, pero Nginx conecta a 127.0.0.1, obtendrás un Connection refused aunque el proceso esté funcionando. Lo más seguro es fijar la dirección explícitamente:
import express from "express";
const app = express();
const PUERTO = Number(process.env.PUERTO ?? 3000);
app.get("/salud", (req, res) => res.json({ estado: "ok" }));
app.listen(PUERTO, "127.0.0.1", () => {
console.log(`API de pedidos escuchando en 127.0.0.1:${PUERTO}`);
});
El segundo es el desajuste de keep-alive. Si Nginx reutiliza una conexión que el backend ya ha cerrado por inactividad, la petición falla con un 502 intermitente. En Node.js conviene que el tiempo de keep-alive del servidor sea mayor que el del proxy o el balanceador:
const servidor = app.listen(PUERTO, "127.0.0.1");
servidor.keepAliveTimeout = 65_000;
servidor.headersTimeout = 66_000;
El tercero es enviar HTTP a un backend que solo acepta HTTPS, o al revés. Si el backend usa TLS, proxy_pass debe empezar por https://.
Cabeceras grandes y búferes del proxy
Las aplicaciones con sesiones complejas, tokens JWT voluminosos o muchas cookies pueden generar cabeceras de respuesta que superan el búfer por defecto de Nginx. El resultado es un 502 acompañado del mensaje upstream sent too big header. Se corrige ampliando los búferes, con moderación:
# Para backends HTTP
proxy_buffer_size 16k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
# Para PHP-FPM
fastcgi_buffer_size 32k;
fastcgi_buffers 16 16k;
Ampliar búferes indefinidamente no es una solución: si las cabeceras crecen sin control, conviene revisar por qué la aplicación está enviando cookies o tokens tan grandes.
El 502 en Docker y Kubernetes
En entornos con contenedores, el 502 suele deberse a problemas de red entre servicios más que a la aplicación en sí. El error clásico en Docker Compose es configurar Nginx para que apunte a localhost. Dentro de un contenedor, localhost es el propio contenedor de Nginx, no el de la aplicación. Hay que usar el nombre del servicio:
services:
proxy:
image: nginx:1.30-alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
api:
condition: service_healthy
api:
build: ./api
environment:
PUERTO: "3000"
HOST: "0.0.0.0"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/salud"]
interval: 10s
retries: 3
location / {
proxy_pass http://api:3000;
}
Fíjate también en que la aplicación debe escuchar en 0.0.0.0 dentro del contenedor, y no en 127.0.0.1, para aceptar conexiones de otros contenedores. Es justo lo contrario de lo recomendado cuando Nginx y la aplicación conviven en la misma máquina.
En Kubernetes, los 502 del Ingress aparecen sobre todo durante despliegues: el controlador envía tráfico a pods que todavía no están listos o que ya están terminando. Las sondas de disponibilidad y una pausa breve antes de apagar el contenedor eliminan la mayoría de estos casos:
containers:
- name: api-pedidos
image: registro.ejemplo.com/api-pedidos:2.4.1
ports:
- containerPort: 3000
readinessProbe:
httpGet:
path: /salud
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
lifecycle:
preStop:
exec:
command: ["sleep", "10"]
Para comprobar si el Service tiene endpoints válidos detrás:
kubectl get endpointslices -l kubernetes.io/service-name=api-pedidos
kubectl describe pod -l app=api-pedidos | grep -A5 "Readiness"
Balanceadores en la nube, CDN y DNS
Cuando la cabecera server de la respuesta indica que el error viene de un balanceador o una CDN, el diagnóstico cambia de capa. En un Application Load Balancer de AWS, un 502 suele indicar que el destino cerró la conexión, devolvió una respuesta malformada o que el certificado del backend no es válido cuando se usa HTTPS hacia los destinos. Revisar el estado de los grupos de destino es el primer paso:
aws elbv2 describe-target-health \
--target-group-arn arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/api-pedidos/abc123
Un caso muy habitual con balanceadores es, de nuevo, el keep-alive: si el balanceador mantiene conexiones abiertas más tiempo que el backend, este las cierra y el balanceador intenta reutilizarlas, provocando 502 esporádicos difíciles de reproducir.
Con CDN como Cloudflare, el 502 puede aparecer porque el servidor de origen no responde en el puerto esperado, porque un cortafuegos bloquea los rangos de IP de la CDN o porque el modo SSL configurado no coincide con lo que ofrece el origen.
Si acabas de migrar el dominio a un servidor nuevo, comprueba que los registros DNS ya apuntan a la IP correcta en todo el mundo, y recuerda que hasta que expire el TTL anterior algunos usuarios seguirán llegando al servidor antiguo.
Errores comunes al intentar solucionar un 502
Algunas reacciones habituales empeoran el problema o solo lo esconden:
- Subir todos los timeouts a valores enormes. Convierte un 502 rápido en peticiones colgadas durante minutos y agota los recursos del servidor.
- Reiniciar servicios sin mirar los logs. El síntoma desaparece temporalmente y la causa vuelve a aparecer en el siguiente pico.
- Aumentar
pm.max_childrensin calcular la memoria. Más procesos de los que caben en RAM provocan que el sistema termine procesos por falta de memoria, con más 502. - Desactivar la verificación de certificados entre capas para que todo funcione. Abre una brecha de seguridad innecesaria.
- Editar la configuración de Nginx en producción sin ejecutar
nginx -tantes de recargar. - Culpar siempre al proxy. En la mayoría de los casos, Nginx solo está informando de un fallo que ocurre detrás de él.
Buenas prácticas para prevenir errores 502 en producción
Un 502 ocasional durante un despliegue puede tolerarse; uno recurrente indica una arquitectura frágil. Estas prácticas reducen drásticamente su frecuencia:
- Expón un endpoint de salud ligero en cada servicio y úsalo en balanceadores, orquestadores y monitorización.
- Despliega con estrategias sin corte: rolling updates con sondas de disponibilidad, o despliegues azul-verde.
- Gestiona los procesos con systemd, Docker o Kubernetes para que se reinicien automáticamente tras un fallo.
- Ejecuta varias réplicas del backend y configura
proxy_next_upstreampara reintentar en otra instancia. - Alinea los tiempos de keep-alive de forma decreciente desde el cliente hacia el origen.
- Monitoriza la tasa de respuestas 5xx y crea alertas cuando supere un umbral.
- Registra el tiempo de respuesta del backend en los logs de acceso de Nginx.
Un formato de log que añade la información del upstream facilita muchísimo el diagnóstico:
log_format diagnostico '$remote_addr "$request" $status '
'upstream=$upstream_addr '
'estado_upstream=$upstream_status '
'tiempo_upstream=$upstream_response_time '
'tiempo_total=$request_time';
access_log /var/log/nginx/acceso-diagnostico.log diagnostico;
Con este formato, localizar todas las peticiones fallidas y el backend que las causó es inmediato:
grep ' 502 ' /var/log/nginx/acceso-diagnostico.log | awk '{print $5}' | sort | uniq -c
47 upstream=127.0.0.1:3001
2 upstream=127.0.0.1:3000
En este ejemplo queda claro que una de las dos instancias es la responsable de casi todos los fallos.
Una lista de verificación rápida
Cuando un 502 aparece en producción y hay prisa, este orden de comprobaciones resuelve la mayoría de los casos:
# 1. ¿Quién genera el error?
curl -sI https://tienda.ejemplo.com/ | grep -i -E 'HTTP|server'
# 2. ¿Qué dice el proxy?
sudo tail -n 30 /var/log/nginx/error.log
# 3. ¿Está vivo el backend?
systemctl status api-pedidos php8.5-fpm --no-pager
# 4. ¿Escucha donde debe?
sudo ss -ltnp
# 5. ¿Responde sin pasar por el proxy?
curl -i http://127.0.0.1:3000/salud
# 6. ¿Se ha quedado sin memoria?
journalctl -k --since "1 hour ago" | grep -i oom
# 7. ¿La configuración es válida?
sudo nginx -t
Conclusiones
El 502 Bad Gateway no es un error misterioso: es el mensaje de una capa intermedia que no ha recibido una respuesta válida de la siguiente. Entenderlo así cambia por completo la forma de abordarlo, porque el objetivo deja de ser “arreglar Nginx” y pasa a ser localizar en qué salto de la cadena se rompe la comunicación.
Desde el lado del usuario, recargar, probar otro navegador o red y vaciar las cachés del navegador y del DNS cubren los pocos casos que dependen del equipo local. Desde el lado del servidor, la combinación de la cabecera server, el log de errores del proxy y una petición directa al backend permite identificar la causa en minutos: un servicio caído, un socket mal configurado, permisos insuficientes, falta de trabajadores, memoria agotada, cabeceras demasiado grandes o desajustes de keep-alive.
La prevención es la mejor estrategia: endpoints de salud, despliegues sin corte, varias réplicas, tiempos de keep-alive alineados y logs que registren el estado del upstream. Con esas piezas en su sitio, el 502 deja de ser una emergencia recurrente y se convierte en una alerta puntual con un diagnóstico claro.
