Lista Esencial de Optimización de Rendimiento de Nginx para Sitios Web de Alto Tráfico

Una lista práctica de verificación de rendimiento de Nginx para trabajadores, conexiones, búferes, almacenamiento en caché, compresión, registros, tiempos de espera, TLS y archivos estáticos.

Lista Esencial de Optimización de Rendimiento de Nginx para Sitios Web de Alto Tráfico

La optimización del rendimiento de Nginx es más fácil cuando la tratas como una lista de verificación, no como un juego de adivinanzas. Comienza con los límites que determinan cuánto tráfico puede aceptar Nginx, luego avanza hacia el almacenamiento en búfer, el almacenamiento en caché, la compresión, el registro, los tiempos de espera, TLS y los servicios backend detrás de él.

No pegues todas las directivas aquí en producción de una vez. Una buena lista de verificación de optimización de rendimiento de Nginx te ayuda a decidir qué verificar, por qué es importante y qué puede salir mal si te excedes. La configuración correcta para un sitio de documentación mayormente estático no es la misma que para una API de sondeo largo o un servicio de carga de archivos.

1. Optimizar Procesos de Trabajo y Conexiones

Nginx aprovecha un modelo de proceso maestro-trabajador. El proceso maestro lee la configuración y gestiona los procesos de trabajo, que manejan las solicitudes reales de los clientes. Configurar correctamente estos puede mejorar drásticamente la concurrencia y la utilización de recursos.

worker_processes

Esta directiva determina cuántos procesos de trabajo generará Nginx. Generalmente, establecerlo en auto permite que Nginx detecte el número de núcleos de CPU y genere un número igual de procesos de trabajo, lo cual es una práctica común recomendada.

worker_connections

Define el número máximo de conexiones simultáneas que un solo proceso de trabajo puede abrir. Esta configuración, junto con worker_processes, dicta el total teórico de conexiones concurrentes que Nginx puede manejar (worker_processes * worker_connections).

multi_accept

Permite que un proceso de trabajo acepte múltiples conexiones nuevas a la vez, evitando posibles cuellos de botella bajo alta carga.

# /etc/nginx/nginx.conf

worker_processes auto; # Generalmente se establece en 'auto' o el número de núcleos de CPU

events {
    worker_connections 1024; # Ajustar según la capacidad del servidor y la carga esperada
    multi_accept on;
}

Consejo: Si la CPU está constantemente alta, aumentar worker_connections no lo solucionará por sí solo. Primero confirma si la CPU proviene de los handshakes TLS, la compresión, el registro, el enrutamiento con muchas expresiones regulares o la aplicación upstream.

2. Gestión Eficiente de Conexiones

Optimizar cómo Nginx maneja las conexiones de red puede reducir la sobrecarga y mejorar la capacidad de respuesta.

keepalive_timeout

Especifica cuánto tiempo permanecerá abierta una conexión keep-alive del cliente. Reutilizar conexiones reduce la sobrecarga de establecer nuevas conexiones TCP y handshakes SSL. Un valor común es de 15 a 65 segundos, dependiendo de la interactividad de tu aplicación.

sendfile

Habilita la transferencia directa de datos entre descriptores de archivo, evitando el almacenamiento en búfer del espacio de usuario. Esto mejora significativamente el rendimiento al servir archivos estáticos.

tcp_nopush

Funciona con sendfile. Nginx intenta enviar el encabezado HTTP y el inicio del archivo en un solo paquete. Después de eso, envía datos en paquetes completos. Esto reduce el número de paquetes enviados.

tcp_nodelay

Indica a Nginx que envíe datos tan pronto como estén disponibles, sin almacenarlos en búfer. Esto es beneficioso para aplicaciones interactivas donde la baja latencia es más crítica que maximizar el rendimiento (por ejemplo, aplicaciones de chat o actualizaciones en tiempo real).

http {
    keepalive_timeout 65; # Conexiones keep-alive durante 65 segundos
    sendfile on;
    tcp_nopush on; # Requiere sendfile activado
    tcp_nodelay on; # Útil para proxy de contenido dinámico
}

3. Optimización de Búferes

Nginx utiliza búferes para manejar las solicitudes de los clientes y las respuestas de los servidores upstream (como los servidores de aplicaciones). Dimensionar correctamente estos búferes puede evitar E/S de disco innecesarias, reducir el uso de memoria y mejorar el rendimiento.

Búferes del Cliente

  • client_body_buffer_size: Tamaño del búfer para los cuerpos de las solicitudes del cliente. Si un cuerpo excede esto, se escribe en un archivo temporal.
  • client_header_buffer_size: Tamaño del búfer para la primera línea y los encabezados de una solicitud del cliente.
  • large_client_header_buffers: Define el número y tamaño de los búferes más grandes para leer los encabezados de las solicitudes del cliente. Útil para solicitudes con muchas cookies o encabezados referer largos.

Búferes de Proxy (para configuraciones de proxy inverso)

  • proxy_buffers: El número y tamaño de los búferes utilizados para leer las respuestas del servidor proxy.
  • proxy_buffer_size: El tamaño del primer búfer para leer la respuesta. Generalmente más pequeño, ya que a menudo solo contiene encabezados.
  • proxy_busy_buffers_size: La cantidad máxima de búferes de respuesta que pueden estar en estado 'ocupado' (enviándose activamente al cliente) en un momento dado.

Búferes FastCGI (para PHP-FPM, etc.)

  • fastcgi_buffers: El número y tamaño de los búferes utilizados para leer las respuestas del servidor FastCGI.
  • fastcgi_buffer_size: El tamaño del primer búfer para leer la respuesta.
http {
    # Búferes del Cliente
    client_body_buffer_size 1M; # Ajustar según el tamaño esperado del cuerpo de la solicitud (por ejemplo, cargas de archivos)
    client_header_buffer_size 1k;
    large_client_header_buffers 4 8k; # 4 búferes, cada uno de 8KB

    # Búferes de Proxy (si Nginx actúa como proxy inverso)
    proxy_buffers 8 16k; # 8 búferes, cada uno de 16KB
    proxy_buffer_size 16k; # Primer búfer de 16KB
    proxy_busy_buffers_size 16k; # Máximo 16KB de búferes ocupados

    # Búferes FastCGI (si Nginx trabaja con PHP-FPM)
    fastcgi_buffers 16 16k; # Punto de partida para muchas aplicaciones PHP-FPM
    fastcgi_buffer_size 16k; # Primer búfer de 16KB
}

Advertencia: Establecer búferes demasiado pequeños puede provocar E/S de disco y degradación del rendimiento. Configurarlos demasiado grandes puede consumir memoria excesiva. Encuentra un equilibrio mediante pruebas.

4. Implementar Estrategias Robustas de Almacenamiento en Caché

El almacenamiento en caché es una de las formas más efectivas de mejorar el rendimiento y reducir la carga en tus servidores backend. Nginx puede servir como un potente caché de contenido.

proxy_cache_path

Define la ruta al directorio de caché, su tamaño, el número de niveles de subdirectorios y cuánto tiempo permanecen los elementos inactivos en la caché.

proxy_cache

Activa el almacenamiento en caché para un bloque de location dado, haciendo referencia a la zona definida en proxy_cache_path.

proxy_cache_valid

Establece el tiempo durante el cual Nginx debe almacenar en caché las respuestas con códigos de estado HTTP específicos.

proxy_cache_revalidate

Cuando está habilitado, Nginx utilizará los encabezados If-Modified-Since e If-None-Match para revalidar el contenido en caché con el backend, reduciendo el uso de ancho de banda.

proxy_cache_use_stale

Indica a Nginx que sirva contenido en caché obsoleto si el servidor backend está caído, no responde o experimenta errores. Esto mejora enormemente la disponibilidad.

expires

Establece los encabezados Cache-Control y Expires para el almacenamiento en caché del lado del cliente de archivos estáticos. Esto minimiza las solicitudes repetidas a Nginx.

http {
    # Define una zona de caché de proxy en el bloque http
    proxy_cache_path /var/cache/nginx/my_cache levels=1:2 keys_zone=my_cache:10m inactive=60m max_size=10g;

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://my_upstream_backend;
            proxy_cache my_cache; # Habilita el almacenamiento en caché para esta ubicación
            proxy_cache_valid 200 302 10m; # Almacena en caché respuestas exitosas durante 10 minutos
            proxy_cache_valid 404 1m; # Almacena en caché 404s durante 1 minuto
            proxy_cache_revalidate on;
            proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
            add_header X-Cache-Status $upstream_cache_status; # Ayuda con la depuración
        }

        # Almacena en caché archivos estáticos en el navegador por un período más largo
        location ~* \.(jpg|jpeg|gif|png|css|js|ico|woff|woff2|ttf|svg|eot)$ {
            expires 30d; # Almacena en caché durante 30 días
            add_header Cache-Control "public, no-transform";
            # Para archivos estáticos, considera servirlos directamente desde Nginx si no están proxy
            root /var/www/html;
        }
    }
}

5. Habilitar Compresión Gzip

Comprimir las respuestas antes de enviarlas a los clientes puede reducir significativamente el uso de ancho de banda y mejorar los tiempos de carga de la página, especialmente para contenido basado en texto.

gzip on

Activa la compresión gzip.

gzip_comp_level

Establece el nivel de compresión (1-9). El nivel 1 es el más rápido con menos compresión; el nivel 9 es el más lento con la máxima compresión. El nivel 6 suele ofrecer un buen equilibrio.

gzip_types

Especifica los tipos MIME que deben comprimirse. Incluye tipos comunes de texto, CSS, JavaScript y JSON.

gzip_min_length

Establece la longitud mínima de una respuesta (en bytes) para la cual se debe habilitar la compresión. Los archivos pequeños no se benefician mucho e incluso podrían ser más lentos debido a la sobrecarga de compresión.

gzip_proxied

Indica a Nginx que comprima las respuestas incluso si son proxy. any es un valor común.

gzip_vary

Agrega el encabezado Vary: Accept-Encoding a las respuestas, informando a los proxies que la respuesta puede diferir según el encabezado de solicitud Accept-Encoding.

http {
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6; # Nivel de compresión 1-9 (6 es un buen equilibrio)
    gzip_buffers 16 8k; # 16 búferes, cada uno de 8KB
    gzip_http_version 1.1; # Versión HTTP mínima para compresión
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
    gzip_min_length 1000; # Solo comprimir respuestas mayores de 1KB
}

6. Optimizar el Registro

Si bien los registros son esenciales para la monitorización y la resolución de problemas, un registro excesivo o no optimizado puede introducir una E/S de disco significativa, especialmente en sitios de alto tráfico.

access_log

  • Deshabilitar para activos estáticos: Para contenido estático muy accedido (imágenes, CSS, JS), deshabilitar access_log puede ahorrar mucha E/S.
  • Almacenamiento en búfer: Nginx puede almacenar en búfer las entradas de registro en memoria antes de escribirlas en disco, reduciendo la frecuencia de las escrituras en disco. Los parámetros buffer y flush se utilizan aquí.

error_log

Establece el nivel de registro apropiado (crit, error, warn, info, debug). Para producción, warn o error suele ser suficiente para capturar problemas críticos sin inundar los registros.

http {
    server {
        # Registro de acceso predeterminado para contenido dinámico
        access_log /var/log/nginx/access.log main;

        location ~* \.(jpg|jpeg|gif|png|css|js|ico|woff|woff2|ttf|svg|eot)$ {
            access_log off; # Deshabilitar el registro para archivos estáticos comunes
            expires 30d;
        }
    }

    # Ejemplo de registro de acceso con búfer para el contexto HTTP principal
    # access_log /var/log/nginx/access.log main buffer=16k flush=5s;
    error_log /var/log/nginx/error.log warn; # Solo registrar advertencias y superiores
}

7. Ajustar Tiempos de Espera

Los tiempos de espera configurados adecuadamente evitan que Nginx mantenga conexiones inactivas demasiado tiempo, liberando recursos.

Tiempos de Espera del Lado del Cliente

  • client_body_timeout: Cuánto tiempo espera Nginx a que un cliente envíe el cuerpo de la solicitud.
  • client_header_timeout: Cuánto tiempo espera Nginx a que un cliente envíe el encabezado de la solicitud.
  • send_timeout: Cuánto tiempo espera Nginx a que un cliente acepte la respuesta después de enviarla.

Tiempos de Espera de Proxy/FastCGI (si corresponde)

  • proxy_connect_timeout: Tiempo de espera para establecer una conexión con un servidor proxy.
  • proxy_send_timeout: Tiempo de espera para transmitir una solicitud al servidor proxy.
  • proxy_read_timeout: Tiempo de espera para leer una respuesta del servidor proxy.
http {
    client_body_timeout 15s; # El cliente tiene 15 segundos para enviar el cuerpo
    client_header_timeout 15s; # El cliente tiene 15 segundos para enviar los encabezados
    send_timeout 15s; # Nginx tiene 15 segundos para enviar la respuesta al cliente

    # Para escenarios de proxy
    proxy_connect_timeout 5s; # 5 segundos para conectarse al upstream
    proxy_send_timeout 15s; # 15 segundos para enviar la solicitud al upstream
    proxy_read_timeout 15s; # 15 segundos para leer la respuesta del upstream

    # Para escenarios FastCGI
    fastcgi_connect_timeout 5s;
    fastcgi_send_timeout 15s;
    fastcgi_read_timeout 15s;
}

8. Optimización SSL/TLS

Para sitios habilitados para HTTPS, optimizar la configuración SSL/TLS es crucial para reducir la sobrecarga de la CPU y mejorar el rendimiento del handshake.

ssl_session_cache y ssl_session_timeout

Habilitar el almacenamiento en caché de sesiones SSL para evitar el costoso handshake TLS completo para conexiones posteriores del mismo cliente.

ssl_protocols y ssl_ciphers

Usar protocolos TLS modernos como TLSv1.2 y TLSv1.3. Ten cuidado con las cadenas de cifrado copiadas: los cifrados TLS 1.3 no se controlan de la misma manera que los conjuntos de cifrado TLS más antiguos, y los valores predeterminados de la distribución suelen ser más seguros que los ejemplos obsoletos de guías antiguas.

ssl_stapling

Habilita el grapado OCSP, donde Nginx obtiene periódicamente la respuesta OCSP de la CA y la "grapa" al handshake SSL/TLS. Esto reduce la latencia del lado del cliente al evitar una consulta OCSP separada.

server {
    listen 443 ssl;
    ssl_certificate /etc/nginx/ssl/your_domain.crt;
    ssl_certificate_key /etc/nginx/ssl/your_domain.key;

    ssl_session_cache shared:SSL:10m; # Caché compartida para 10MB de datos de sesión
    ssl_session_timeout 10m; # Las sesiones expiran después de 10 minutos

    ssl_protocols TLSv1.2 TLSv1.3; # Usar protocolos modernos y seguros
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
    ssl_prefer_server_ciphers on;

    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 1.1.1.1 8.8.8.8 valid=300s; # Usar resolutores aprobados para tu entorno
    resolver_timeout 5s;
}

9. Caché de Archivos Abiertos

Nginx puede almacenar en caché los descriptores de archivos para archivos accedidos con frecuencia, reduciendo la necesidad de llamadas al sistema repetidas para abrir y cerrar archivos.

open_file_cache

Habilita la caché, especificando el número máximo de elementos y cuánto tiempo permanecen los elementos inactivos.

open_file_cache_valid

Establece con qué frecuencia la caché debe verificar la validez de sus elementos.

open_file_cache_min_uses

Especifica el número mínimo de veces que un archivo debe ser accedido dentro del tiempo inactive para permanecer en la caché.

open_file_cache_errors

Determina si Nginx debe almacenar en caché los errores al abrir archivos.

http {
    open_file_cache max=100000 inactive=60s; # Almacenar en caché hasta 100,000 descriptores de archivo durante 60s
    open_file_cache_valid 80s; # Verificar validez cada 80 segundos
    open_file_cache_min_uses 1; # Almacenar en caché archivos usados al menos una vez
    open_file_cache_errors on; # Almacenar en caché errores relacionados con la apertura de archivos
}

10. Validar con Señales de Tráfico Real

El último elemento de la lista de verificación es la medición. Antes de un cambio, captura una pequeña línea base: latencia de solicitud, tasa de 5xx, conexiones activas, CPU, memoria, E/S de disco, rendimiento de red y tiempo de respuesta del upstream. Después del cambio, compara los mismos números.

Para un proxy inverso, $request_time y $upstream_response_time son especialmente útiles. Si ambos aumentan juntos, el backend probablemente es lento. Si $request_time es alto mientras que el tiempo del upstream es bajo o vacío, observa la velocidad de carga del cliente, el tiempo de transferencia de la respuesta, el almacenamiento en búfer, la compresión o la entrega de archivos estáticos. Si ninguna métrica explica el problema, verifica el registro de errores y el sistema operativo.

La secuencia de ajuste más segura es simple: probar la configuración con nginx -t, recargar en lugar de reiniciar cuando sea posible, monitorear los registros y retroceder rápidamente si la latencia o los errores se mueven en la dirección incorrecta. Nginx puede manejar mucho tráfico, pero solo cuando sus límites, el kernel y la aplicación upstream están de acuerdo entre sí.