Con nginx sendfile y tcp_nopush Entrego archivos estáticos mediante «zero-copy» desde el sistema de archivos al socket, lo que reduce notablemente tanto la carga de la CPU como el número de paquetes. Si se configuran correctamente, ambas directivas aumentan la eficiencia de la transmisión, reducen la sobrecarga y sientan las bases para una optimización adecuada de Nginx en lo que respecta a los recursos y las descargas.
Puntos centrales
- Copia cero mediante sendfile: menos copias, mayor rendimiento
- tcp_nopush almacena paquetes en búfer: tramas más grandes, menos sobrecarga
- Combinación cuenta: sendfile + tcp_nopush + tcp_nodelay
- Casos prácticos Priorizar: recursos estáticos, descargas de gran tamaño
- Pruebas En NFS/SMB: medir el impacto; si es necesario, desactivar sendfile
Por qué «sendfile» mejora tanto el rendimiento de NGINX
Activo sendfile, ya que el núcleo puede enviar archivos directamente a través de la pila de red, sin necesidad de pasar por operaciones de copia adicionales en el espacio de usuario. Esta ruta de «zero-copy» reduce los cambios de contexto y ahorra ciclos de CPU, especialmente cuando muchos clientes simultáneos acceden a contenidos estáticos. Los archivos de gran tamaño, como imágenes, CSS, JavaScript o archivos comprimidos, se benefician de ello, ya que la transferencia de datos se realiza de forma más uniforme y con menos sobrecarga. Las cachés del sistema también funcionan de manera más eficiente, ya que se producen menos movimientos de memoria y el núcleo controla la ruta de los datos. La mejora se aprecia con mayor claridad en los sistemas de archivos locales, por lo que realizo las mediciones allí primero, antes de aplicarlas a configuraciones más exóticas.
Qué hace exactamente tcp_nopush y cuándo destaca
Con tcp_nopush Pido al sistema que envíe los paquetes TCP solo cuando estén completamente llenos, en lugar de enviar segmentos pequeños antes de tiempo. En Linux, esto se corresponde con TCP_CORK; en FreeBSD, con TCP_NOPUSH; y, en ambos casos, el número de paquetes se reduce de forma apreciable. Esta directiva no reduce la latencia al mínimo, sino que busca una mejor relación entre los datos útiles y la sobrecarga. Utilizo tcp_nopush específicamente con archivos estáticos, ya que es ahí donde los flujos de datos contiguos aportan mayores ganancias de eficiencia. Sin sendfile, tcp_nopush no surte efecto, por lo que siempre integro ambas configuraciones conjuntamente.
sendfile y tcp_nopush como dúo: así es como establezco las bases
La combinación de sendfile y tcp_nopush reduce las copias y agrupa los paquetes, lo que permite que un servidor por núcleo de CPU pueda gestionar un número significativamente mayor de transferencias en paralelo. Yo configuro ambas opciones en el nivel de contexto http y, a menudo, añado tcp_nodelay para que el último resto de un flujo se transmita sin tiempo de espera. Sigue siendo importante realizar pruebas con tráfico real, ya que el tamaño de los paquetes, la MTU y los clientes varían, y el mejor equilibrio puede variar ligeramente en función de la carga de trabajo. Para los directorios estáticos, suele bastar con la activación global, mientras que en el caso de las rutas de respuesta dinámicas presto atención al efecto que tiene. Esta combinación proporciona una base sólida para otras medidas de optimización de nginx que se aplicarán posteriormente.
| directiva | Propósito | Efectos típicos | Dependencia |
|---|---|---|---|
| sendfile activado | Copia sin transferencia de archivos al socket | Menor carga de la CPU, mayor rendimiento | Un sistema de archivos local es la opción ideal |
| tcp_nopush activado | Llenar los paquetes, reducir los gastos generales | Menos segmentos por archivo | Solo funciona con sendfile |
| tcp_nodelay on | Enviar los últimos bytes sin esperar | Conclusión rápida de la transferencia | Se ha añadido tcp_nopush |
Así es como interactúan tcp_nodelay y tcp_nopush
Activo tcp_nopush, para enviar el inicio de una transferencia en paquetes más grandes, y habilito al mismo tiempo tcp_nodelay para que la finalización no se atasque. Ambos ajustes afectan a diferentes fases del flujo y no interfieren entre sí cuando NGINX entrega archivos mediante sendfile. Especialmente cuando hay muchos archivos pequeños, tcp_nodelay evita que el cliente espere innecesariamente debido a pequeños datos restantes. Primero pruebo la combinación en el entorno de staging, observo los RTT y los tamaños de los segmentos, y los comparo con las métricas en producción. De esta forma, me aseguro de que la transmisión sea eficiente al principio y rápida al final.
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
}
Situaciones típicas de aplicación: en las que las directivas tienen un gran impacto
Para los grandes Descargas Al igual que con los vídeos, los archivos o las imágenes ISO, la ruta «zero-copy» del núcleo reduce considerablemente el tiempo de CPU por transferencia. En configuraciones similares a las de una CDN, con numerosos archivos CSS, JS y de fuentes, tcp_nopush ahorra segmentos y, de este modo, aumenta el ancho de banda útil por socket. En las páginas de WordPress con un buen almacenamiento en caché, la mayoría de las solicitudes se dirigen a recursos estáticos, por lo que el efecto se nota allí muy rápidamente. También se benefician los artefactos de compilación, las imágenes de contenedores o los instaladores, siempre que se encuentren localmente y no se accedan a través de un sistema de archivos de red inestable. Quien prevea picos de carga, conseguirá con este dúo sacar mucho más partido al hardware disponible.
Ejemplo práctico: NGINX para WordPress con almacenamiento en caché y recursos
En las configuraciones de WordPress, utilizo sendfile, tcp_nopush y tcp_nodelay a nivel global, sirvo los recursos estáticos directamente y mantengo PHP-FPM bien separado para las rutas dinámicas. Añado encabezados de caché adecuados para imágenes, CSS y JavaScript, de modo que los navegadores realicen menos idas y venidas. Cuando sirvo respuestas de tipo streaming, tengo en cuenta la interacción con el almacenamiento en búfer y compruebo cómo afectan los tamaños de los fragmentos a la latencia y al rendimiento; para ello, resulta útil la descripción general sobre Respuesta en secuencias. Para los contenidos de texto, aplico compresión sin comprimir innecesariamente los archivos binarios. De este modo, el flujo de solicitudes se mantiene estable, la CPU no se sobrecarga y el tiempo hasta el primer byte es breve.
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
gzip on;
gzip_types text/css application/javascript image/svg+xml;
server {
listen 80;
server_name blog.example.com;
root /var/www/blog;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~* \.(jpg|jpeg|png|gif|css|js|ico|svg|woff2?)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}
}
}
Cuándo desactivo «sendfile» de forma deliberada
Cambio sendfile cuando los archivos se encuentran en NFS, SMB o sistemas de archivos distribuidos, que en mi prueba ofrecen un rendimiento inferior. Algunos controladores o latencias en la ruta de almacenamiento anulan la ventaja de «zero-copy», por lo que las mediciones son determinantes. Ante peculiaridades esporádicas de la red, primero desactivo tcp_nopush para delimitar los efectos, antes de cuestionar el propio sendfile. También pueden ser motivos para cambiar temporalmente a la ruta clásica de lectura y escritura algunos errores inusuales del núcleo o pilas más antiguas. Lo importante es implementar los cambios de forma gradual y respaldarlos con métricas.
Fuentes de error que tengo en cuenta
Primero compruebo si tcp_nopush está activada por error, mientras que sendfile permanece desactivada, ya que en ese caso la configuración no surte efecto. En el caso de las rutas dinámicas, compruebo si el almacenamiento en búfer adicional aumenta la latencia y sopeso las ventajas frente al tiempo de respuesta. En redes de alta latencia, mido si los paquetes más grandes realmente ayudan o si debo ajustar el tamaño de los segmentos y el «keep-alive». La configuración de la MTU y las funciones de descarga de la tarjeta de red también pueden influir notablemente en el resultado. Los registros limpios, las muestras pcap y las métricas del sistema correlacionadas me permiten identificar rápidamente dónde debo realizar ajustes.
Un enfoque integral del rendimiento de NGINX: otros ajustes posibles
Además de sendfile Merece la pena establecer el número adecuado de worker_processes y worker_connections para no limitar artificialmente los sockets. En Linux utilizo epoll y me aseguro de que haya suficientes descriptores de archivo para que los picos de carga no provoquen cuellos de botella. Para los contenidos de texto, activo gzip o Brotli y compruebo si el nivel de compresión supone una carga razonable para la CPU. A nivel de transporte, mantengo las conexiones abiertas durante más tiempo y optimizo el «keep-alive», tal y como indica la guía Ajuste de Keep Alive ofrece pautas prácticas. TLS, la reutilización de sesiones y HTTP/2 o HTTP/3 completan la configuración y permiten un alto grado de paralelismo con una latencia moderada.
Limitaciones y casos especiales: TLS, HTTP/2/3 y el uso de proxies
Tengo en cuenta que sendfile Técnicamente, solo es aplicable a rutas de archivos sin cifrar o a funciones específicas del núcleo. En el TLS clásico, NGINX cifra los bytes en el espacio de usuario, por lo que se pierde la ventaja de «zero-copy»; los núcleos modernos pueden trasladar parcialmente el cifrado al núcleo, lo que recupera ese efecto, pero no está disponible en todas las configuraciones. En HTTP/2 Los datos se encuentran en tramas, varias respuestas comparten una conexión TCP y NGINX reagrupa activamente los bytes; en este caso, sendfile tiene menos relevancia. HTTP/3 se basa en UDP/QUIC y sigue otras reglas, por lo que consigo una mayor eficiencia más bien mediante búferes, control de congestión y un tamaño de fragmentos adecuado. Como Proxy inverso sendfile solo se aplica cuando realmente sirvo archivos desde el sistema de archivos local; respuestas de proxy_pass o fastcgi_pass De todos modos, pasan por el espacio de usuario. Por eso separo estrictamente los recursos de la ruta dinámica, para aprovechar al máximo el método «zero-copy».
Entender bien la compresión: gzip/Brotli frente a gzip_static
Cada vez que NGINX comprime contenidos sobre la marcha, tiene que leer el archivo, procesarlo y escribir el resultado, con lo que se pierde sendfile su ventaja. Por eso, para los recursos estáticos, utilizo, siempre que sea posible, precomprimidos Archivos (por ejemplo, .gz o .br) y los envío directamente. De este modo se mantiene la ruta «zero-copy», ya que NGINX puede transferir el archivo precomprimido como cualquier otro recurso. De este modo, para contenidos con gran cantidad de texto que rara vez se modifican, consigo un ahorro de CPU y un rendimiento estable sin perder tiempo de transmisión. En el caso de los archivos binarios y los formatos ya comprimidos, me ahorro cualquier compresión en tiempo de ejecución; aquí lo que cuenta es el rendimiento puro de E/S, y «sendfile» junto con «tcp_nopush» sacan partido a sus puntos fuertes.
AIO, directio y Page Cache: patrones para archivos pequeños y grandes
Combino sendfile con E/S asíncrona y acceso directo al disco, para obtener el mejor rendimiento en función del tamaño del archivo. Los archivos pequeños y medianos se benefician de la caché de páginas del núcleo y permanecen en la ruta de `sendfile`. Por el contrario, los archivos muy grandes pueden desplazar la caché; en ese caso, los leo específicamente con directio fuera de la caché y utilizo hilos AIO. De este modo, alivio la carga de la memoria y mantengo baja la latencia para otras solicitudes. Un patrón típico es el siguiente:
http {
# Ruta estándar: copia cero desde la caché de páginas
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# Archivos grandes: sin pasar por la caché y lectura asíncrona
aio threads;
directio 4m; # solo se aplica a archivos >= 4 MiB
output_buffers 1 512k; #: búferes para rutas directio
sendfile_max_chunk 1m; #: equidad bajo carga elevada
}
Con esta escalabilidad, los activos pequeños siguen siendo extremadamente eficientes, mientras que las transferencias muy grandes no saturan la memoria RAM. Importante: directio desactiva la ruta sendfile para los archivos afectados, tal y como yo pretendo para el caso de uso de archivos de gran tamaño.
Equidad y control del flujo bajo carga
En momentos de alta carga, quiero evitar que un solo flujo acapare la CPU o el socket. Utilizo sendfile_max_chunk, para que NGINX devuelva el kernel tras una cantidad definida de bytes y deje espacio para otras conexiones. Para el control del ancho de banda, son útiles limit_rate y limit_rate_after, por ejemplo, para limitar las descargas masivas, mientras que los elementos de la interfaz de usuario siguen funcionando con fluidez. Con postpone_output Controlo a partir de qué tamaño de respuesta NGINX comienza a enviar datos; en combinación con tcp_nopush, me aseguro así de que los paquetes se fragmenten correctamente. Además, presto atención a lingering_close, para que los paquetes restantes se transmitan correctamente y el socket no se cierre de forma brusca.
Sistemas de archivos, lectura anticipada y rutas de almacenamiento
Porque sendfile Cuando se utilizan las cachés de página, el sistema de archivos subyacente desempeña un papel fundamental. Compruebo los valores de lectura anticipada y los mantengo de tal forma que las operaciones de lectura secuencial de archivos grandes no se vean interrumpidas, sin que ello suponga desplazar activos más pequeños. En ext4 o xfs Observo lo bien que se adaptan el prefetching y el programador de E/S a mi patrón de rendimiento. En los sistemas de archivos de red (NFS/SMB), realizo pruebas exhaustivas de rsize/wsize, el almacenamiento en caché y las latencias, ya que incluso pequeñas desviaciones neutralizan la ventaja del «zero-copy». Mi regla sigue siendo la misma: primero aprovechar al máximo las rutas locales, luego ajustar con cuidado las pilas externas y dar siempre prioridad a los datos de medición frente a la intuición.
Ajustar de forma pragmática la pila de red y la descarga de la tarjeta de red
Para un gran número de conexiones, confío en el ajuste automático de los búferes de las pilas modernas, aunque, si es necesario, ajusto los búferes de envío y recepción. Las descargas de la tarjeta de red, como TSO, GSO y GRO, reducen notablemente la carga de la CPU; sin embargo, en las mediciones procedo con cautela, ya que las capturas de paquetes pueden verse distorsionadas por la descarga (aparentemente, pocos segmentos muy grandes). Por eso, correlaciono pcap‑Traces con métricas de NGINX y del núcleo, para distinguir los tamaños reales de los datos transmitidos de los artefactos de descarga. En caso de picos de latencia, interrumpo brevemente las pruebas desactivando las descargas, documento la diferencia y luego decido qué opción resulta más beneficiosa en el funcionamiento continuo.
Plantillas de configuración por ubicación: activar y desactivar de forma selectiva
Me reservo la posibilidad de, sendfile sobrescribir en función de la ruta o el tipo de archivo. Para los directorios estáticos, se mantiene activado; para las rutas de streaming o dinámicas, lo desactivo de forma selectiva cuando los búferes o los filtros (por ejemplo, la compresión) tienen prioridad. Un breve ejemplo:
server {
listen 80;
server_name static.example.com;
root /var/www/static;
# Recursos estáticos: Zero-Copy
location /assets/ {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
expires 7d;
}
# Contenido dinámico o streaming: flexibilidad antes que «Zero-Copy»
location /api/ {
sendfile off;
proxy_pass http://app_upstream;
}
}
Esta separación evita que pierda ventajas por un lado, simplemente porque otra vía plantea requisitos específicos.
Rango, segmentos y catálogos extensos
En el caso de inmuebles de gran tamaño, hay que tener en cuenta alcance-Las solicitudes sacan partido de sus puntos fuertes: el cliente solo carga las partes necesarias y las conexiones se mantienen estables. En catálogos de contenido con archivos muy grandes, me gusta segmentar las transferencias de forma lógica: la carga del servidor se distribuye de manera más uniforme y los errores, como las interrupciones, suponen una pérdida de tiempo menor. En escenarios de almacenamiento en caché, evito los „thundering herds“ almacenando en búfer las respuestas de forma sensata, pero sin retener artificialmente pequeños fragmentos ni datos residuales. La interacción con tcp_nopush sigue siendo fundamental: mantengo grandes los segmentos iniciales, pero no dejo que el final tenga que esperar.
Estrategia de medición y ensayo: demostrar los efectos de forma fiable
Corroboro las optimizaciones con pruebas reproducibles. En el lado del servidor, analizo los perfiles de la CPU, 1TP4Hora_petición, 1 TP4 Tbytes_enviados, conexiones activas y cambios de contexto. En la red mido los tamaños de los segmentos, las retransmisiones y la distribución del RTT; correlaciono las capturas de paquetes con las estadísticas de sockets para tener en cuenta los efectos de la descarga. Por parte del cliente, comparo el TTFB, el First Contentful Paint y los tiempos de descarga con RTT y anchos de banda realistas. Vario el MTU, la configuración de Keep-Alive y los tamaños de los archivos para no limitarme a observar solo las curvas del mejor caso. Al final, basándome en cifras concretas, decido si sendfile/tcp_nopush proporcionan la estabilidad y la eficiencia deseadas en la carga de trabajo correspondiente, y realizo ajustes precisos hasta que lo consigan.
Detalles de HTTP que marcan la diferencia: «Range» y «Streaming»
Utilizo alcance-Solicitudes para archivos de gran tamaño, de modo que los clientes solo recarguen las partes necesarias y las conexiones se mantengan estables. Especialmente en el caso del avance de vídeos y la reanudación de actualizaciones, un buen soporte de los rangos de bytes ayuda a distribuir el rendimiento de forma adecuada; en la página sobre Solicitudes HTTP de rango. Para respuestas continuas con un cuerpo cada vez mayor, pruebo estrategias de streaming y me aseguro de que los búferes no retengan los datos durante demasiado tiempo de forma involuntaria. Para ello, tengo en cuenta las cachés y establezco encabezados adecuados para que los proxies y los navegadores actúen correctamente. Tengo en cuenta la interacción con tcp_nopush, ya que el tamaño de los paquetes y el momento del vaciado influyen directamente en la percepción de la velocidad.
Brevemente resumido
Con sendfile Transmito los archivos de forma eficiente directamente al núcleo y, con tcp_nopush, hago que los paquetes se llenen adecuadamente antes de que sobrecarguen la línea. Ambas directivas se complementan, mientras que tcp_nodelay envía el último byte restante sin demora. Compruebo el efecto con tráfico real, presto atención a la ruta de almacenamiento, la MTU, el Keep-Alive y la compresión, y realizo mediciones de forma sistemática. En cargas de trabajo de tipo WordPress y CDN, los beneficios se aprecian con especial rapidez, ya que muchas solicitudes se refieren a recursos estáticos. Quien utilice estos ajustes de forma específica obtendrá un mayor rendimiento por núcleo, reducirá la sobrecarga y creará reservas para los picos de crecimiento reales.


