Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 220 minIntermédiaire

Installer et configurer Nginx sur Debian 13

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • installer Nginx et lire son fichier de configuration ;
  • créer et activer un site avec un server block ;
  • prévoir quel server block et quel bloc location traitent une requête ;
  • restreindre l'accès par adresse ou par mot de passe ;
  • lire et exploiter les journaux d'accès et d'erreurs.

3 exercices corrigés1 schéma

Nginx (prononcé « engine-x ») est un serveur web : il reçoit les requêtes HTTP et HTTPS des navigateurs et leur renvoie des pages, des images ou la réponse d'une application. Il sert aussi de reverse proxy et de répartiteur de charge devant d'autres serveurs. Ce chapitre l'installe sur Debian 13, explique son fichier de configuration, crée un site avec un server block, puis montre comment Nginx choisit le bon site et le bon bloc location, comment restreindre un accès et comment lire ses journaux.

Installer Nginx

Sur srv-web, le serveur de la DMZ qui publie le site de suivi des livraisons :

bash
sudo apt update
sudo apt install nginx
systemctl status nginx --no-pager
nginx -v
# nginx version: nginx/1.26.3

Le paquet démarre le service et l'active au démarrage. Vérifie qu'il répond :

bash
curl -I http://localhost
# HTTP/1.1 200 OK
# Server: nginx
# Content-Type: text/html

L'en-tête Server: nginx n'affiche pas le numéro de version : la configuration de Debian 13 contient déjà server_tokens off. C'est une bonne pratique, qui évite d'annoncer à un attaquant la version exacte à viser. La page affichée est /var/www/html/index.nginx-debian.html, celle du site par défaut.

Nginx fonctionne avec un processus maître, lancé en root, qui lit la configuration et ouvre les ports 80 et 443, et des processus de travail (workers), sous le compte www-data, qui traitent les requêtes. Chaque processus de travail gère des milliers de connexions à la fois, sans créer un processus ni un fil d'exécution par visiteur : c'est ce modèle « événementiel » qui rend Nginx économe en mémoire sous forte charge.

bash
ps -o user,pid,cmd -C nginx
# USER       PID CMD
# root       812 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
# www-data   813 nginx: worker process
# www-data   814 nginx: worker process

L'arborescence de Nginx sous Debian

CheminRôle
/etc/nginx/nginx.confConfiguration principale : processus, journaux, options HTTP communes à tous les sites
/etc/nginx/sites-available/Un fichier par site, actif ou non
/etc/nginx/sites-enabled/Des liens symboliques vers les sites actifs : Nginx ne lit que ceux-là
/etc/nginx/conf.d/Fragments de configuration lus pour tous les sites (*.conf)
/etc/nginx/snippets/Morceaux réutilisables, inclus avec include (fastcgi-php.conf pour PHP)
/etc/nginx/proxy_paramsLes en-têtes à transmettre quand Nginx sert de reverse proxy (chapitre 10)
/var/www/Racines des sites, par convention
/var/log/nginx/Journaux : access.log et error.log, plus ceux que tu déclares par site

Le couple sites-available / sites-enabled est une convention de Debian et d'Ubuntu, pas de Nginx lui-même : sur d'autres distributions, ou avec les paquets publiés par nginx.org, tout passe par conf.d/. L'intérêt est de pouvoir désactiver un site sans supprimer son fichier, en retirant simplement le lien.

Lire nginx.conf

La configuration de Nginx est faite de directives, terminées par un point-virgule, rangées dans des blocs entre accolades, appelés contextes. Voici le fichier de Debian 13 sans ses commentaires :

nginx
user www-data;
worker_processes auto;
worker_cpu_affinity auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 768;
}

http {
    sendfile on;
    tcp_nopush on;
    types_hash_max_size 2048;
    server_tokens off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;

    access_log /var/log/nginx/access.log;

    gzip on;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}
ContexteCe qu'il règle
Principal (hors de tout bloc)Le compte des processus de travail (user), leur nombre (worker_processes auto : un par cœur), le fichier du PID, le journal d'erreurs global
eventsLe nombre maximal de connexions simultanées par processus de travail
httpTout ce qui concerne le web : types MIME, compression, TLS, journaux, et l'inclusion des sites
serverUn site (on dit server block, l'équivalent d'un VirtualHost d'Apache), dans un fichier de sites-available
locationLe traitement d'une partie des adresses d'un site (/, /api/, *.php)

Une directive écrite dans un contexte s'applique aux contextes qu'il contient, sauf s'ils la redéfinissent. server_tokens off dans http vaut donc pour tous les sites. Tu ne modifies presque jamais nginx.conf : les réglages d'un site vont dans son fichier, les réglages communs dans un fichier de conf.d/.

Créer un site avec un server block

Le site de suivi des livraisons est d'abord une page statique, en attendant l'application PHP du chapitre 4. Crée sa racine et une page de test :

bash
sudo mkdir -p /var/www/suivi/public
echo '<h1>Suivi des livraisons Sorbier</h1>' | sudo tee /var/www/suivi/public/index.html

Le dossier appartient à root et reste lisible par tous : Nginx (www-data) doit pouvoir lire les fichiers, jamais les modifier. Un site dont les fichiers appartiennent à www-data permet à quiconque exploite une faille de l'application de réécrire le site.

Crée ensuite /etc/nginx/sites-available/suivi :

nginx
server {
    listen 80;
    listen [::]:80;
    server_name suivi.sorbier-logistique.fr;

    root /var/www/suivi/public;
    index index.html index.php;

    access_log /var/log/nginx/suivi.access.log;
    error_log  /var/log/nginx/suivi.error.log;

    location / {
        try_files $uri $uri/ =404;
    }
}
DirectiveRôle
listen 80 et listen [::]:80Le port d'écoute, en IPv4 et en IPv6
server_nameLe ou les noms auxquels ce bloc répond (comparés à l'en-tête Host de la requête)
rootLe dossier dont les fichiers sont servis : /css/style.css devient /var/www/suivi/public/css/style.css
indexLe fichier servi quand l'adresse désigne un dossier
access_log, error_logDes journaux propres au site, plus faciles à lire et à transmettre au SIEM
try_files $uri $uri/ =404Essaie le fichier demandé, puis un dossier du même nom, sinon renvoie l'erreur 404

Active le site, vérifie la syntaxe, recharge :

bash
sudo ln -s /etc/nginx/sites-available/suivi /etc/nginx/sites-enabled/
sudo nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
sudo systemctl reload nginx

Tant que le nom public ne pointe pas encore vers le serveur, teste en fournissant toi-même l'en-tête Host :

bash
curl -H "Host: suivi.sorbier-logistique.fr" http://192.168.10.225/
# <h1>Suivi des livraisons Sorbier</h1>

Comment Nginx choisit le site qui répond

Un même serveur peut héberger des dizaines de sites sur la même adresse IP et le même port. Pour chaque requête, Nginx procède en deux temps :

  1. il retient les server blocks dont la directive listen correspond à l'adresse et au port de la connexion ;
  2. parmi eux, il choisit celui dont le server_name correspond à l'en-tête Host envoyé par le navigateur. Si aucun ne correspond, la requête va au serveur par défaut de ce port : celui qui porte default_server dans son listen, sinon le premier lu.
Requêtes reçues sur le port 80Server blocks de srv-webHost: suivi.sorbier-logistique.frun client sur InternetHost: recette.sorbier-logistique.frun testeur autoriséHost: 203.0.113.17un robot qui balaie les adressesserver block « suivi »root /var/www/suivi/publicserver block « recette »root /var/www/recette/publicserver block par défautdefault_server : return 444Aucun server_name ne correspond : la requête va au bloc marqué default_server.
Nginx compare l'en-tête Host aux server_name ; une requête sans nom connu va au server block par défaut

Sur un serveur exposé à Internet, les robots qui balaient les adresses IP envoient des requêtes sans nom, ou avec un nom quelconque. Si le site par défaut est ton site de production, ils le voient. Remplace donc le site par défaut de Debian par un bloc qui ne sert rien :

nginx
# /etc/nginx/sites-available/000-defaut
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 444;
}
bash
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/000-defaut /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

server_name _ est un nom qui ne correspond à rien, et 444 est un code propre à Nginx : il ferme la connexion sans rien renvoyer. Le chapitre 6 fait de même pour le port 443.

Les blocs location

Dans un server block, les blocs location disent quoi faire selon le chemin demandé. Leur ordre d'écriture compte moins que leur type :

SyntaxeTypeExempleCorrespond à
location = /santeÉgalité exactePage de contrôle d'un répartiteur/sante seulement
location ^~ /static/Préfixe prioritaireFichiers statiquesTout ce qui commence par /static/, sans tester les expressions régulières
location ~ \.php$Expression régulière, sensible à la casseScripts PHP/index.php, /admin/liste.php
location ~* \.(png|jpg|css|js)$Expression régulière, insensible à la casseRessources/logo.PNG, /app.js
location /PréfixeTout le resteToute adresse

Nginx choisit ainsi :

  1. une égalité exacte (=) qui correspond gagne immédiatement ;
  2. sinon, il cherche le plus long préfixe qui correspond ; s'il porte ^~, il gagne ;
  3. sinon, il teste les expressions régulières dans l'ordre du fichier, et la première qui correspond gagne ;
  4. si aucune ne correspond, c'est le plus long préfixe trouvé à l'étape 2.

Exemple sur le site de suivi : interdire l'accès aux fichiers cachés (.git, .env) qui traîneraient dans la racine, et mettre en cache navigateur les ressources statiques.

nginx
    # Fichiers et dossiers cachés : .git, .env, .htaccess
    location ~ /\. {
        deny all;
    }

    location ~* \.(css|js|png|jpg|svg|woff2)$ {
        expires 7d;
        access_log off;
    }

Restreindre l'accès à une partie du site

Par adresse IP. La page de statistiques du site ne doit être vue que depuis le réseau d'administration :

nginx
    location /stats/ {
        allow 192.168.10.208/28;
        deny  all;
    }

Les règles se lisent dans l'ordre et la première qui correspond s'applique : allow d'abord, deny all ensuite. Une requête interdite reçoit l'erreur 403.

Par mot de passe (authentification HTTP Basic). Le fichier des mots de passe se crée avec htpasswd, du paquet apache2-utils, et se range hors de la racine du site :

bash
sudo apt install apache2-utils
sudo htpasswd -c -B /etc/nginx/htpasswd-stats amercier   # -c crée le fichier, -B hache avec bcrypt
sudo chown root:www-data /etc/nginx/htpasswd-stats
sudo chmod 640 /etc/nginx/htpasswd-stats
nginx
    location /stats/ {
        satisfy all;               # il faut l'adresse ET le mot de passe
        allow 192.168.10.208/28;
        deny  all;
        auth_basic "Statistiques";
        auth_basic_user_file /etc/nginx/htpasswd-stats;
    }

L'authentification Basic envoie l'identifiant et le mot de passe à chaque requête, seulement encodés en Base64 : elle n'est acceptable qu'en HTTPS. Pour une vraie application, préfère l'authentification de l'application elle-même, reliée à l'annuaire.

La taille des envois. Par défaut, Nginx refuse un corps de requête de plus de 1 Mo, avec l'erreur 413 Request Entity Too Large. Une application qui accepte des pièces jointes a besoin de plus, à régler dans son server block, en cohérence avec PHP (chapitre 4) :

nginx
    client_max_body_size 20m;

Les journaux de Nginx

Chaque requête ajoute une ligne au journal d'accès, au format combined par défaut :

text
198.51.100.23 - - [21/Oct/2026:10:42:17 +0200] "GET /suivi.php?colis=SL-48213 HTTP/1.1" 200 3184 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
ChampValeurSens
Adresse du client198.51.100.23Qui a fait la requête (derrière un reverse proxy, c'est l'adresse du proxy : voir le chapitre 10)
Utilisateur-Rempli seulement avec l'authentification HTTP
Date[21/Oct/2026:10:42:17 +0200]Avec le fuseau horaire
Requête"GET /suivi.php?colis=SL-48213 HTTP/1.1"Méthode, chemin avec ses paramètres, version de HTTP
Code de réponse200Succès ; 404 introuvable ; 403 interdit ; 5xx erreur du serveur
Taille3184Octets envoyés
Référent"-"La page d'où vient le visiteur
Agent"Mozilla/5.0 …"Le navigateur ou le robot

Le journal d'erreurs, lui, dit pourquoi une requête a échoué : fichier absent, droit refusé, application injoignable. C'est le premier fichier à ouvrir quand un site renvoie une erreur 403, 404 ou 502.

bash
sudo tail -f /var/log/nginx/suivi.error.log
# 2026/10/21 10:44:02 [error] 813#813: *51 open() "/var/www/suivi/public/favicon.ico" failed (2: No such file or directory)

Debian fait tourner ces journaux chaque jour et en garde 14, compressés (/etc/logrotate.d/nginx). Pour les conserver plus longtemps et pouvoir enquêter après un incident, ils sont envoyés au serveur central de Sorbier (chapitre 12).

Quelques commandes pour exploiter un journal d'accès :

bash
# Les 10 adresses qui font le plus de requêtes
sudo awk '{print $1}' /var/log/nginx/suivi.access.log | sort | uniq -c | sort -rn | head
# Les requêtes en erreur 404 (le code est le 9e champ)
sudo awk '$9 == 404 {print $7}' /var/log/nginx/suivi.access.log | sort | uniq -c | sort -rn | head

Exercices

Exercice 1 sur 3 Quel bloc location ? Réussi À revoir

Un server block contient, dans cet ordre :

nginx
location / { … }                     # A
location /images/ { … }              # B
location ~* \.(png|jpg)$ { … }        # C
location ^~ /images/logos/ { … }     # D
location = /images/plan.png { … }    # E

Quel bloc traite chacune de ces requêtes : /images/photo.jpg, /images/logos/sorbier.png, /images/plan.png, /images/notice.pdf, /contact ?

Voir la réponseMasquer la réponse de l'exercice 1
RequêteBlocPourquoi
/images/photo.jpgCLe plus long préfixe est B, sans ^~ : Nginx teste les expressions régulières, et C correspond
/images/logos/sorbier.pngDLe plus long préfixe est D, qui porte ^~ : les expressions régulières ne sont pas testées
/images/plan.pngEL'égalité exacte gagne avant tout le reste
/images/notice.pdfBAucune expression régulière ne correspond : le plus long préfixe, B, s'applique
/contactASeul le préfixe / correspond

Le piège : croire que B traite toutes les adresses qui commencent par /images/. Sans ^~, une expression régulière qui correspond lui passe devant.

Tu avais trouvé ?

Exercice 2 sur 3 Le site de recette Réussi À revoir

Sorbier veut un site de recette, recette.sorbier-logistique.fr, racine /var/www/recette/public, sur srv-web. Il ne doit être accessible que depuis le réseau d'administration et le VLAN des postes, avec des journaux propres. Écris le server block et les commandes pour l'activer.

Voir la réponseMasquer la réponse de l'exercice 2
nginx
# /etc/nginx/sites-available/recette
server {
    listen 80;
    listen [::]:80;
    server_name recette.sorbier-logistique.fr;

    root /var/www/recette/public;
    index index.html index.php;

    access_log /var/log/nginx/recette.access.log;
    error_log  /var/log/nginx/recette.error.log;

    allow 192.168.10.208/28;   # ADMIN
    allow 192.168.10.64/26;    # POSTES
    deny  all;

    location / {
        try_files $uri $uri/ =404;
    }
}
bash
sudo mkdir -p /var/www/recette/public
sudo ln -s /etc/nginx/sites-available/recette /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Les directives allow et deny placées au niveau du server valent pour tous ses blocs location. Le filtrage de Nginx ne remplace pas celui du pare-feu, mais ici il est indispensable : la redirection du pare-feu porte sur l'adresse et le port, pas sur le nom. N'importe qui sur Internet peut envoyer une requête avec Host: recette.sorbier-logistique.fr à 203.0.113.17. La vraie protection serait d'héberger la recette sur un serveur interne, non publié.

Tu avais trouvé ?

Exercice 3 sur 3 Lire l'erreur Réussi À revoir

Après avoir activé un nouveau site, sudo nginx -t affiche : nginx: [emerg] a duplicate default server for 0.0.0.0:80 in /etc/nginx/sites-enabled/suivi:2. Explique l'erreur et corrige-la.

Voir la réponseMasquer la réponse de l'exercice 3

Deux server blocks déclarent default_server sur le même port : le site par défaut (000-defaut, ou le default de Debian s'il n'a pas été retiré) et le site suivi, ligne 2. Il ne peut y avoir qu'un seul serveur par défaut par adresse et par port. Correction : retirer default_server de la directive listen du site suivi (listen 80;), puis relancer sudo nginx -t et recharger. Tant que nginx -t échoue, la configuration en service reste l'ancienne : aucun site n'est coupé.

Tu avais trouvé ?

Chapitre terminé !

Fais les 3 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Apache 2.4 : VirtualHost, modules et différences avec NginxRevoir le sommaire du cours