Servir HLS (HTTP Live Streaming) desde nginx es la columna vertebral de la mayoría de las configuraciones de streaming autoalojadas — VOD, catch-up y retransmisión en vivo se basan en ello. Esta guía configura nginx para servir segmentos HLS de manera eficiente, con los encabezados de almacenamiento en caché y CORS que los reproductores realmente necesitan.
Asumimos que ya está generando listas de reproducción .m3u8 y segmentos .ts (o .m4s) — por ejemplo, desde la pipeline FFmpeg NVENC.
Requisitos previos
- Ubuntu 22.04 con nginx instalado (
apt install -y nginx) - Segmentos HLS siendo escritos en un directorio (ej.
/var/www/hls) - Un dominio apuntando al servidor
Paso 1 — Bloque de ubicación HLS básico
Edite la configuración de su sitio (/etc/nginx/sites-available/streaming):
server {
listen 80;
server_name stream.yourdomain.com;
location /hls/ {
# Serve segments from disk
root /var/www;
# Correct MIME types for HLS
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
video/iso.segment m4s;
}
# CORS — required for browser players (hls.js, video.js)
add_header Access-Control-Allow-Origin * always;
add_header Cache-Control no-cache always;
}
}El Cache-Control: no-cache en la lista de reproducción es importante — los reproductores deben volver a obtener el .m3u8 para ver nuevos segmentos. A continuación, refinaremos el almacenamiento en caché.
Paso 2 — Almacenamiento en caché dividido: listas de reproducción vs segmentos
Las listas de reproducción cambian con cada segmento; los segmentos nunca cambian una vez escritos. Almacénelos en caché de manera diferente:
location ~ \.m3u8$ {
root /var/www;
add_header Access-Control-Allow-Origin * always;
add_header Cache-Control "no-cache, no-store" always;
types { application/vnd.apple.mpegurl m3u8; }
}
location ~ \.(ts|m4s)$ {
root /var/www;
add_header Access-Control-Allow-Origin * always;
# Segments are immutable — cache aggressively
add_header Cache-Control "public, max-age=86400" always;
types {
video/mp2t ts;
video/iso.segment m4s;
}
}Esto permite que los CDNs y navegadores almacenen en caché los archivos .ts pesados mientras siempre vuelven a verificar la pequeña lista de reproducción.
Paso 3 — Habilitar sendfile y ajustar para el rendimiento
El streaming está limitado por el ancho de banda. En el bloque http {} de /etc/nginx/nginx.conf:
sendfile on;
tcp_nopush on;
tcp_nodelay on;
sendfile_max_chunk 1m;
# Bump worker connections for high concurrency
worker_processes auto;
events {
worker_connections 8192;
use epoll;
multi_accept on;
}
# Larger output buffers for large segments
output_buffers 4 256k;sendfile on permite que el kernel copie archivos de segmento directamente del disco al socket sin pasar por el espacio de usuario — una gran ventaja para el servicio de segmentos de alta concurrencia.
Paso 4 — Añadir HTTPS (los reproductores lo requieren cada vez más)
Los navegadores bloquean el contenido mixto, por lo que una página en HTTPS no puede obtener HLS a través de HTTP. Obtenga un certificado con Certbot:
apt install -y certbot python3-certbot-nginx
certbot --nginx -d stream.yourdomain.comCertbot reescribe su bloque de servidor para escuchar en el puerto 443 con el certificado. Verifique la renovación automática:
certbot renew --dry-runPaso 5 — Habilitar HTTP/2 (y HTTP/3 si es posible)
HTTP/2 multiplexa las solicitudes de segmentos a través de una conexión — significativo para HLS donde un reproductor obtiene muchos archivos pequeños:
listen 443 ssl;
http2 on;Para HTTP/3 (QUIC), necesita nginx compilado con el módulo http_v3 o una compilación como nginx-quic. HTTP/3 ayuda más a los reproductores en redes móviles con pérdidas.
Paso 6 — Manejar el "rebaño atronador" en los bordes en vivo
Cuando un segmento de una transmisión en vivo popular se libera, miles de reproductores lo solicitan casi simultáneamente. Sin cuidado, todos acceden al disco a la vez. Habilite el almacenamiento en caché de archivos abiertos:
open_file_cache max=10000 inactive=60s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;Esto almacena en caché los descriptores de archivo y los metadatos, de modo que la segunda a la milésima solicitud de un segmento popular se sirve desde la caché, no desde un stat()+open() fresco.
Paso 7 — Pruébelo
Recargue y verifique:
nginx -t && systemctl reload nginx
curl -I https://stream.yourdomain.com/hls/stream.m3u8Debe ver 200 OK, Content-Type: application/vnd.apple.mpegurl, Access-Control-Allow-Origin: * y Cache-Control: no-cache en la lista de reproducción. Cárguelo en un navegador con hls.js o en VLC para confirmar la reproducción.
Solución de problemas
Errores CORS en la consola del navegador: el encabezado Access-Control-Allow-Origin no llega al cliente. Verifique que esté configurado tanto en las ubicaciones .m3u8 como .ts, y que ningún proxy ascendente lo elimine.
El reproductor se detiene / se recarga: generalmente la entrega de segmentos no puede mantener el ritmo. Verifique el ancho de banda con iftop; si el puerto está saturado, necesita un enlace ascendente más grande. También verifique que hls_time (duración del segmento) no sea demasiado corto — 6s es un valor predeterminado seguro.
404 en segmentos pero la lista de reproducción carga: la ruta root es incorrecta, o los segmentos se están eliminando ( hls_list_size corto) antes de que los reproductores los soliciten. Aumente hls_list_size en su comando FFmpeg.
Próximos pasos
Para la entrega geo-distribuida, coloque una flota de borde CDN delante de este origen. Para el lado de la transcodificación, consulte la guía FFmpeg NVENC. Y dimensione el ancho de banda de su origen con nuestra guía de velocidad de puerto.