---
title: "Hardening Web — Headers HTTP & TLS"
domain: security
subdomain: hardening
type: snippet
tags: [hardening, nginx, apache, TLS, headers, HSTS, CSP, web-security]
difficulty: intermediate
status: stable
updated: "2025-05-10"
---
## Headers de sécurité HTTP

### Nginx

```nginx
# /etc/nginx/conf.d/security-headers.conf

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 "camera=(), microphone=(), geolocation=()" always;
add_header X-XSS-Protection "1; mode=block" always;

# Content Security Policy (adapter selon l'application)
add_header Content-Security-Policy "
  default-src 'self';
  script-src 'self' 'nonce-RANDOM_NONCE';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  font-src 'self';
  connect-src 'self';
  frame-ancestors 'none';
  form-action 'self';
  base-uri 'self'
" always;

# Masquer la version
server_tokens off;
```

### Apache

```apache
# /etc/apache2/conf-available/security-headers.conf

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 X-XSS-Protection "1; mode=block"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"

# Masquer la version et la signature
ServerTokens Prod
ServerSignature Off

# Activer
a2enconf security-headers
a2enmod headers
systemctl reload apache2
```

## Configuration TLS

### Nginx — config TLS moderne

```nginx
# /etc/nginx/conf.d/ssl.conf

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;

ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;

# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;

# Certificat
ssl_certificate     /etc/letsencrypt/live/{{TARGET_DOMAIN}}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{TARGET_DOMAIN}}/privkey.pem;

# DH params (4096 bits)
ssl_dhparam /etc/nginx/dhparam.pem;
```

```bash
# Générer les paramètres DH
openssl dhparam -out /etc/nginx/dhparam.pem 4096
```

### Apache — config TLS moderne

```apache
# /etc/apache2/mods-available/ssl.conf

SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLSessionTickets off
SSLUseStapling on
SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"
```

## Let's Encrypt — Certbot

```bash
# Nginx
certbot --nginx -d {{TARGET_DOMAIN}} -d www.{{TARGET_DOMAIN}}

# Apache
certbot --apache -d {{TARGET_DOMAIN}}

# Standalone (pause nginx/apache)
certbot certonly --standalone -d {{TARGET_DOMAIN}}

# Wildcard (DNS challenge requis)
certbot certonly --manual --preferred-challenges=dns -d "*.{{TARGET_DOMAIN}}"

# Renouvellement automatique (déjà configuré via systemd timer)
certbot renew --dry-run

# Voir les certificats
certbot certificates
```

## Test de la configuration

```bash
# testssl.sh — audit TLS complet
bash testssl.sh {{TARGET_DOMAIN}}
bash testssl.sh --severity CRITICAL {{TARGET_DOMAIN}}

# SSL Labs (en ligne)
# https://www.ssllabs.com/ssltest/

# nmap — vérifier les ciphers
nmap --script ssl-enum-ciphers -p 443 {{TARGET}}

# openssl — tester une connexion
openssl s_client -connect {{TARGET_DOMAIN}}:443 -tls1_3
openssl s_client -connect {{TARGET_DOMAIN}}:443 | openssl x509 -noout -dates

# curl — vérifier les headers
curl -sI https://{{TARGET_DOMAIN}} | grep -i "strict\|x-frame\|content-security\|x-content"
```

## Headers — Analyse et score

```bash
# securityheaders.com (en ligne)
# https://securityheaders.com/?q={{TARGET_DOMAIN}}&followRedirects=on

# Localement avec curl
curl -s -D - https://{{TARGET_DOMAIN}} -o /dev/null | grep -i "^strict\|^x-frame\|^content-sec\|^referrer\|^x-content\|^permissions"
```

## HSTS Preload

```bash
# Pour être dans la liste preload des navigateurs :
# 1. max-age >= 31536000 (1 an)
# 2. includeSubDomains obligatoire
# 3. preload keyword
# 4. Soumettre sur : https://hstspreload.org/

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
```

## Redirect HTTP → HTTPS

```nginx
# Nginx
server {
    listen 80;
    server_name {{TARGET_DOMAIN}};
    return 301 https://$host$request_uri;
}
```

```apache
# Apache
<VirtualHost *:80>
    ServerName {{TARGET_DOMAIN}}
    Redirect permanent / https://{{TARGET_DOMAIN}}/
</VirtualHost>
```

<Tip>
Cible pour un site public : score A+ sur SSL Labs et grade A sur securityheaders.com. Le CSP est le header le plus complexe à configurer correctement — commencer en mode `report-only` pour détecter les violations sans bloquer.
</Tip>
