Le mois dernier, nous avons dépanné un client SaaS : VPS 2 vCPU / 4 Go, CPU autour de 20 %, et chaque après-midi à 15 h le site devenait un diaporama. Sur le monitoring cloud, la bande passante sortante touchait le plafond — 100 Mbps saturés pendant quarante minutes d’affilée. Sa première réaction : « Il faut upgrader. » Nous avons ouvert les logs d’accès Nginx : 70 % du trafic venait de réponses API JSON non compressées et de JS statiques avec des paramètres ?v= aléatoires — les mêmes ressources renvoyées en entier à chaque fois.
Après Gzip, en-têtes de cache statique et upstream keepalive, le pic est tombé à 35 Mbps, le chargement de 8 à 1,2 seconde, sans dépenser un centime de plus. Sur les petits VPS, c’est classique : le goulot, c’est la bande passante sortante, pas le CPU. Nginx en reverse proxy entre utilisateurs et backend est le meilleur levier pour économiser la bande passante — à condition de savoir quelles vis tourner.
Cet article n’est pas un guide d’installation Nginx, mais une checklist d’optimisation orientée bande passante validée en production : diagnostiquer, compresser, mettre en cache, puis optimiser la réutilisation des connexions et le buffering. Prérequis : Nginx en reverse proxy HTTPS sur le port 443 (sans pare-feu, commencez par notre guide UFW pour serveur cloud — ouvrir 22/80/443, puis optimiser).
Pourquoi la bande passante sature avant le CPU ?
Sur un VPS cloud, la bande passante facturée est souvent asymétrique : petits forfaits à 100 Mbps de pic et 1–2 To/mois — ça paraît confortable, mais une API JSON non compressée de 500 Ko à 200 req/s représente théoriquement 800 Mbps et fait sauter la limite. Nginx sert fichiers statiques et reverse proxy simple avec peu de CPU — d’où le tableau habituel : « CPU au repos, bande passante pleine ».
Autre piège : tout envoyer à l’origine. Sans CDN, sans cache navigateur, sans proxy_cache, chaque utilisateur retélécharge bundle.js et logo.png à chaque refresh. Les modules http de la documentation officielle Nginx servent surtout à transmettre moins d’octets ou ouvrir moins de connexions — l’enjeu est de les combiner selon le scénario, pas de copier une config universelle.
| Symptôme | Cause fréquente | Action prioritaire |
|---|---|---|
| API lente, bande passante élevée | JSON/HTML non compressés | Activer gzip / brotli |
| Gros trafic au rechargement | Assets statiques sans Cache-Control | expires + noms avec empreinte |
| Connexions backend en hausse | Nouveau TCP upstream par requête | Pool keepalive |
| Gros téléchargements saturent la sortie | Origine directe, pas de cache segmenté | CDN ou proxy_cache |
Étape 1 : diagnostiquer avant d’upgrader à l’aveugle
Avant de toucher à la config, dix minutes pour savoir où part la bande passante. Sur le serveur, nous utilisons souvent :
# Trafic en direct par connexion (apt install iftop) sudo iftop -i eth0 # Stats horaires/journalières (vnstat) vnstat -h vnstat -d # Logs d'accès Nginx : URLs les plus volumineuses awk '{print $7, $10}' /var/log/nginx/access.log | \ awk '{a[$1]+=$2} END {for(i in a) print a[i], i}' | sort -rn | head -20 # Connexions ESTABLISHED actuelles ss -s ss -tn state established | wc -l
Si /api/ domine avec des corps de plusieurs centaines de Ko, la compression est la priorité. Si ce sont .js, .css, .woff2 toujours en 200 (pas de 304), les en-têtes de cache manquent. Distinguez entrant et sortant : DDoS ou crawlers saturent l’entrée — autre sujet (limitation, WAF, fail2ban). Ici, on vise l’optimisation sortante en conditions normales.
Les graphiques cloud agrègent avec 1–5 minutes de retard ; au dépannage, iftop sur la machine fait foi. Si SSH rame, consultez notre guide de dépannage SSH refusé sur serveur Linux pour écarter un problème réseau.
Étape 2 : Gzip / Brotli — la réduction la moins chère
Les réponses texte (JSON, HTML, JS, CSS, SVG, XML) se compressent typiquement de 60 à 80 %. Le module gzip intégré ne demande que quelques lignes ; la charge CPU reste acceptable sur la plupart des VPS.
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5; # 6–9 : rendements décroissants, 5 est le bon compromis
gzip_min_length 256; # Ne pas compresser les petites réponses
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml
font/woff2;
En pratique : gzip_proxied any compresse aussi le contenu proxifié (par défaut, seules les réponses directes). Ne gzip pas JPEG/PNG/WebP ni la vidéo — binaire déjà compressé, CPU gaspillé. Vérifiez avec curl -H 'Accept-Encoding: gzip' -I https://your.site/api/foo la présence de Content-Encoding: gzip.
Avec le module Brotli (certaines distros ou builds maison), 15–20 % de gain supplémentaire à qualité égale — idéal pour les API textuelles. Sans module, un gzip stable suffit largement.
Étape 3 : cache des assets statiques et sendfile
Les outils de build (Vite, Webpack) ajoutent un hash au nom de fichier, ex. app.a3f2b1.js. Ces fichiers peuvent être mis en cache longtemps — la deuxième visite ne les retélécharge pas. Beaucoup d’équipes hashent côté build mais n’envoient pas Cache-Control depuis Nginx : le navigateur redemande tout à chaque fois.
location ~* \.(js|css|woff2?|ttf|ico|svg)$ {
root /var/www/app/dist;
expires 30d;
add_header Cache-Control "public, immutable";
access_log off; # Couper access_log sur cache hit pour moins d'IO
}
# Recommandé dans le bloc http
sendfile on;
tcp_nopush on;
tcp_nodelay on;
sendfile envoie les pages fichier du noyau directement vers le socket, moins de copies userspace — utile pour gros fichiers et téléchargements concurrents. Testez si vous combinez AIO ou pools de threads ; pour la plupart des sites, sendfile + tcp_nopush + tcp_nodelay suffisent.
Pas de immutable sur HTML et index.html, sinon les utilisateurs gardent l’ancienne coque après déploiement. Stratégie courante : assets hashés 30 jours, HTML en no-cache (revalidation autorisée).
Étape 4 : cache reverse proxy (proxy_cache)
Pour les API surtout en lecture (listes de config, catalogues, détails d’article), un cache court côté Nginx divise la bande passante origine par le taux de succès. L’une des capacités les plus sous-estimées du module proxy.
# bloc http proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=apicache:50m max_size=2g inactive=60m use_temp_path=off; upstream backend { server 127.0.0.1:3000; keepalive 32; # voir section suivante } server { location /api/ { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_cache apicache; proxy_cache_valid 200 5m; proxy_cache_key "$scheme$request_method$host$request_uri"; add_header X-Cache-Status $upstream_cache_status; proxy_buffering on; proxy_buffers 16 16k; proxy_busy_buffers_size 64k; } }
$upstream_cache_status affiche HIT, MISS ou BYPASS — le cache est-il actif ? Les écritures (POST/PUT/DELETE) doivent contourner le cache ; les API personnalisées avec cookies demandent prudence pour ne pas servir les données de A à B.
Avec proxy_buffering on, Nginx absorbe la réponse backend avant de l’envoyer au client — backend lent, client rapide : moins de connexions bloquées. Pour SSE ou gros uploads : proxy_buffering off sur la location concernée.
Étape 5 : upstream keepalive — moins de poignées de main TCP
Par défaut, Nginx peut ouvrir une nouvelle TCP vers le backend à chaque requête client. À forte QPS, TIME_WAIT et handshakes consomment CPU et ports locaux — réponses plus lentes, débit effectif en baisse. upstream keepalive maintient un pool de connexions réutilisables après chaque réponse.
upstream backend {
server 127.0.0.1:8080;
keepalive 64; # taille du pool selon la QPS
}
location / {
proxy_pass http://backend;
proxy_http_version 1.1; # HTTP/1.1 requis pour keepalive
proxy_set_header Connection ""; # supprimer Connection: close
}
Sans proxy_http_version 1.1 ou Connection "", keepalive ne fonctionne pas — le cas le plus fréquent de « configuré pour rien » chez nos clients. keepalive 64 convient en dessous d’un million de PV/jour ; trop grand surcharge les descripteurs côté backend.
Étape 6 : workers, connexions et limitation de débit
Bande passante pleine : vérifier que Nginx n’est pas lui-même le goulot de connexions. Paramètres clés en tête de nginx.conf :
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
# Optionnel : limiter par IP pour éviter qu'un client monopolise
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
server {
limit_req zone=perip burst=20 nodelay;
limit_conn conn 50;
}
worker_connections × worker_processes doit dépasser le pic de connexions. Sur 2 cœurs, worker_processes auto donne souvent 2 — 4096 × 2 = 8192 largement suffisant pour PME. Alignez fs.file-max et worker_rlimit_nofile, sinon too many open files dans les logs.
limit_rate bride un téléchargement ; limit_req protège les API. Ça n’« augmente » pas la bande passante, mais empêche quelques flux anormaux de pénaliser tout le monde — dernière barrière de gouvernance.
Étape 7 : quand passer au CDN ?
Optimisations faites et bande passante encore au plafond de façon cyclique : CDN ou forfait supérieur. Critères simples :
- Assets statiques > 60 % du trafic sortant → CDN pour JS/CSS/images, origine pour HTML et API seulement
- Utilisateurs multi-régions → nœuds edge raccourcissent la distance et soulagent l’origine
- Pics (campagnes, live) → le CDN absorbe les pointes, pas besoin d’acheter la bande passante au pic sur l’origine
Avec un CDN, côté Nginx : origine accessible uniquement aux IP du CDN (liste blanche pare-feu) et IP client réelle transmise (X-Forwarded-For / module real_ip). Sinon un attaquant contourne protection et cache en frappant l’IP origine directement.
Exemple complet de bloc server (à adapter et déployer)
Tous les fragments réunis en exemple minimal — backend Node / Python sur 127.0.0.1:3000 :
upstream app {
server 127.0.0.1:3000;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
gzip on;
gzip_types application/json application/javascript text/css text/plain;
gzip_min_length 256;
location /assets/ {
alias /var/www/app/dist/assets/;
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
}
}
Puis sudo nginx -t && sudo systemctl reload nginx et onglet Network du navigateur : cache 30 jours sur les assets, gzip sur les API. Dans notre checklist de release : curl -sI https://app.example.com/assets/main.js | grep -i cache.
Validation et surveillance continue
L’optimisation n’est pas ponctuelle. Dans Grafana ou monitoring cloud, suivez quatre courbes : bande passante sortante, connexions actives Nginx, temps de réponse upstream, taux de succès cache (si proxy_cache). Avant release ou pic de trafic, test de charge : ab -n 1000 -c 50 https://app.example.com/ ou hey -n 5000 -c 100 — comparez pic de bande passante et latence P95 avant/après.
Bande passante en baisse mais latence toujours haute : le goulot est peut-être la base ou le disque — autre chapitre. Au moins vous savez : investir dans les index, pas acheter 500 Mbps à l’aveugle.
Au-delà du tuning : le bon nœud économise aussi la bande passante
Nginx réduit les octets envoyés, mais pas la distance physique utilisateur–origine. Public APAC sur un VPS US Est : RTT élevé, retransmissions, débit effectif réduit. Beaucoup d’équipes placent build et preview sur Mac cloud ou nœuds proches, statique via stockage objet + CDN, origine réservée aux API — la facture bande passante s’en remercie.
Le Mac mini M4 VPSSPark en cloud convient au CI, previews internes et bastion : macOS natif, Terminal et OpenSSH prêts ; veille ~4 W, idéal 7×24 en relais ou machine de build, plus sobre qu’un petit x86. Avec UFW sur 443 seulement et apps sur 127.0.0.1, surface d’attaque et gaspillage de bande passante diminuent.
Si vous concevez une architecture économe en bande passante et simple à opérer, le Mac mini cloud VPSSPark est un choix pragmatique comme nœud bastion et build basse consommation — voir les offres — pour que votre tuning Nginx ne soit pas annulé par un mauvais choix de région.