Déployer un reverse-proxy Nginx avec Docker

Dans ce tutoriel, nous allons voir comment déployer un reverse proxy Nginx à l’aide d’une image Docker dédiée, que j’ai créée pour simplifier la mise en place d’un reverse proxy dans un environnement conteneurisé.

Cette image a été pensée pour être simple à utiliser, facilement extensible et adaptée aussi bien à un usage personnel que professionnel. Elle permet de centraliser l’accès à plusieurs services web derrière un point d’entrée unique, tout en conservant une configuration Nginx claire et maîtrisée.

Nous verrons pas à pas comment récupérer l’image, configurer Nginx et déployer le reverse proxy avec Docker, ainsi que les bonnes pratiques pour l’intégrer dans une stack existante.

Pourquoi cette image Docker Nginx ?

Au quotidien, je gère un système d’information dans lequel plus d’une centaine de services sont publiés via un reverse proxy, qu’il s’agisse de sites web ou d’applications métiers. Certaines de ces publications nécessitent des configurations Nginx assez spécifiques, voire atypiques, j’y reviendrai d’ailleurs plus loin dans ce tutoriel.

Avant la création de cette image Docker, le reverse proxy reposait sur un conteneur LXC sous Ubuntu 24.04, avec Nginx installé depuis le dépôt PPA Ondřej Surý. Ce choix nous permettait de bénéficier d’une version récente de Nginx ainsi que de modules indispensables à notre usage, notamment GeoIP, Headers et SubFilter.

Cependant, notre infrastructure évoluant de plus en plus vers des applications conteneurisées, il devenait logique d’uniformiser également le reverse proxy et de le basculer sur Docker.

Dans un premier temps, nous avons étudié les solutions existantes afin d’éviter de réinventer la roue :

  • Nginx Proxy Manager : une très bonne solution, simple et efficace, mais qui ne nous permettait pas d’atteindre un périmètre fonctionnel équivalent à notre infrastructure existante.
  • BunkerWeb : une solution que j’apprécie particulièrement, sur laquelle j’ai d’ailleurs déjà rédigé plusieurs tutoriels. Là encore, malgré ses qualités, elle ne permettait pas d’être iso périmètre avec nos besoins actuels.

Si j’avais dû choisir entre ces deux solutions, mon choix se serait clairement porté sur BunkerWeb.

Plusieurs contraintes fortes de notre environnement entraient également en jeu :

  • supervision de Nginx via Zabbix
  • exposition des métriques Nginx VTS dans Grafana
  • analyse centralisée des logs avec CrowdSec, mutualisée avec d’autres applications
  • utilisation de pages d’erreur personnalisées basées sur l’image tarampampam/error-pages

Par ailleurs, nous travaillons depuis quelque temps sur la centralisation et le versionning des configurations Nginx via GitLab, avec à terme des pipelines permettant de :

  • tester les fichiers de configuration
  • valider leur conformité
  • puis les déployer automatiquement sur le reverse proxy

(Cette partie est encore en cours de mise en place.)

Face à l’ensemble de ces contraintes, je n’ai finalement pas eu d’autre choix que de concevoir ma propre image Docker Nginx, basée sur Debian 13 (et non plus Ubuntu — j’expliquerai également ce choix dans un autre article), tout en conservant le dépôt Sury afin de disposer d’une version récente de Nginx et de ses modules.

Ce choix me permet aujourd’hui d’être strictement iso périmètre avec l’infrastructure existante.

Comme vous allez le voir dans la suite de ce tutoriel, la véritable force de cette solution ne réside pas uniquement dans l’image Docker, mais surtout dans :

  • le stack Docker Compose
  • et la gestion structurée des fichiers de configuration Nginx

Présentation de l’image Docker

L’image rdrit/nginx-rproxy a été conçue pour répondre à un besoin précis : disposer d’un reverse proxy Nginx conteneurisé, complet et iso périmètre, tout en conservant une maîtrise totale de la configuration.

Base technique :

L’image est basée sur Debian 13, avec utilisation du dépôt Sury afin de bénéficier :

  • d’une version récente et maintenue de Nginx
  • des modules complémentaires nécessaires à notre environnement
  • d’un cycle de mise à jour maîtrisé

Ce choix permet de s’affranchir des limitations des images officielles trop minimalistes, tout en conservant un socle stable et pérenne.

Philosophie de l’image :

L’objectif n’était pas de produire une image “magique” ou fortement abstraite, mais au contraire :

  • conserver un fonctionnement proche d’une installation bare-metal
  • garder une structure de fichiers claire
  • permettre une intégration simple avec les outils de supervision et de sécurité

L’image expose ainsi les « répertoires standards » Nginx, afin que toute la configuration soit externalisée via des volumes Docker.

Présentation du stack Docker Compose

Si l’image constitue la base technique, la véritable valeur de la solution repose sur le stack Docker Compose, pensé pour structurer l’ensemble de l’écosystème autour du reverse proxy.

L’objectif n’est pas uniquement de faire fonctionner Nginx, mais de proposer une architecture complète, modulaire et adaptée à un contexte d’exploitation professionnel.

Service principal : Nginx

Le service nginx s’appuie sur l’image : rdrit/nginx-rproxy:${NGINX_TAG:-latest} qui expose les ports 80 (HTTP) et 443 (HTTPS).

Les performances peuvent être ajustées dynamiquement via des variables d’environnement :

  • NGINX_WORKER_PROCESSES
  • NGINX_WORKER_CONNECTIONS

Externalisation des répertoires critiques

L’ensemble des éléments structurants est externalisé via des volumes :

  • Configuration (conf, sites, streams, snippets, modules)
  • Certificats (ssl et certs)
  • Logs
  • Cache
  • Base GeoIP

Pages d’erreur personnalisées

Le service error-pages, basé sur l’image tarampampam/error-pages, permet de fournir des pages d’erreur homogènes et maîtrisées.

En cas d’indisponibilité d’un backend (502, 503, 504…), l’utilisateur final ne voit pas une erreur Nginx brute, mais une page cohérente et personnalisée.

Ce composant contribue à améliorer l’expérience utilisateur et la perception globale de la disponibilité des services.

Gestion des certificats Let’s Encrypt

Le service certbot repose sur le challenge DNS Cloudflare, permettant :

  • la génération de certificats wildcard
  • l’absence d’exposition temporaire en HTTP
  • un renouvellement automatique

Les certificats sont stockés dans un volume partagé (certs) avec Nginx, garantissant leur persistance.

Intégration GeoIP

Le service geoipupdate assure la mise à jour automatisée des bases MaxMind.

Cela permet :

  • d’enrichir les logs Nginx
  • de mettre en place des règles de filtrage par pays
  • d’améliorer la sécurité globale

Utilisation des profiles Docker Compose

Un point important de cette architecture est l’utilisation des profiles Docker Compose, permettant d’activer ou non certains composants selon le besoin.

Nous verrons leur utilisation par la suite

Comme vous pouvez le constater, la force de cette solution ne réside pas uniquement dans l’image Docker, mais bien dans l’architecture complète du stack et dans la structuration des fichiers de configuration.

Prérequis

Avant de commencer le déploiement, assurez-vous de disposer des éléments suivants :

  • Un serveur Linux
  • Docker installé et fonctionnel
  • Docker Compose (plugin docker compose recommandé)

Le reverse proxy peut fonctionner avec une configuration minimale, cependant certaines fonctionnalités optionnelles nécessitent des éléments complémentaires.

GeoIP (optionnel)

Si vous souhaitez activer la fonctionnalité GeoIP, vous devrez disposer d’un compte chez MaxMind afin d’obtenir :

  • un Account ID
  • une License Key

Ces informations permettront au service geoipupdate de télécharger automatiquement les bases GeoIP nécessaires.

Gestion des certificats Let’s Encrypt (optionnel)

La gestion automatisée des certificats repose sur le challenge DNS via Cloudflare.

Pour l’utiliser, votre domaine devra :

  • être hébergé chez Cloudflare
  • disposer d’une clé API avec les permissions DNS nécessaires

Cela permet notamment :

  • la génération de certificats wildcard
  • l’absence d’ouverture temporaire du port 80
  • un fonctionnement compatible avec des environnements protégés par firewall

Si vous utilisez un autre fournisseur DNS, il est tout à fait possible d’adapter le fichier docker-compose.yml afin d’utiliser un autre plugin DNS pris en charge par Certbot.

Initialisation et configuration de l’environnement Nginx

Sur l’hôte Docker, commencer par créer un dossier et se déplacer dedans :

mkdir -p /containers/nginx-rproxy
cd /containers/nginx-rproxy

Cloner le dossier :

bash <(wget -qO- https://git.rdr-it.com/root/scripts/-/raw/master/Linux/rdr-it/get-docker-compse/get.sh) Nginx-RProxy

Copier le fichier sample.env en le nommant .env puis éditer le.

cp sample.env .env
nano .env

La configuration des variables d’environnement se trouve ici : https://github.com/rdrouche/Docker-Compose/tree/main/Nginx-RProxy#les-variables-denvironnement

Commencer par démarrer les conteneurs Nginx et Error page :

docker compose up -d

Astuce :

Si vous utilisez une version à jour de Docker et Docker Compose, vous pouvez démarrer les conteneurs avec la commande : docker compose up, cela permet d’avoir les logs en directe puis appuyer sur la touche d pour sortir des logs et faire fonctionner les conteneurs en mode detach (arrière plan)

Afin de disposer d’un fichier docker-compose.yml flexible et facilement adaptable aux différents contextes d’utilisation, j’ai mis en place des profiles Docker Compose.

Ces profiles permettent de sélectionner précisément les services à démarrer, sans avoir à modifier le fichier docker-compose.yml, rendant ainsi le déploiement plus simple, plus lisible et plus modulaire.

Par défaut, si aucun profil n’est renseigner dans la commande docker compose up, seul les services nginx et error-pages sont démarrés

Pour inclure seulement geoipupdate :

docker compose --profile geo up -d

Pour inclure seulement certbot :

docker compose --profile ssl up -d

Pour démarrer tous les services:

docker compose --profile all up -d

Comment utiliser le reverse proxy

Nous allons maintenant aborder l’utilisation du reverse proxy et la gestion des configurations Nginx.

Contrairement à une installation classique sous Debian ou Ubuntu, où l’on utilise généralement des liens symboliques (par exemple sites-available / sites-enabled) pour activer ou désactiver une configuration, l’approche retenue ici est volontairement plus simple et mieux adaptée à un environnement conteneurisé.

Dans ce stack, le chargement des configurations repose uniquement sur l’extension des fichiers.

Principe de fonctionnement

Les répertoires suivants sont analysés par Nginx :

  • conf
  • sites
  • modules
  • streams

Pour être pris en compte par Nginx, un fichier de configuration doit obligatoirement porter l’extension .conf.

Cette convention permet :

  • d’activer une configuration simplement
  • de désactiver un fichier sans le supprimer
  • d’éviter l’usage de liens symboliques dans un environnement Docker

Exemple

Un fichier actif :

site-exemple.conf

Pour désactiver temporairement cette configuration, il suffit de le renommer :

site-exemple.conf.disable

Nginx n’interprétera alors plus ce fichier au prochain rechargement de configuration.

Avantages de cette approche

  • activation / désactivation rapide des services
  • simplicité de gestion
  • lisibilité des configurations
  • compatibilité avec le versionning Git
  • aucun lien symbolique à gérer

Voici un exemple de configuration Nginx à placer dans le répertoire sites avec l’extension .conf.

Cette configuration met en place :

  • une redirection HTTP vers HTTPS
  • un virtualhost HTTPS avec terminaison TLS
  • un proxy vers un backend distant

Fichier exemple.domain.tld.conf

server {
    listen 80;
    server_name exemple.domain.tld;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name exemple.domain.tld;

    ssl_certificate /ssl/exemplecom.cer;
    ssl_certificate_key /ssl/exemplecom.key;

    access_log /var/log/nginx/exemple.domain.tld_access.log;
    error_log /var/log/nginx/exemple.domain.tld_error.log;

    location / {
        proxy_pass https://A.B.C.D;
        proxy_ssl_verify off;
        include proxy_params;
    }
}

Pour tester la configuration :

docker compose exec nginx nginx -t

Puis pour recharger la configuration :

docker compose exec nginx nginx -s reload

Optimisation des virtualhosts avec les snippets

La configuration précédente fonctionne parfaitement, mais si vous déployez plusieurs virtualhosts, vous allez rapidement constater :

  • de la répétition (TLS, logs, paramètres proxy…)
  • des risques d’incohérence entre fichiers
  • une maintenance plus complexe
  • des modifications globales fastidieuses

Pour résoudre ce problème, nous allons utiliser des snippets Nginx.

Pourquoi utiliser des snippets ?

Les snippets permettent de :

  • centraliser les blocs communs
  • réduire la taille des fichiers virtualhosts
  • améliorer la lisibilité
  • faciliter les modifications globales
  • standardiser les bonnes pratiques

L’objectif est d’avoir des virtualhosts simples, lisibles et homogènes.

Snippets Nginx fournis et prêts à l’emploi

Afin de faciliter la mise en place des virtualhosts et de garantir une configuration homogène, plusieurs snippets Nginx sont déjà inclus dans l’image et le stack Docker Compose.

Ces snippets sont conçus pour être réutilisables, modulaires et adaptés à un usage en production.

logging.conf

Ce snippet permet de configurer les access logs de manière dynamique, en s’appuyant sur la variable $host.

Fonctionnalités principales :

  • création automatique d’un fichier de log par virtualhost
  • journalisation uniquement des requêtes utiles
  • exclusion des ressources statiques (CSS, JS, favicon, etc.)
  • réduction significative du bruit dans les logs
  • amélioration de la lisibilité et de l’analyse (monitoring, SIEM)

Idéal pour :

  • environnements multi-sites
  • supervision et troubleshooting ciblés

global-error.conf

Ce snippet centralise la gestion des pages d’erreur HTTP et redirige les erreurs vers le service error-pages.

Avantages :

  • pages d’erreur homogènes sur l’ensemble des services
  • aucune fuite d’informations techniques
  • meilleure expérience utilisateur en cas d’incident
  • découplage entre logique applicative et présentation des erreurs

proxy-common.conf

Il s’agit du socle de configuration pour tous les reverse proxy.

Il inclut notamment :

  • les headers standards (X-Forwarded-*)
  • les paramètres proxy essentiels
  • les bonnes pratiques pour un backend HTTP/HTTPS

Ce snippet permet de :

  • standardiser les reverse proxy
  • éviter les oublis ou incohérences
  • simplifier les virtualhosts

remove-header.conf

Ce snippet a pour objectif de réduire la surface d’exposition d’informations.

Il permet notamment :

  • la suppression du header Server
  • la limitation des informations retournées par Nginx

C’est une mesure simple mais efficace pour améliorer la sécurité globale.

ssl-exemplecom.conf

Ce fichier fournit un exemple de configuration SSL prêt à l’emploi.

Il inclut :

  • la déclaration des certificats
  • l’activation de HTTP/2
  • une base saine pour TLS

Ce snippet est volontairement générique et peut être :

  • dupliqué par domaine
  • adapté à votre politique de sécurité
  • remplacé par une configuration plus stricte si nécessaire

Pour reprendre l’exemple précédent, voici le même virtualhost avec les snippets :

server {
    listen 80;
    server_name exemple.domain.tld;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name exemple.domain.tld;

    include snippets/ssl-exemplecom.conf;
    include snippets/logging.conf;
    include snippets/remove-header.conf;
    include snippets/global-error.conf;

    location / {
        proxy_pass https://A.B.C.D;
        include proxy-common.conf;
    }
}

Optimisation : éviter la multiplication inutile des virtualhosts

Dans un environnement de production, il est important d’éviter la duplication inutile des configurations.

Si plusieurs noms de domaine pointent vers le même backend et utilisent exactement les mêmes paramètres (SSL, proxy, logs, sécurité…), il est inutile de créer un virtualhost par domaine.

Cas typique :

Vous disposez d’un serveur web qui héberge plusieurs sites :

  • site1.exemple.tld
  • site2.exemple.tld
  • site3.exemple.tld

Tous passent par le reverse proxy et redirigent vers le même serveur backend, avec la même configuration.

Dans ce cas, il est préférable de déclarer un seul virtualhost avec plusieurs server_name.

server {
    listen 80;
    server_name site1.exemple.tld site2.exemple.tld site2.exemple.tld;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name site1.exemple.tld site2.exemple.tld site2.exemple.tld;

    include snippets/ssl-exemplecom.conf;
    include snippets/logging.conf;
    include snippets/remove-header.conf;
    include snippets/global-error.conf;

    location / {
        proxy_pass https://A.B.C.D;
        include proxy-common.conf;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_ssl_name $host;
        proxy_ssl_server_name on;        
        proxy_ssl_session_reuse off;
    }
}

Pourquoi c’est préférable ?

Cette approche permet :

  • d’éviter la duplication de configuration
  • de réduire les risques d’incohérence
  • de simplifier la maintenance
  • d’améliorer la lisibilité globale
  • de limiter les erreurs lors de modifications futures

Une modification du backend ou d’un paramètre SSL ne nécessite alors qu’un seul ajustement.

⚠️ Attention :

Si les domaines nécessitent des comportements différents (certificats distincts, règles spécifiques, redirections personnalisées, sécurité différente), il est préférable de conserver des virtualhosts séparés.

Conclusion

Vous disposez désormais de tous les éléments nécessaires pour déployer, configurer et exploiter ce stack de reverse proxy basé sur Nginx.

Nous avons vu :

  • la structure du stack Docker Compose
  • l’utilisation des profiles pour adapter le déploiement
  • l’organisation des virtualhosts
  • l’usage des snippets pour améliorer la lisibilité et la maintenabilité
  • plusieurs bonnes pratiques applicables en production

J’avais initialement prévu d’aborder également :

  • la mise en place du cache Nginx
  • les mécanismes de réécriture de codes HTML

Cependant, l’article est déjà conséquent.
Si ces sujets vous intéressent, n’hésitez pas à me le faire savoir : je pourrai les détailler dans un ou plusieurs tutoriels dédiés.

Au-delà de l’aspect conteneurisé, les bonnes pratiques présentées ici (modularisation via snippets, centralisation des paramètres proxy, gestion homogène des erreurs, optimisation des logs) peuvent tout à fait être appliquées à une installation Nginx plus “classique” sous Linux.

Certes, cette solution est moins “user friendly” que certaines alternatives disposant d’une interface web d’administration.
En revanche, elle conserve toute la puissance et la souplesse de configuration de Nginx, souvent limitée ou abstraite par des solutions plus automatisées.

Ce choix s’adresse donc davantage :

  • aux environnements maîtrisés
  • aux infrastructures professionnelles
  • aux administrateurs souhaitant garder un contrôle fin
  • aux architectures nécessitant flexibilité et évolutivité

L’objectif n’est pas de simplifier à l’extrême, mais de proposer une base propre, modulaire et robuste, adaptée aux besoins réels d’une infrastructure moderne.

Aller plus loin

Ce tutoriel pose les bases pour déployer un reverse proxy Nginx en conteneur, modulable et maintenable.

Pour aller plus loin, vous pouvez envisager :

  • Cache Nginx : activer et configurer le cache pour améliorer les performances des sites statiques ou applicatifs.
  • CI/CD pour les configurations : automatiser le déploiement des virtualhosts et snippets via GitLab ou un autre outil de pipeline.
  • Monitoring avancé : intégrer VTS, logs dans Grafana, et supervision via Zabbix ou CrowdSec pour un suivi complet de vos services.

Ces sujets peuvent faire l’objet de tutoriels dédiés si vous souhaitez approfondir.

FAQ – Questions fréquentes

Peut-on utiliser cette image sur un serveur non-Debian ?

Oui, l’image fonctionne sur toute distribution Linux capable d’exécuter Docker et Docker Compose. Certaines dépendances (ex : chemins par défaut) peuvent toutefois nécessiter un ajustement.

Faut-il obligatoirement utiliser Cloudflare pour les certificats ?

Non, Cloudflare est utilisé comme exemple. Certbot supporte de nombreux fournisseurs DNS, et vous pouvez adapter le docker-compose.yml en fonction de votre environnement.

Comment gérer plusieurs sites pointant vers le même backend ?

Pour éviter de multiplier les virtualhosts identiques, utilisez un seul virtualhost avec plusieurs server_name, ce qui simplifie la maintenance et réduit les risques d’erreur.

Les snippets peuvent-ils être utilisés dans une installation Nginx “classique” ?

Absolument. Les snippets sont une bonne pratique Nginx qui fonctionne aussi sur un serveur standard, permettant de centraliser les configurations communes et d’améliorer la maintenabilité.

Comment désactiver temporairement un virtualhost ?

Renommez simplement le fichier avec une extension autre que .conf (par exemple .conf.disable). Nginx ignorera ce fichier au prochain rechargement de configuration.

Romain Drouche
Architecte Système | MCSE: Core Infrastructure
Expert en infrastructures IT avec plus de 15 ans d’expérience sur le terrain. Actuellement Chef de projet Systèmes et Réseaux et Référent SSI (Sécurité des Systèmes d’Information), je mets mon expertise au service de la fiabilité et de la sécurité des environnements technologiques.

Laisser un commentaire