MDstable
NoteSnippetChecklistPlaybook

Serveur Web — Configuration & Durcissement

Apache et Nginx : TLS, headers de sécurité, hardening, virtual hosts, reverse proxy

snippetintermediate 2026-05-14 5 min read
apachenginxtlshttpshardeningheadersreverse-proxycsphstsssl

Apache

Installation et structure

bash
apt install apache2
a2enmod ssl rewrite headers http2
# Structure
/etc/apache2/apache2.conf # config principale
/etc/apache2/sites-available/ # vhosts disponibles
/etc/apache2/sites-enabled/ # vhosts actifs (symlinks)
/etc/apache2/conf-available/ # configs supplémentaires

Masquer la version et le banner

apache
# /etc/apache2/conf-available/security.conf
ServerTokens Prod # affiche "Apache" uniquement, sans version
ServerSignature Off # supprime la signature dans les pages d'erreur
TraceEnable Off # désactive la méthode HTTP TRACE

Virtual Host HTTPS

apache
# /etc/apache2/sites-available/example.com.conf
<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
DocumentRoot /var/www/example.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.com.crt
SSLCertificateKeyFile /etc/ssl/private/example.com.key
SSLCertificateChainFile /etc/ssl/certs/ca-bundle.crt
# TLS — désactiver les protocoles obsolètes
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:!aNULL:!MD5:!RC4
SSLHonorCipherOrder on
SSLCompression off
SSLSessionTickets off
# Headers de sécurité
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Frame-Options "DENY"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
# Supprimer les headers qui exposent la stack
Header unset X-Powered-By
Header unset Server
# Interdire le listing de répertoires
<Directory /var/www/example.com>
Options -Indexes -FollowSymLinks
AllowOverride None
Require all granted
</Directory>
# Logs
ErrorLog ${APACHE_LOG_DIR}/example.com_error.log
CustomLog ${APACHE_LOG_DIR}/example.com_access.log combined
</VirtualHost>

Reverse Proxy Apache

apache
a2enmod proxy proxy_http proxy_balancer lbmethod_byrequests
<VirtualHost *:443>
ServerName app.example.com
SSLEngine on
# ... (cert TLS comme ci-dessus)
# Proxy vers le backend
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
# Timeout backend
ProxyTimeout 60
# Masquer le backend dans les réponses
Header unset X-Powered-By
</VirtualHost>

Limiter les méthodes HTTP

apache
<Directory /var/www/example.com>
<LimitExcept GET POST HEAD>
Require all denied
</LimitExcept>
</Directory>

Commandes de gestion

bash
# Vérifier la configuration
apachectl configtest
# Activer/désactiver un vhost
a2ensite examplecomconf
a2dissite examplecomconf
systemctl reload apache2
# Vérifier les modules actifs
apache2ctl -M | sort
# Voir les vhosts configurés
apache2ctl -S

Nginx

Configuration globale — /etc/nginx/nginx.conf

nginx
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 1024;
multi_accept on;
}
http {
# Masquer la version Nginx
server_tokens off;
# Timeouts (protection contre slowloris)
client_body_timeout 12;
client_header_timeout 12;
keepalive_timeout 15;
send_timeout 10;
# Taille max des requêtes
client_max_body_size 10M;
# Logs
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
include /etc/nginx/mime.types;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

Virtual Host HTTPS — /etc/nginx/sites-available/example.com

nginx
# Redirection HTTP → HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
# TLS
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
ssl_dhparam /etc/ssl/certs/dhparam.pem; # openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:!aNULL:!MD5:!RC4;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
# Headers de sécurité
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" always;
# Interdire le listing de répertoires
autoindex off;
# Bloquer les méthodes non nécessaires
if ($request_method !~ ^(GET|POST|HEAD)$) {
return 405;
}
location / {
try_files $uri $uri/ =404;
}
# Bloquer l'accès aux fichiers sensibles
location ~ /\. {
deny all;
}
location ~* \.(env|log|conf|bak|sql|git)$ {
deny all;
}
}

Reverse Proxy Nginx

nginx
upstream backend {
server 127.0.0.1:8080;
server 127.0.0.1:8081 backup;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name app.example.com;
# ... (TLS et headers comme ci-dessus)
location / {
proxy_pass http://backend;
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 Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 60s;
# Masquer les headers du backend
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
}
}

Commandes de gestion

bash
# Générer le paramètre Diffie-Hellman
openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
# Vérifier la configuration
nginx -t
# Recharger sans interruption
systemctl reload nginx
# Voir les vhosts actifs
nginx -T | grep server_name
# Activer un vhost
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

Certificats SSL/TLS

bash
# Let's Encrypt (Certbot) — Apache
apt install certbot python3-certbot-apache
certbot --apache -d examplecom -d wwwexamplecom
# Let's Encrypt — Nginx
apt install certbot python3-certbot-nginx
certbot --nginx -d examplecom
# Renouvellement automatique (cron)
certbot renew --dry-run
# Certificat auto-signé (tests internes)
openssl req -x509 -nodes -days 365 -newkey rsa2048
-keyout /etc/ssl/private/self-signed.key
-out /etc/ssl/certs/self-signed.crt
-subj "/CN=srv-web.example.local/O=Example/C=FR"

Vérification et tests

bash
# Headers HTTP retournés
curl -sI https//example.com
# Audit TLS complet
testsslsh https//example.com
# Points à valider : TLS 1.0/1.1 désactivés, Forward Secrecy, HSTS
# Ciphers disponibles
nmap --script ssl-enum-ciphers -p 443 examplecom
# Méthodes HTTP autorisées
curl -sI -X OPTIONS https//example.com | grep Allow
curl -sI -X TRACE https//example.com # doit retourner 405
# Listing de répertoires
curl -s https//example.com/images/ | grep -i "Index of"
# Vérifier le banner serveur (doit être absent ou générique)
curl -sI https//example.com | grep -iE "^Server:|^X-Powered-By:"
Checklist0/14
💡 Tip —

Générer le paramètre Diffie-Hellman personnalisé (openssl dhparam 2048) avant de déployer HTTPS — sans ça, Nginx utilise des paramètres DH par défaut parfois faibles selon la version.

OPS·BRAIN v1.027 notes · Networklocal