Loading...

Básicos · 11 min de lectura

Qué es nginx: el servidor web detrás de medio internet, explicado

nginx (se pronuncia "engine-x") es un software de código abierto que acepta conexiones HTTP y decide qué hacer con cada petición: enviar un archivo desde disco, pasarla a una aplicación, repartirla entre varios servidores o responder desde su propia caché. Unos pocos procesos worker, cada uno con un bucle de eventos, mantienen miles de conexiones a la vez, por lo que nginx está delante de tantos sitios, aplicaciones y CDN.

Actualizado

Qué es nginx: el servidor web detrás de medio internet, explicado

Qué es nginx y los cinco trabajos que hace

nginx (se pronuncia "engine-x") es un servidor y proxy HTTP de código abierto. Igor Sysoev lo escribió para mantener diez mil conexiones simultáneas abiertas en una sola máquina, en una época en la que eso tumbaba a la mayoría de los servidores, y lo publicó en 2004. Hoy es uno de los dos servidores web más usados, junto con Apache, y el motor dentro de muchos balanceadores de carga, controladores de ingress de Kubernetes y CDN. La versión de código abierto vive en nginx.org; F5, que compró la empresa en 2019, vende una edición comercial llamada NGINX Plus.

El nombre cubre cinco roles, y una sola configuración puede combinarlos todos:

Servidor web. Lee archivos de disco y los envía: HTML, CSS, JavaScript, imágenes, descargas. Es lo que hace más rápido, con sendfile delegando la copia al kernel.

Proxy inverso. Acepta la petición y la pasa a una aplicación que no debería estar expuesta a internet por sí misma: PHP-FPM, Node.js, Python, Go, Java, un contenedor. El tema completo, incluyendo las cabeceras que hay que reenviar, está en nuestra guía de proxy inverso.

Balanceador de carga. Reparte peticiones entre varias copias de esa aplicación y deja de enviar tráfico a la que falle.

Caché. Guarda las respuestas del upstream en disco y responde a peticiones repetidas sin preguntar otra vez a la aplicación.

Terminador TLS. Guarda los certificados, habla HTTPS, HTTP/2 y HTTP/3 con el navegador, y habla HTTP normal con la aplicación en una dirección privada.

Lo que no es: un servidor de aplicaciones. nginx no ejecuta tu código PHP, Python o Ruby. Pasa esas peticiones a un proceso que sí lo hace, y devuelve la respuesta.

Por eventos: por qué nginx soporta tantas conexiones

Cuando nginx arranca, un proceso maestro lee la configuración, abre los puertos de escucha y arranca unos pocos procesos worker, normalmente uno por núcleo de CPU (worker_processes auto). El maestro nunca sirve tráfico. Gestiona a los workers, y es lo que hace que un reload sea gradual, sin cortes.

Cada worker es de un solo hilo y ejecuta un bucle de eventos. Le pregunta al kernel, a través de epoll en Linux o kqueue en BSD y macOS, cuáles de sus conexiones tienen algo listo: una petición nueva, un cliente que puede recibir más bytes, un upstream que ha respondido. Hace un trozo corto de trabajo en cada conexión lista y sigue adelante; nada se queda esperando. Una conexión keep-alive inactiva entre peticiones, o un cliente móvil lento que envía su petición a trozos, le cuesta al worker unos pocos kilobytes de memoria y nada de CPU.

El modelo clásico de Apache es lo contrario. El MPM prefork da a cada conexión su propio proceso y el MPM worker su propio hilo, y ese proceso o hilo queda ocupado durante toda la duración de la conexión, esté activa o no. Diez mil conexiones keep-alive abiertas significan diez mil procesos o hilos, con la memoria y el cambio de contexto que eso implica. El MPM event, más reciente en Apache, aparca las conexiones keep-alive inactivas con un hilo oyente, lo que acorta la diferencia, pero cada petición activa sigue ocupando un hilo.

De ahí la regla que se sigue: un worker nunca debe bloquearse. El trabajo que realmente tarda, como ejecutar PHP o consultar una base de datos, ocurre en otro proceso, y nginx trata la respuesta como un evento más. La capacidad total es worker_processes × worker_connections, y cuando nginx hace de proxy, cada cliente usa dos de esos huecos: uno hacia el navegador, otro hacia el upstream.

El principio de nginx.conf: un maestro, un worker por núcleo, un bucle de eventos en cada uno

user www-data;
worker_processes auto;            # one worker per CPU core
error_log /var/log/nginx/error.log warn;

events {
    worker_connections 1024;      # per worker: clients + upstream connections
}

http {
    include      mime.types;
    sendfile     on;
    keepalive_timeout 65s;

    include /etc/nginx/conf.d/*.conf;   # one file per site
}

Para qué se usa nginx

En la práctica, nginx ocupa una de estas posiciones, a menudo varias a la vez.

Servir un sitio estático o un build de front-end. Un build de React, Vue o Astro, documentación, una landing page: archivos en disco y nginx delante, nada más.

Delante de PHP. WordPress, Laravel y la mayoría de las aplicaciones PHP se ejecutan como nginx más PHP-FPM. nginx sirve él mismo las imágenes, el CSS y el JavaScript, y pasa las peticiones .php a FPM por un socket Unix con fastcgi_pass.

Delante de un servidor de aplicaciones. Node.js, Python (Gunicorn, Uvicorn), Ruby (Puma) y servicios en Go escuchan en un puerto local. nginx termina HTTPS, absorbe clientes lentos almacenando en buffer la petición, y luego la reenvía con proxy_pass, de modo que la aplicación solo ve peticiones rápidas y completas.

Balanceo de carga entre varias instancias de una aplicación, tratado brevemente más abajo.

TLS y protocolos modernos para una aplicación que no tiene ninguno: certificados (la mayoría los obtiene con certbot y Let's Encrypt), HTTP/2 con http2 on; y HTTP/3 con listen 443 quic; en nginx 1.25 y versiones posteriores. Lo que cambian los protocolos está en HTTP/2 frente a HTTP/3.

Reglas en la puerta: redirecciones, rate limiting con limit_req, listas de permitidos y denegados por IP, compresión gzip y cabeceras de respuesta. Qué cabeceras enviar, y por qué, está en cabeceras de seguridad HTTP.

Un server block mínimo, línea a línea

Un bloque server es un sitio. nginx lo elige por el puerto en el que llegó la petición y por la cabecera Host, comparada con server_name. Dentro de él, los bloques location hacen match con rutas de URL. El bloque de abajo sirve un sitio estático y pasa /api/ a una aplicación en el puerto 3000.

listen es el puerto, una vez para IPv4 y otra para IPv6.

server_name enumera los nombres de host que responde este bloque. Una petición cuyo Host no coincide con ningún bloque va al servidor por defecto de ese puerto: el bloque marcado default_server, o si no, el primero que nginx leyó. Por eso un nombre de host desconocido que apunte a tu IP muestra algún otro sitio. Un bloque genérico que responda return 444; (cerrar la conexión) lo evita.

root es dónde se buscan los archivos: /about.html se convierte en /var/www/example/about.html.

try_files prueba el archivo, luego un directorio, luego el valor por defecto. Para una aplicación de una sola página, sustituye =404 por /index.html para que las rutas del lado del cliente carguen la aplicación.

El orden de coincidencia de location sorprende a la gente. Una coincidencia exacta (=) gana directamente. Si no, nginx recuerda el prefijo más largo que coincide, luego prueba las expresiones regulares (~, ~*) en el orden del archivo y toma la primera que coincida; solo si ninguna coincide se usa el prefijo recordado. Un prefijo ^~ salta el paso de las expresiones regulares. En este ejemplo, /api/logo.png lo sirve la expresión regular de imágenes, no /api/, que es la respuesta habitual a "por qué se ignora mi location".

expires fija Cache-Control: max-age y Expires en los activos con huella de versión. Elige los valores con la guía de Cache-Control.

Este bloque es HTTP normal. Añade el certificado con certbot, que edita el bloque por ti, y redirige el puerto 80 al 443.

/etc/nginx/conf.d/example.conf: un sitio estático con una API detrás

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    root  /var/www/example;
    index index.html;

    access_log /var/log/nginx/example.access.log;
    error_log  /var/log/nginx/example.error.log;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~* \.(?:css|js|woff2|png|jpg|webp|avif|svg)$ {
        expires 30d;
        access_log off;
    }

    location /api/ {
        proxy_pass http://127.0.0.1:3000;
        # plus the forwarded headers from the reverse proxy guide
    }
}

Instalar, comprobar, recargar: los comandos del día a día

Los paquetes de las distribuciones ponen el archivo principal en /etc/nginx/nginx.conf. Debian y Ubuntu guardan los sitios en /etc/nginx/sites-available/ y los activan con un enlace simbólico en sites-enabled/; RHEL, Rocky, Alpine y los paquetes de nginx.org leen /etc/nginx/conf.d/*.conf. Los logs van a /var/log/nginx/.

Dos costumbres evitan la mayoría de los cortes autoinfligidos. Ejecuta nginx -t antes de cada reload: analiza toda la configuración y señala el archivo y la línea de cualquier error. Y recarga en lugar de reiniciar. En un reload, el maestro arranca workers nuevos con la configuración nueva y deja que los antiguos terminen sus peticiones abiertas, así que no se pierde ninguna conexión; si la configuración nueva no carga, el maestro sigue con los workers antiguos y registra por qué.

nginx -T imprime la configuración completa tal como la ve nginx, con cada include expandido. Cuando un ajuste parece no tener efecto, suele estar sobrescrito en un archivo que olvidaste, y -T te muestra cuál.

Instalación del paquete, reload seguro y la configuración efectiva
# Debian / Ubuntu
sudo apt install nginx
nginx -v                                   # version; -V adds build options and modules

# check the syntax, then apply without dropping connections
sudo nginx -t && sudo systemctl reload nginx

# the whole effective configuration, includes expanded
sudo nginx -T | less

# which names and ports are configured
sudo nginx -T | grep -E '^\s*(server_name|listen)'

# watch errors while you test
sudo tail -f /var/log/nginx/error.log

nginx frente a Apache

Los dos son maduros, gratuitos y lo bastante rápidos para casi cualquier sitio. Las diferencias que de verdad deciden entre uno y otro:

Modelo de conexión. El bucle de eventos de nginx mantiene las conexiones lentas o inactivas casi gratis; Apache ata un proceso o hilo a cada petición activa y, fuera del MPM event, también a cada conexión inactiva. Con muchas conexiones keep-alive concurrentes, nginx usa mucha menos memoria.

Configuración. Con AllowOverride activado, Apache lee archivos .htaccess de cada directorio en la ruta de cada petición, así que un usuario puede cambiar reglas sin tocar la configuración del servidor. nginx no tiene equivalente: cada regla vive en la configuración central y se analiza una sola vez, al cargar. Eso es más rápido y más fácil de auditar, pero un .htaccess de WordPress o Laravel hay que reescribirlo como reglas location y try_files cuando migras.

PHP. Apache puede ejecutar PHP dentro de sus propios procesos con mod_php; nginx siempre habla con un pool de PHP-FPM separado. Las configuraciones actuales de Apache también suelen usar PHP-FPM, así que hoy es sobre todo una diferencia de valores por defecto.

Módulos. Apache carga módulos en tiempo de ejecución desde un catálogo muy amplio. nginx admite módulos dinámicos, pero cada uno tiene que compilarse contra la versión exacta de nginx que ejecutas, así que muchos extras significan compilar.

Archivos estáticos y proxy es donde nginx es más fuerte. Por eso un híbrido habitual pone nginx delante, sirviendo archivos y terminando TLS, con Apache detrás ejecutando una aplicación que depende de .htaccess.

El resumen honesto: empezando de cero, nginx es la opción por defecto para la mayoría de los equipos. Sustituir un Apache que funciona no va a hacer rápido un sitio lento. La parte lenta casi siempre es la aplicación o la distancia hasta el visitante, no el servidor web.

Proxy inverso, balanceador de carga y caché: la versión corta

Hacer de proxy es una sola directiva, proxy_pass, más las cabeceras que le dicen a la aplicación quién es realmente el cliente. Esas cabeceras, el soporte de WebSocket y proxy_cache están en la guía de proxy inverso, así que esta página no las repite.

El balanceo de carga añade un bloque upstream que nombra a los servidores. Por defecto es round robin. least_conn envía cada petición al servidor con menos conexiones activas, lo que va bien con peticiones de duración desigual, y ip_hash o hash mantiene a un cliente en el mismo servidor. La comprobación de salud en nginx de código abierto es pasiva: tras max_fails fallos dentro de fail_timeout, se salta a un servidor durante ese tiempo, y proxy_next_upstream reintenta la petición en otro. Las comprobaciones activas que piden una URL de forma periódica son una función de NGINX Plus. Para la imagen más amplia, consulta qué hace un balanceador de carga.

El cacheo funciona bien, con una carencia que conviene conocer de antemano: nginx de código abierto no tiene un comando para purgar una URL cacheada. Esperas a que la entrada caduque o borras archivos del directorio de caché en cada servidor. Esa carencia es una de las razones más habituales por las que los equipos mueven la caché pública a una CDN.

Tres servidores de aplicación detrás de un solo nginx

upstream app {
    least_conn;
    server 10.0.0.11:3000 max_fails=3 fail_timeout=10s;
    server 10.0.0.12:3000 max_fails=3 fail_timeout=10s;
    server 10.0.0.13:3000 backup;     # used only when the others are down
    keepalive 32;                     # reuse connections to the app
}

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

    location / {
        proxy_pass http://app;
        proxy_http_version 1.1;
        proxy_set_header Connection "";          # required for upstream keepalive
        proxy_next_upstream error timeout http_502;
        # plus the forwarded headers from the reverse proxy guide
    }
}

502, 504 y 413: qué te está diciendo nginx

El código de estado que ve el visitante es el resumen; el log de errores es la explicación. Cada uno de estos errores deja ahí una línea característica.

502 Bad Gateway significa que nginx no recibió una respuesta usable del upstream. connect() failed (111: Connection refused) dice que no hay nada escuchando en la dirección de proxy_pass: la aplicación está caída o en otro puerto. connect() to unix:/run/php/php8.3-fpm.sock failed (2: No such file or directory) es una ruta de socket de PHP-FPM que no coincide con la versión de PHP instalada, y (13: Permission denied) en la misma línea es un socket que el usuario de nginx no puede abrir. upstream prematurely closed connection significa que la aplicación se cayó o la mataron a mitad de la petición; revisa su propio log y los mensajes de falta de memoria del kernel. upstream sent too big header significa que las cabeceras de la respuesta, a menudo un montón de cookies, no caben en el buffer: sube proxy_buffer_size, o fastcgi_buffer_size para PHP. El diagnóstico completo, incluido el caso con una CDN delante, está en 502 Bad Gateway.

504 Gateway Timeout significa que el upstream aceptó la petición y no respondió dentro de proxy_read_timeout (o fastcgi_read_timeout), 60 segundos por defecto. El log dice upstream timed out (110: Connection timed out) while reading response header from upstream. Un timeout más largo tiene sentido para una exportación o un informe lentos ya conocidos; para páginas normales solo oculta una consulta lenta o un pool de workers lleno. Cada proxy de la cadena tiene su propio límite, así que un balanceador de carga o una CDN delante puede rendirse antes que nginx.

413 Request Entity Too Large significa que el cuerpo de la petición es mayor que client_max_body_size, que es 1 MB por defecto: el clásico fallo al subir una imagen o una copia de seguridad. El log dice client intended to send too large body. Sube el límite en el server o location que recibe las subidas en lugar de hacerlo global, y para PHP sube también upload_max_filesize y post_max_size a juego, o PHP rechazará lo que nginx dejó pasar.

Un 503 de nginx suele ser su propio limit_req o limit_conn rechazando una petición; eso y las demás causas están en 503 Service Unavailable. Y directory index of "/var/www/..." is forbidden es un 403 por un directorio sin archivo índice, tratado en 403 Forbidden.

Líneas del log de errores y la directiva a la que apunta cada una

# /var/log/nginx/error.log (abridged)
connect() failed (111: Connection refused) while connecting to upstream        -> 502
upstream prematurely closed connection while reading response header          -> 502
upstream sent too big header while reading response header from upstream       -> 502
upstream timed out (110: Connection timed out) while reading response header   -> 504
client intended to send too large body: 52428800 bytes                         -> 413

# the matching fixes, in the server or location that needs them
client_max_body_size 64m;     # PHP: raise upload_max_filesize and post_max_size too
proxy_read_timeout   300s;    # only for a known slow endpoint
proxy_buffer_size    16k;     # large response headers and cookies
proxy_buffers        8 16k;

Cuándo poner una CDN delante de nginx

Un solo nginx sirve mucho tráfico. Lo que no puede cambiar es dónde está. Un visitante en otro país espera en cada ida y vuelta a tu único datacenter; un pico de tráfico formado en su mayoría por peticiones repetidas sigue cayendo en tu única máquina; un ataque dirigido a tu IP llega al servidor que ejecuta tu sitio. Esos son los momentos para añadir una CDN: tu audiencia está lejos del servidor, el tráfico cacheable es una parte grande de la carga, necesitas purgar la caché en más de una máquina, o el origen debería dejar de ser accesible directamente.

Mantienes nginx; cambia su trabajo. La CDN se convierte en la puerta de entrada pública y nginx se convierte en el origen, sirviendo archivos y enrutando hacia la aplicación mientras el edge se encarga de la distancia, el cacheo y el filtrado. Tres cosas que ajustar en el lado de nginx. Envía cabeceras Cache-Control correctas, porque el edge las sigue. Restaura la IP del visitante con el módulo realip (set_real_ip_from para las direcciones de la CDN, real_ip_header para la cabecera que envía), o cada línea de log y cada zona de limit_req verá al edge en lugar de al visitante. Y permite que solo la CDN alcance el origen, para que los ataques no puedan evitarla.

El edge de CDN.com.tr se ejecuta sobre el propio nginx: su configuración aplica el tamaño de archivo de tu paquete como client_max_body_size, así que una subida que pasa por la CDN necesita que ese límite también sea lo bastante grande en tu lado. Delante de tu origen, la red de edge, con servidores en Turquía y en el extranjero, cachea según reglas de entrega por ruta y purga al instante por ruta exacta o carpeta desde el panel, la línea de comandos cdnctl o la API REST. Un WAF, protección DDoS, protección contra bots con un desafío JavaScript, reglas por país e IP y rate limiting se ejecutan antes de que una petición llegue a tu servidor, y los certificados de Let's Encrypt se emiten y renuevan para cada dominio conectado.

Dos detalles hacen el cambio más fácil de verificar. Las páginas que ya están en la caché del edge siguen sirviéndose mientras tu origen está caído, y la cabecera de respuesta X-Proxy-Cache-MT muestra HIT o MISS, así que puedes saber si una petición llegó siquiera a tu nginx. El edge también envía SNI al origen, de modo que un nginx con varios bloques server HTTPS presenta el certificado correcto.

Preguntas frecuentes sobre nginx

¿Cómo se pronuncia nginx?

"Engine-x". Se usan tanto nginx como NGINX; la forma en minúsculas es el nombre del proyecto de código abierto y de su binario.

¿Es gratis nginx?

El nginx de código abierto de nginx.org es gratuito bajo una licencia BSD de dos cláusulas, también para uso comercial. NGINX Plus es una suscripción de pago de F5 que añade comprobaciones de salud activas, una API para purgar la caché y cambiar los upstreams en tiempo de ejecución, un panel de estado en vivo y soporte.

¿nginx es un servidor web o un proxy inverso?

Las dos cosas, a menudo en la misma configuración. Un location con root sirve archivos de disco, uno con proxy_pass o fastcgi_pass pasa la petición a una aplicación. La mayoría de los sitios hacen las dos cosas: los activos estáticos directamente, todo lo dinámico a través del proxy.

¿Es nginx mejor que Apache?

Para archivos estáticos, proxy y un gran número de conexiones concurrentes usa menos memoria, y es la opción habitual para configuraciones nuevas. Apache encaja mejor cuando dependes de .htaccess o de un módulo exclusivo de Apache. Para un sitio típico, ninguno de los dos servidores web es el cuello de botella; lo son la aplicación y la distancia hasta los visitantes.

¿Por qué mi dominio muestra otro sitio en nginx?

Ningún server_name coincidió con la cabecera Host de la petición, así que nginx usó el servidor por defecto de ese puerto: el bloque marcado default_server, o el primero que cargó. Revisa los nombres con nginx -T | grep server_name, y añade un bloque genérico que devuelva 444 para que los nombres de host desconocidos no reciban nada.

¿Sigo necesitando nginx si uso una CDN?

Normalmente sí, como origen: algo tiene que seguir sirviendo archivos y enrutando peticiones a tu aplicación, y la CDN se abastece de ahí. En las aplicaciones en contenedor de CDN.com.tr puedes saltártelo para publicar, porque el edge llega a cada aplicación a través de una ruta interna de la plataforma sin ningún contenedor de proxy inverso de por medio.