Aide-mémoire Apache .htaccess
Bases et Prérequis
ObjectifConfiguration / Commande
Activer .htaccess (httpd.conf / apache2.conf) <Directory /var/www/html>
  AllowOverride All  :: activer .htaccess
</Directory>

:: Recharger Apache après modification :
systemctl reload apache2
apachectl graceful
Modules à activer (Debian/Ubuntu) a2enmod rewrite  :: mod_rewrite (URL)
a2enmod headers  :: en-têtes HTTP
a2enmod deflate  :: compression gzip
a2enmod expires  :: cache navigateur
a2enmod auth_basic :: authentification HTTP
systemctl restart apache2
Commentaires # Ceci est un commentaire .htaccess
# Une ligne par directive
mod_rewrite — Réécriture d'URL
Activer le moteur de réécriture RewriteEngine On
RewriteBase /  :: chemin de base (si sous-dossier : /monapp/)
RewriteRule — Syntaxe RewriteRule <pattern> <substitution> [flags]

:: pattern : regex sur l'URL demandée (sans le /)
:: substitution : URL cible, ou - pour ne rien changer
:: flags : L=dernier, R=redirect, F=403, G=410, etc.
RewriteCond — Condition RewriteCond %{VARIABLE} valeur [flags]

:: Variables utiles :
:: %{HTTP_HOST}     :: nom d'hôte
:: %{REQUEST_URI}  :: chemin demandé
:: %{QUERY_STRING} :: paramètres GET
:: %{REQUEST_METHOD}:: GET, POST, etc.
:: %{HTTPS}        :: on si HTTPS
:: %{SERVER_PORT}  :: 80, 443
:: %{REMOTE_ADDR}  :: IP du visiteur
:: %{HTTP_USER_AGENT}:: navigateur client
Réécriture simple — URL propres RewriteEngine On

:: /produit/42 → index.php?id=42
RewriteRule ^produit/([0-9]+)$ index.php?id=$1 [L,QSA]

:: /article/mon-titre → article.php?slug=mon-titre
RewriteRule ^article/([a-z0-9-]+)$ article.php?slug=$1 [L,QSA]

:: Tout vers index.php (Front Controller)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L,QSA]

:: QSA = Query String Append (conserver les paramètres)
Flags courants [L]        :: Last — arrêter le traitement des règles
[R=301]   :: Redirect permanent (301)
[R=302]   :: Redirect temporaire (302)
[NC]       :: No Case — insensible à la casse
[QSA]      :: Query String Append
[NE]       :: No Escape — ne pas encoder les caractères spéciaux
[PT]       :: Pass Through — pour les Alias
[F]        :: Forbidden — retourner 403
[G]        :: Gone — retourner 410
[E=var:val]:: Définir une variable d'environnement
Redirections
HTTP → HTTPS (redirection globale) RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

:: Alternative (port 80)
RewriteCond %{SERVER_PORT} 80
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
www → sans www RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
sans www → www RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
Redirection permanente d'une page Redirect 301 /ancienne-page.html /nouvelle-page
Redirect 301 /ancien-dossier/ /nouveau-dossier/

:: Ou avec RewriteRule
RewriteRule ^ancienne-page\.html$ /nouvelle-page [R=301,L]
Redirection de domaine complet RewriteEngine On
RewriteCond %{HTTP_HOST} ^ancien-domaine\.com$ [NC]
RewriteRule ^ https://nouveau-domaine.com%{REQUEST_URI} [R=301,L]
Redirection extension (.php → sans extension) :: Accéder à /contact au lieu de /contact.php
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^([^\.]+)$ $1.php [L]
Authentification HTTP (Basic Auth)
Protéger un dossier par mot de passe AuthType Basic
AuthName "Zone privée"
AuthUserFile /chemin/absolu/.htpasswd
Require valid-user
Créer / gérer le fichier .htpasswd :: Créer (premier utilisateur)
htpasswd -c /chemin/.htpasswd jean

:: Ajouter un utilisateur
htpasswd /chemin/.htpasswd marie

:: Supprimer un utilisateur
htpasswd -D /chemin/.htpasswd jean

:: Mot de passe en ligne de commande (non interactif)
htpasswd -b /chemin/.htpasswd jean motdepasse
Autoriser un seul utilisateur AuthType Basic
AuthName "Admin"
AuthUserFile /chemin/.htpasswd
Require user jean
Exclure certains fichiers de l'auth AuthType Basic
AuthName "Zone privée"
AuthUserFile /chemin/.htpasswd
<Files "webhook.php">
  Satisfy Any
  Allow from all
</Files>
Require valid-user
Contrôle d'Accès
Bloquer / autoriser par IP :: Apache 2.4+
Require ip 192.168.1.0/24
Require ip 10.0.0.1
Require all denied

:: Bloquer une IP
<RequireAll>
  Require all granted
  Require not ip 192.168.1.5
</RequireAll>
Bloquer l'accès à certains fichiers <FilesMatch "\.(log|sql|bak|env|json|md|git)$">
  Require all denied
</FilesMatch>

:: Bloquer .htaccess et .htpasswd eux-mêmes
<Files ~ "^\.ht">
  Require all denied
</Files>
Interdire le listage de dossier Options -Indexes

:: Ou pour activer le listage :
Options +Indexes
Bloquer par User-Agent RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (bot|spider|crawler) [NC]
RewriteRule .* - [F,L]
Hotlinking (protéger les images) RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?mondomaine\.com [NC]
RewriteRule \.(jpg|jpeg|png|gif|webp)$ - [F,L]
Headers de Sécurité
HSTS (forcer HTTPS) :: À activer uniquement quand HTTPS est stable !
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Frame-Options (anti-clickjacking) Header always set X-Frame-Options "SAMEORIGIN"
:: DENY = bloquer totalement
:: SAMEORIGIN = autoriser uniquement le même domaine
X-Content-Type-Options Header always set X-Content-Type-Options "nosniff"
Referrer-Policy Header always set Referrer-Policy "strict-origin-when-cross-origin"
Content-Security-Policy (CSP) Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"

:: CSP permissive pour débuter :
Header always set Content-Security-Policy "default-src *; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
Masquer la version Apache :: Dans httpd.conf (pas .htaccess)
ServerSignature Off
ServerTokens Prod
Supprimer/modifier un header Header unset X-Powered-By
Header always unset X-Powered-By
Header set X-Powered-By "PHP"
Cache Navigateur (mod_expires)
Configurer les durées de cache ExpiresActive On
ExpiresDefault "access plus 1 month"

:: Par type de fichier
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType application/json "access plus 0 seconds"
ExpiresByType text/html "access plus 0 seconds"
Cache-Control via Header <FilesMatch "\.(css|js|jpg|png|webp|woff2)$">
  Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>

<FilesMatch "\.(html|php)$">
  Header set Cache-Control "no-cache, must-revalidate"
</FilesMatch>
Compression (mod_deflate)
Activer la compression Gzip SetOutputFilter DEFLATE

:: Ou plus précis avec mod_deflate :
AddOutputFilterByType DEFLATE text/html text/plain text/xml
AddOutputFilterByType DEFLATE text/css application/javascript
AddOutputFilterByType DEFLATE application/json application/xml
AddOutputFilterByType DEFLATE image/svg+xml

:: Exclure les navigateurs anciens
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
Pages d'Erreur Personnalisées
Définir des pages d'erreur ErrorDocument 400 /erreurs/400.html
ErrorDocument 401 /erreurs/401.html
ErrorDocument 403 /erreurs/403.html
ErrorDocument 404 /erreurs/404.php
ErrorDocument 500 /erreurs/500.html

:: Page d'erreur avec message texte
ErrorDocument 404 "Page non trouvée"
Paramètres PHP (si mod_php)
Modifier des directives PHP :: Taille des uploads
php_value upload_max_filesize 64M
php_value post_max_size 64M
php_value max_execution_time 300
php_value memory_limit 256M

:: Masquer les erreurs PHP en production
php_flag display_errors Off
php_flag log_errors On
php_value error_log /var/log/php_errors.log

:: Timezone
php_value date.timezone Europe/Paris
CORS (Cross-Origin Resource Sharing)
Autoriser toutes les origines (API publique) Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Header always set Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With"
Autoriser une origine spécifique SetEnvIf Origin "^https://app\.mondomaine\.com$" CORS_ORIGIN=$0
Header always set Access-Control-Allow-Origin "%{CORS_ORIGIN}e" env=CORS_ORIGIN
Header always set Access-Control-Allow-Credentials "true"
Répondre aux requêtes OPTIONS (preflight) RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* - [R=204,L]
Types MIME et Encodage
Ajouter des types MIME AddType application/font-woff2 .woff2
AddType image/webp .webp
AddType application/manifest+json .webmanifest
AddType text/cache-manifest .appcache
Encodage des fichiers texte AddDefaultCharset UTF-8
AddCharset UTF-8 .html .php .css .js .json