Installer Nginx et un certificat HTTPS Let's Encrypt sur un VPS

De l'enregistrement DNS au cadenas vert : un hôte virtuel Nginx propre, un certificat Let's Encrypt qui se renouvelle tout seul et les en-têtes qui vont avec.

Fenêtre de navigateur en HTTPS avec un sceau de certificat

Mettre un site en ligne sur un VPS demande quatre briques : un enregistrement DNS qui pointe vers la machine, un serveur web qui répond, un certificat TLS valide et un renouvellement qui ne dépend pas de votre mémoire. Comptez vingt minutes.

Le guide part d'une VM Ubuntu 24.04 déjà durcie, si ce n'est pas le cas, commencez par la sécurisation de base, c'est l'étape qui conditionne tout le reste.

1. Pointer le DNS, en A et en AAAA

Chez votre registraire ou votre hébergeur DNS, créez deux enregistrements vers les adresses de la VM. Publier uniquement un enregistrement A revient à rendre le site invisible depuis les réseaux mobiles en IPv6 seul, de plus en plus nombreux.

zone DNS
exemple.com.      300  IN  A     198.51.100.42
exemple.com.      300  IN  AAAA  2602:xxxx:xxxx::10
www.exemple.com.  300  IN  CNAME exemple.com.

Vérifiez la propagation avant d'aller plus loin, Certbot échouera tant que le nom ne résout pas vers la bonne machine.

bash
dig +short exemple.com A
dig +short exemple.com AAAA

2. Installer Nginx

bash
sudo apt update
sudo apt install -y nginx
sudo ufw allow 'Nginx Full'
systemctl status nginx --no-pager

La page « Welcome to nginx » doit répondre sur l'adresse IP. Si le port 80 reste muet, le pare-feu est presque toujours en cause : sudo ufw status tranche en une seconde.

3. Écrire un bloc serveur propre

Un site par fichier dans sites-available, activé par lien symbolique. Cette séparation vous permettra d'en désactiver un sans toucher aux autres.

nginx
sudo mkdir -p /var/www/exemple.com
sudo chown -R $USER:$USER /var/www/exemple.com
echo '<h1>exemple.com</h1>' > /var/www/exemple.com/index.html

sudo tee /etc/nginx/sites-available/exemple.com <<'EOF'
server {
    listen 80;
    listen [::]:80;

    server_name exemple.com www.exemple.com;
    root /var/www/exemple.com;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}
EOF

sudo ln -s /etc/nginx/sites-available/exemple.com /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx

La ligne listen [::]:80; n'est pas décorative : sans elle, Nginx n'écoute qu'en IPv4 et votre AAAA pointe vers le vide.

4. Obtenir le certificat Let's Encrypt

Le greffon Nginx de Certbot lit vos blocs serveur, obtient le certificat et réécrit la configuration pour ajouter le bloc TLS. C'est la voie la plus courte et la moins sujette aux erreurs.

bash
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d exemple.com -d www.exemple.com \
     --agree-tos -m admin@exemple.com --redirect --no-eff-email

--redirect ajoute la redirection permanente de HTTP vers HTTPS, indispensable côté référencement, où deux versions accessibles d'une même page diluent le signal.

Validation HTTP-01 : Let's Encrypt contacte votre serveur sur le port 80, y compris en IPv6 si un AAAA existe. Un AAAA publié vers une adresse qui ne répond pas fait échouer l'émission, même si l'IPv4 fonctionne parfaitement. Publiez l'AAAA une fois le service joignable, pas avant.

5. Confirmer le renouvellement automatique

Le paquet installe une minuterie systemd qui tente le renouvellement deux fois par jour et n'agit que dans les trente derniers jours de validité. Vérifiez-la plutôt que de la supposer.

bash
systemctl list-timers certbot.timer
sudo certbot renew --dry-run

Un --dry-run réussi est la seule preuve qui compte. Le reste est une hypothèse que vous découvrirez fausse quatre-vingt-dix jours plus tard.

6. Durcir la configuration TLS

Certbot produit une base saine. Trois ajouts la complètent : HTTP/2, HSTS et les en-têtes de sécurité usuels. À placer dans le bloc server qui écoute sur le port 443.

nginx
listen 443 ssl;
listen [::]:443 ssl;
http2 on;

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

gzip on;
gzip_vary on;
gzip_min_length 512;
gzip_types text/css application/javascript image/svg+xml application/json;

HSTS : une fois l'en-tête envoyé, les navigateurs refuseront le HTTP en clair sur ce domaine pendant toute la durée annoncée. Commencez par max-age=300 le temps de valider la chaîne, puis passez à un an.

bash
sudo nginx -t && sudo systemctl reload nginx
curl -I https://exemple.com

7. Vérifier

  • curl -I https://exemple.com renvoie 200 et l'en-tête HSTS.
  • curl -6 -I https://exemple.com répond aussi, l'IPv6 est bien servie.
  • http://exemple.com redirige en 301 vers HTTPS.
  • sudo certbot certificates affiche une expiration à environ 90 jours.
  • Une analyse SSL Labs remonte au moins un A.

À partir de là, remplacer le contenu de /var/www/exemple.com suffit à publier un vrai site. Pour une application dynamique, ajoutez un bloc location avec proxy_pass vers le port local de votre service, le certificat, lui, ne change pas.