Qué hay entre el navegador y tu app
Apunta el navegador a example.com y la petición nunca toca el proceso que ejecuta tu código. Primero llega a otra cosa: un servidor que lee la petición, decide qué backend debe responder, la reenvía ahí y devuelve la respuesta como si la hubiera generado él mismo. Ese servidor es un reverse proxy.
Nginx, Caddy, Traefik y HAProxy hacen todos este trabajo, igual que el load balancer que se sienta delante de un clúster de Kubernetes. El esquema no cambia nunca: los clientes hablan con el proxy y con nada más. Los servidores reales quedan detrás, sean los que sean y estén donde estén.
Cómo gestiona un reverse proxy una petición
Un cliente abre una conexión contra la IP y el puerto del reverse proxy, igual que haría con cualquier servidor. El proxy termina esa conexión, lee la línea de petición y las cabeceras, y las compara con sus propias reglas — normalmente la cabecera Host y la ruta de la URL. Según ese resultado abre una segunda conexión, independiente, hacia un backend, reenvía la petición por ella, espera la respuesta y la devuelve por la conexión original del cliente.
Dos conexiones independientes, cosidas entre sí. Desde el lado del cliente esto es indistinguible de hablar directamente con el backend. Desde el lado del backend, cada petición parece venir de una sola máquina, el proxy, en lugar de venir de internet en general.
| |
Como el proxy ve cada petición antes que el backend, puede inspeccionarla, modificarla, cachearla o rechazarla. Esa posición es la razón por la que termina asumiendo el trabajo que nadie quiere dentro de la aplicación: terminar el TLS, comprimir respuestas, cachear contenido estático, limitar clientes abusivos y enviar rutas distintas a servicios distintos — /api a un backend, / a otro, /static directo a disco.
Reverse proxy vs forward proxy
Los dos se sitúan en medio de una conexión. La diferencia está en para quién trabajan.
| Forward proxy | Reverse proxy | |
|---|---|---|
| Actúa en nombre de | El cliente | El servidor |
| El cliente sabe que existe | Normalmente sí (configurado explícitamente) | No (parece el servidor real) |
| Qué oculta | La identidad del cliente frente al servidor | La identidad del servidor frente al cliente |
| Uso típico | Filtrado de salida corporativo, saltarse geobloqueos, navegación anónima | Enrutamiento, terminación TLS, caché, protección de los servidores de origen |
Un forward proxy es la caja que tu empresa pone en la oficina para que todo el tráfico saliente pase por un único punto filtrado: el sitio que visitas registra la dirección del proxy, no la de tu portátil. Un reverse proxy lo gestiona quien es dueño del sitio, y oculta el otro extremo. Desde fuera no puedes saber si example.com es un servidor o cuarenta.
Reverse proxy vs load balancer
Los dos términos se usan como sinónimos, sobre todo porque un mismo proceso Nginx o HAProxy suele hacer ambos trabajos a la vez.
| Reverse proxy | Load balancer | |
|---|---|---|
| Pregunta principal que responde | ¿Qué debe pasar con esta petición? | ¿Qué servidor debe atender esta petición? |
| ¿Funciona con un solo backend? | Sí, sigue siendo útil (TLS, caché, enrutamiento por ruta) | No, necesita al menos dos para repartir carga |
| Funciones extra típicas | Caché, reescritura de cabeceras, terminación TLS, enrutamiento por ruta | Health checks, distribución ponderada, afinidad de sesión |
Un reverse proxy delante de un solo backend sigue justificando su sitio: termina el TLS para que tu app no tenga que hacerlo, oculta la dirección real del backend, añade gzip. Un load balancer no tiene nada que hacer hasta que hay al menos dos backends entre los que elegir. La etiqueta que uses describe la configuración que has escrito, no el software que has instalado.
Cómo configurar Nginx como reverse proxy
Escucha en un puerto, compara un hostname, reenvía a un backend. Eso es todo el mínimo necesario:
| |
proxy_pass es la línea que importa: todo lo que capta location va a http://127.0.0.1:3000. Las cuatro líneas proxy_set_header existen porque, si no, el backend ve al proxy como cliente, y cada petición parece venir de 127.0.0.1. X-Real-IP y X-Forwarded-For llevan la dirección real del cliente hasta el final. X-Forwarded-Proto le dice al backend si la petición original era HTTPS, ya que el TLS se terminó en el proxy y el siguiente salto es HTTP plano.
Un detalle rompe configuraciones constantemente: una barra final en la URI de proxy_pass cambia la ruta reenviada. proxy_pass http://backend; (sin ruta) reenvía la URI original sin cambios. proxy_pass http://backend/; (con barra final) quita la parte de la URI que location capturó antes de reenviar. Con location /api/ y proxy_pass http://backend/;, una petición a /api/users llega al backend como /users. Invierte el orden y todas las rutas devuelven 404 en el backend mientras curl contra el proxy parece funcionar bien.
Reverse proxy delante de una app en Docker
Aquí es donde este patrón aparece más en el día a día: Nginx en un contenedor, tu app en otro, ambos en la misma red de Docker para que Nginx pueda alcanzar la app por el nombre del contenedor — el comportamiento DNS que se explica en qué ofrece una red Docker.
| |
| |
app expone el puerto 3000 en la red de Compose pero no publica nada en el host. Solo Nginx lo hace, en el puerto 80. El nombre app:3000 en la configuración de Nginx se resuelve mediante el DNS interno de Docker, el mismo mecanismo descrito en la guía de Docker Compose. Ejecuta docker compose up y las peticiones a localhost llegan a Nginx, que las reenvía al contenedor de la app. La app nunca es accesible desde fuera del host por sí sola, lo cual es tan intencionado como el propio enrutamiento.
Qué te cuesta un reverse proxy
Añade un salto de red y un sitio más donde una petición puede fallar. Un proxy_pass mal configurado o una cabecera perdida aparece como un bug en la app cuando el fallo está una capa más arriba. También es un punto único de fallo por defecto: mata ese único proceso Nginx y cada backend detrás queda inalcanzable. Los entornos de producción o lo ejecutan de forma redundante, o le pasan el trabajo a algo que ya asume el fallo — un load balancer en la nube, o un Ingress controller de Kubernetes apoyado en los Deployments y Pods que hay debajo.
Un reverse proxy tampoco hace una app más rápida o más escalable por sí solo. Puede cachear y comprimir, pero un backend lento sigue siendo lento; el proxy simplemente reenvía esa lentitud un salto más tarde. Y no es autenticación ni validación de entrada. Bloquear peticiones claramente malformadas en el borde es un extra, nunca una razón para que la app deje de comprobar lo que recibe.
Llevar esta configuración a producción
Coge el compose.yaml de arriba, cambia app por un servicio real, y añade ssl_certificate y ssl_certificate_key al bloque server de Nginx en cuanto tengas un certificado. Esa es la diferencia entre un ejercicio local y algo que pondrías delante de tráfico real. Si enrutas hacia más de un backend, sustituye la dirección única por un bloque upstream con varias líneas server y apunta proxy_pass a ese bloque: la misma configuración se convierte en un load balancer.