La diffusion de HLS (HTTP Live Streaming) depuis nginx est l'épine dorsale de la plupart des configurations de streaming auto-hébergées — VOD, rattrapage et rediffusion en direct reposent tous sur elle. Ce guide configure nginx pour diffuser efficacement les segments HLS, avec les en-têtes de mise en cache et CORS dont les lecteurs ont réellement besoin.

Nous supposons que vous générez déjà des playlists .m3u8 et des segments .ts (ou .m4s) — par exemple à partir du pipeline FFmpeg NVENC.

Prérequis

  • Ubuntu 22.04 avec nginx installé (apt install -y nginx)
  • Segments HLS écrits dans un répertoire (par exemple /var/www/hls)
  • Un domaine pointant vers le serveur

Étape 1 — Bloc de localisation HLS de base

Modifiez la configuration de votre site (/etc/nginx/sites-available/streaming) :

server {
    listen 80;
    server_name stream.yourdomain.com;

    location /hls/ {
        # Diffuser les segments depuis le disque
        root /var/www;

        # Types MIME corrects pour HLS
        types {
            application/vnd.apple.mpegurl m3u8;
            video/mp2t ts;
            video/iso.segment m4s;
        }

        # CORS — requis pour les lecteurs de navigateur (hls.js, video.js)
        add_header Access-Control-Allow-Origin * always;
        add_header Cache-Control no-cache always;
    }
}

Le Cache-Control: no-cache sur la playlist est important — les lecteurs doivent recharger le .m3u8 pour voir les nouveaux segments. Nous affinerons la mise en cache ensuite.

Étape 2 — Mise en cache séparée : playlists vs segments

Les playlists changent à chaque segment ; les segments ne changent jamais une fois écrits. Mettez-les en cache différemment :

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;
    # Les segments sont immuables — mettez-les en cache de manière agressive
    add_header Cache-Control "public, max-age=86400" always;
    types {
        video/mp2t ts;
        video/iso.segment m4s;
    }
}

Cela permet aux CDN et aux navigateurs de mettre en cache les fichiers .ts volumineux tout en vérifiant toujours la petite playlist.

Étape 3 — Activer sendfile et optimiser le débit

Le streaming est limité par la bande passante. Dans le bloc http {} de /etc/nginx/nginx.conf :

sendfile on;
tcp_nopush on;
tcp_nodelay on;
sendfile_max_chunk 1m;

# Augmenter les connexions des workers pour une concurrence élevée
worker_processes auto;
events {
    worker_connections 8192;
    use epoll;
    multi_accept on;
}

# Tampons de sortie plus grands pour les segments volumineux
output_buffers 4 256k;

sendfile on permet au noyau de copier les fichiers de segment directement du disque vers le socket sans passer par l'espace utilisateur — un avantage majeur pour la diffusion de segments à haute concurrence.

Étape 4 — Ajouter HTTPS (les lecteurs l'exigent de plus en plus)

Les navigateurs bloquent le contenu mixte, donc une page en HTTPS ne peut pas récupérer HLS via HTTP. Obtenez un certificat avec Certbot :

apt install -y certbot python3-certbot-nginx
certbot --nginx -d stream.yourdomain.com

Certbot réécrit votre bloc serveur pour écouter sur 443 avec le certificat. Vérifiez le renouvellement automatique :

certbot renew --dry-run

Étape 5 — Activer HTTP/2 (et HTTP/3 si possible)

HTTP/2 multiplexe les requêtes de segments sur une seule connexion — significatif pour HLS où un lecteur récupère de nombreux petits fichiers :

listen 443 ssl;
http2 on;

Pour HTTP/3 (QUIC), vous avez besoin de nginx compilé avec le module http_v3 ou d'une version comme nginx-quic. HTTP/3 aide le plus les lecteurs sur les réseaux mobiles avec pertes.

Étape 6 — Gérer l'« effet de meute » sur les bords des flux en direct

Lorsqu'un segment d'un flux en direct populaire est diffusé, des milliers de lecteurs le demandent presque simultanément. Sans précaution, ils accèdent tous au disque en même temps. Activez la mise en cache des fichiers ouverts :

open_file_cache max=10000 inactive=60s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;

Cela met en cache les descripteurs de fichiers et les métadonnées, de sorte que la deuxième à la millième requête pour un segment populaire est servie depuis le cache, et non par un nouveau stat()+open().

Étape 7 — Tester

Rechargez et vérifiez :

nginx -t && systemctl reload nginx
curl -I https://stream.yourdomain.com/hls/stream.m3u8

Vous voulez 200 OK, Content-Type: application/vnd.apple.mpegurl, Access-Control-Allow-Origin: * et Cache-Control: no-cache sur la playlist. Chargez-la dans un navigateur avec hls.js ou dans VLC pour confirmer la lecture.

Dépannage

Erreurs CORS dans la console du navigateur : l'en-tête Access-Control-Allow-Origin n'atteint pas le client. Vérifiez qu'il est défini sur les emplacements .m3u8 et .ts, et qu'aucun proxy en amont ne le supprime.

Le lecteur se bloque / met en mémoire tampon : généralement, la livraison des segments ne peut pas suivre. Vérifiez la bande passante avec iftop ; si le port est saturé, vous avez besoin d'une liaison montante plus importante. Vérifiez également que hls_time (durée du segment) n'est pas trop court — 6s est une valeur par défaut sûre.

404 sur les segments mais la playlist se charge : le chemin root est incorrect, ou les segments sont supprimés ( hls_list_size court) avant que les lecteurs ne les demandent. Augmentez hls_list_size dans votre commande FFmpeg.

Prochaines étapes

Pour une diffusion géodistribuée, placez une flotte de périphérie CDN devant cette origine. Pour la partie transcodage, consultez le guide FFmpeg NVENC. Et dimensionnez la bande passante de votre origine avec notre guide des vitesses de port.