guide/deployer-une-app.md

Déployer une application en production

Ce guide décrit la méthode pour mettre en ligne une application PHP/Symfony sur un serveur Linux (Ubuntu récent) derrière un serveur web (Apache) avec PHP-FPM, TLS et un durcissement de base. Il s'adresse à un lecteur qui sait déjà administrer un serveur : ce sont les étapes et les points d'attention, pas un cours pas-à-pas.

Convention : partout où apparaît <VOTRE_DOMAINE>, remplacez par votre domaine réel. Les valeurs d'accès (ports SSH, comptes, chemins d'hébergement) dépendent de votre infrastructure et ne doivent jamais être publiées.

Prérequis

  • Une distribution Linux Ă  jour (Ubuntu LTS ou rĂ©cent).
  • Un accès sudo.
  • Un nom de domaine configurĂ© et pointant vers le serveur.

1) Durcir le serveur

Mettez le système à jour, puis fermez la surface d'attaque.

sudo apt update && sudo apt upgrade -y

Pare-feu (UFW) : n'autorisez que le web et SSH.

sudo apt install ufw
sudo ufw allow 'Apache Full'
sudo ufw allow ssh          # adaptez si vous avez déplacé le port SSH
sudo ufw enable
sudo ufw status verbose

Protection contre le brute-force SSH (Fail2ban) :

sudo apt install fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Activez la prison sshd dans jail.local :

[sshd]
enabled = true
sudo systemctl restart fail2ban

2) Installer la stack

Serveur web, PHP et base de données :

sudo apt install -y apache2 php libapache2-mod-php mysql-server php-mysql
sudo apt install -y php-pdo php-mysql php-zip php-gd php-mbstring \
  php-curl php-xml php-bcmath php-intl php-dom php-simplexml

Activez les modules Apache nécessaires :

sudo a2enmod ssl headers rewrite http2 proxy_fcgi setenvif

Passez en PHP-FPM + MPM event (recommandé en prod : meilleure isolation et concurrence que mod_php + prefork). Remplacez X.Y par votre version de PHP.

sudo a2dismod phpX.Y mpm_prefork
sudo a2enmod mpm_event
sudo apt install phpX.Y-fpm
sudo a2enconf phpX.Y-fpm
sudo systemctl restart apache2 phpX.Y-fpm

3) Configurer le vhost HTTPS

Redirigez tout le trafic HTTP vers HTTPS et déléguez le PHP à FPM via socket.

<VirtualHost *:80>
  ServerName <VOTRE_DOMAINE>
  RewriteEngine On
  RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>

<VirtualHost *:443>
  ServerName <VOTRE_DOMAINE>
  Protocols h2 http/1.1
  DocumentRoot /var/www/<VOTRE_APP>/public

  <Directory /var/www/<VOTRE_APP>/public>
    AllowOverride None
    Require all granted
    FallbackResource /index.php
  </Directory>

  <FilesMatch \.php$>
    SetHandler proxy:unix:/var/run/php/phpX.Y-fpm.sock|fcgi://<VOTRE_DOMAINE>
  </FilesMatch>

  SSLEngine on
  SSLCertificateFile /etc/letsencrypt/live/<VOTRE_DOMAINE>/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/<VOTRE_DOMAINE>/privkey.pem

  ErrorLog ${APACHE_LOG_DIR}/<VOTRE_DOMAINE>-error.log
  CustomLog ${APACHE_LOG_DIR}/<VOTRE_DOMAINE>-access.log combined
</VirtualHost>

Activez le site et rechargez :

sudo a2ensite <VOTRE_DOMAINE>.conf
sudo systemctl reload apache2

4) Obtenir le certificat TLS

Pour un certificat wildcard (<VOTRE_DOMAINE> + *.<VOTRE_DOMAINE>), la validation DNS-01 est nécessaire ; elle passe par un plugin certbot adapté à votre fournisseur DNS.

sudo apt install certbot            # + le plugin DNS de votre fournisseur

Renseignez les credentials DNS dans un fichier lisible uniquement par root :

sudo chmod 600 /etc/letsencrypt/dns-credentials.ini

Demandez le certificat (adaptez le plugin --dns-... Ă  votre fournisseur) :

sudo certbot certonly --dns-<fournisseur> \
  --dns-<fournisseur>-credentials /etc/letsencrypt/dns-credentials.ini \
  -d <VOTRE_DOMAINE> -d "*.<VOTRE_DOMAINE>"

Vérifiez le renouvellement automatique :

sudo certbot renew --dry-run

La validation DNS-01 fonctionne sans port 80 ouvert, mais gardez-le pour la redirection HTTP → HTTPS.

5) Déployer le code

Posez des permissions saines : le propriétaire déploie, le groupe du serveur web lit (et écrit là où l'app en a besoin).

sudo chown -R <deployeur>:www-data /var/www/<VOTRE_APP>
sudo find /var/www/<VOTRE_APP> -type d -exec chmod 770 {} \;
sudo find /var/www/<VOTRE_APP> -type f -exec chmod 660 {} \;
sudo find /var/www/<VOTRE_APP> -type d -exec chmod g+s {} \;   # setgid : héritage de groupe

Installez les dépendances en mode production :

cd /var/www/<VOTRE_APP>
composer install --no-dev --optimize-autoloader

Configurez l'environnement (.env.local). Ne committez jamais ce fichier : il contient des secrets. Générez APP_SECRET et le mot de passe de base localement.

APP_ENV=prod
APP_DEBUG=false
APP_SECRET=<GENERER_UN_SECRET>
DATABASE_URL="mysql://<user>:<motdepasse>@127.0.0.1:3306/<base>?charset=utf8mb4"
MAILER_DSN=sendmail://default

Appliquez les migrations et préparez le cache :

php bin/console doctrine:migrations:migrate --no-interaction
php bin/console cache:clear
php bin/console cache:warmup

Le piège OPcache (à retenir absolument)

En production, opcache.validate_timestamps=0 est recommandé pour la performance, mais il fige le code en mémoire : PHP-FPM continue de servir l'ancien code tant qu'il n'est pas rechargé. Symptôme classique après un déploiement avec changement de schéma : le CLI et les tests sont verts, mais le web plante (par ex. une erreur SQL sur une colonne qui vient d'être supprimée).

Ordre sûr de déploiement :

git pull
php bin/console doctrine:migrations:migrate --no-interaction
php bin/console cache:clear
sudo systemctl reload phpX.Y-fpm     # indispensable : purge l'OPcache

6) Diagnostics rapides

sudo apachectl -t -D DUMP_VHOSTS     # vhosts chargés
sudo apache2ctl -t                   # syntaxe de conf
curl -I --http2 https://<VOTRE_DOMAINE>

7) Durcissement HTTP et TLS (aller plus loin)

Une fois l'app en ligne, renforcez la couche HTTP.

En-têtes de sécurité : appliquez une politique standardisée côté serveur web (HSTS, CSP, X-Content-Type-Options, etc.) via mod_headers. Traitez ce point dans le guide dédié : Durcir les en-têtes HTTP de sécurité.

OCSP Stapling : réduit la latence TLS côté client en servant la réponse OCSP depuis le serveur.

SSLUseStapling On
SSLStaplingCache shmcb:${APACHE_LOG_DIR}/stapling_cache(128000)
SSLStaplingReturnResponderErrors off
SSLStaplingResponderTimeout 5
openssl s_client -connect <VOTRE_DOMAINE>:443 -servername <VOTRE_DOMAINE> \
  -status < /dev/null | grep -A3 "OCSP Response Status"
# Attendu : OCSP Response Status: successful

Compression (Brotli + Gzip en repli) :

<IfModule mod_brotli.c>
  AddOutputFilterByType BROTLI_COMPRESS \
    text/html text/plain text/css application/javascript application/json image/svg+xml
  BrotliCompressionQuality 5
</IfModule>

<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE \
    text/html text/plain text/css application/javascript application/json image/svg+xml
</IfModule>
curl -sI -H "Accept-Encoding: br"   https://<VOTRE_DOMAINE> | grep -i content-encoding
curl -sI -H "Accept-Encoding: gzip" https://<VOTRE_DOMAINE> | grep -i content-encoding

Cache navigateur des assets versionnés : sur des fichiers dont le nom porte un hash, un cache long est sûr.

<IfModule mod_headers.c>
  <FilesMatch "\.(css|js|svg)$">
    Header set Cache-Control "public, max-age=31536000, immutable"
  </FilesMatch>
</IfModule>
ETAG=$(curl -sI https://<VOTRE_DOMAINE>/favicon.svg | awk -F': ' '/^etag:/ {print $2}')
curl -I -H "If-None-Match: $ETAG" https://<VOTRE_DOMAINE>/favicon.svg   # 304 si inchangé

Points d'attention transverses

  • Rechargez PHP-FPM après tout changement de code ou de schĂ©ma — sinon l'OPcache sert du code obsolète (cf. §5).
  • Aucun secret dans le dĂ©pĂ´t : .env.local, credentials DNS, clĂ©s TLS et clĂ©s de dĂ©ploiement restent hors versionnement, avec des permissions restrictives (600).
  • BOM UTF-8 en tĂŞte d'un fichier .php est fatal sous PHP rĂ©cent (declare(strict_types=1) doit ĂŞtre la toute première instruction) : enregistrez vos .php sans BOM.
  • Dossiers inscriptibles partagĂ©s (cache, index, fichiers gĂ©nĂ©rĂ©s) : quand un process (dĂ©ploiement) Ă©crit et qu'un autre (serveur web, www-data) lit et Ă©crit, utilisez un groupe commun + setgid pour que les fichiers créés hĂ©ritent du bon groupe, ou lancez la gĂ©nĂ©ration directement sous le compte du serveur web.

Assistant documentaire

Posez une question sur la documentation. Les réponses citent leurs sources — un clic ouvre le document à gauche.