Servir HLS (HTTP Live Streaming) a partir do nginx é a espinha dorsal da maioria das configurações de streaming auto-hospedadas — VOD, catch-up e retransmissão ao vivo dependem disso. Este guia configura o nginx para servir segmentos HLS de forma eficiente, com os cabeçalhos de cache e CORS que os players realmente precisam.
Assumimos que você já está gerando playlists .m3u8 e segmentos .ts (ou .m4s) — por exemplo, a partir do pipeline FFmpeg NVENC.
Pré-requisitos
- Ubuntu 22.04 com nginx instalado (
apt install -y nginx) - Segmentos HLS sendo gravados em um diretório (ex:
/var/www/hls) - Um domínio apontado para o servidor
Passo 1 — Bloco de localização HLS básico
Edite a configuração do seu site (/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;
}
}O Cache-Control: no-cache na playlist é importante — os players devem buscar novamente o .m3u8 para ver novos segmentos. Refinaremos o cache a seguir.
Passo 2 — Cache dividido: playlists vs segmentos
Playlists mudam a cada segmento; segmentos nunca mudam depois de gravados. Faça o cache deles de forma 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;
}
}Isso permite que CDNs e navegadores armazenem em cache os arquivos .ts pesados, enquanto sempre verificam novamente a pequena playlist.
Passo 3 — Habilitar sendfile e ajustar para throughput
Streaming é limitado pela largura de banda. No bloco 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 o kernel copie arquivos de segmento diretamente do disco para o socket sem passar pelo userspace — uma grande vantagem para o serviço de segmentos de alta concorrência.
Passo 4 — Adicionar HTTPS (players exigem cada vez mais)
Navegadores bloqueiam conteúdo misto, então uma página em HTTPS não pode puxar HLS sobre HTTP. Obtenha um certificado com Certbot:
apt install -y certbot python3-certbot-nginx
certbot --nginx -d stream.yourdomain.comO Certbot reescreve seu bloco de servidor para escutar na porta 443 com o certificado. Verifique a renovação automática:
certbot renew --dry-runPasso 5 — Habilitar HTTP/2 (e HTTP/3 se possível)
HTTP/2 multiplexa requisições de segmento sobre uma única conexão — significativo para HLS onde um player puxa muitos arquivos pequenos:
listen 443 ssl;
http2 on;Para HTTP/3 (QUIC), você precisa do nginx compilado com o módulo http_v3 ou uma compilação como nginx-quic. HTTP/3 ajuda mais os players em redes móveis com perdas.
Passo 6 — Lidar com o "thundering herd" em transmissões ao vivo
Quando um segmento de uma transmissão ao vivo popular é lançado, milhares de players o solicitam quase simultaneamente. Sem cuidado, todos acessam o disco de uma vez. Habilite o cache de arquivos abertos:
open_file_cache max=10000 inactive=60s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;Isso armazena em cache descritores de arquivo e metadados, de modo que a segunda até a milésima requisição para um segmento quente é servida do cache, e não de um stat()+open() novo.
Passo 7 — Teste
Recarregue e verifique:
nginx -t && systemctl reload nginx
curl -I https://stream.yourdomain.com/hls/stream.m3u8Você quer 200 OK, Content-Type: application/vnd.apple.mpegurl, Access-Control-Allow-Origin: * e Cache-Control: no-cache na playlist. Carregue-a em um navegador com hls.js ou no VLC para confirmar a reprodução.
Solução de problemas
Erros de CORS no console do navegador: o cabeçalho Access-Control-Allow-Origin não está chegando ao cliente. Verifique se ele está configurado nas localizações .m3u8 e .ts, e se nenhum proxy upstream o remove.
Player trava / faz rebuffering: geralmente a entrega de segmentos não consegue acompanhar. Verifique a largura de banda com iftop; se a porta estiver saturada, você precisa de um uplink maior. Verifique também se hls_time (duração do segmento) não é muito curto — 6s é um padrão seguro.
404 nos segmentos, mas a playlist carrega: o caminho root está errado, ou os segmentos estão sendo excluídos ( hls_list_size curto) antes que os players os solicitem. Aumente hls_list_size no seu comando FFmpeg.
Próximos passos
Para entrega geo-distribuída, coloque uma frota de borda CDN na frente desta origem. Para o lado da transcodificação, consulte o guia FFmpeg NVENC. E dimensione a largura de banda da sua origem com nosso guia de velocidade de porta.