Situation actuelle : PHP2Shell. Deux failles PHP critiques (CVSS 9.2) permettent d'exécuter du code malveillant sans authentification. Correctifs disponibles depuis le 21 octobre 2026.Ce qu'il faut faire maintenant →

Configurer Nginx pour PHP en toute sécurité

La plupart des failles des installations Nginx pour PHP ne viennent pas de Nginx, mais de quelques lignes de la location PHP. Voici comment configurer la transmission à PHP-FPM pour que seuls les scripts voulus soient exécutés.

État 7 min de lectureDurcissement et configuration

En bref : pour configurer Nginx pour PHP en toute sécurité, ne transmettez à PHP-FPM que des scripts qui existent réellement, et pour les applications à contrôleur frontal uniquement index.php. S'y ajoutent des dotfiles bloqués comme .git et .env, un serveur par défaut pour les noms d'hôte inconnus, des limites de taille adaptées et security.limit_extensions = .php dans le pool. Cela ne protège pas des failles de PHP lui-même, mais limite ce qui est exécutable après une erreur.

Les lignes les plus dangereuses se trouvent dans la location PHP

Depuis PHP2Shell, de nombreuses équipes vérifient leurs versions et pools PHP-FPM, comme le décrit la page Sécuriser Nginx avec PHP-FPM, et redémarrent tous les workers après la mise à jour. Un second regard porte sur la configuration de Nginx elle-même. C'est là que se décide quels fichiers sont exécutés comme PHP.

Dans de nombreuses configurations, un bloc transmet à PHP-FPM toute requête vers .php, quel que soit l'emplacement du fichier. Si un fichier PHP injecté atterrit dans un dossier d'upload, le serveur l'exécute. D'une erreur de fichier, on fait ainsi une porte dérobée.

Cet article se concentre sur le côté Nginx : quelles directives comptent, comment l'ordre des blocs location agit et comment tester le résultat.

Comment Nginx décide quel bloc s'applique

Qui veut configurer Nginx pour PHP en toute sécurité doit connaître la sélection des blocs location. Selon la documentation de Nginx, elle se déroule ainsi :

  1. Une correspondance exacte avec location = met fin immédiatement à la recherche.
  2. Parmi les locations par préfixe, la plus longue correspondante est mémorisée. Si elle porte ^~, plus aucune expression régulière n'est vérifiée.
  3. Sinon, les expressions régulières (~, ~*) sont vérifiées dans l'ordre du fichier de configuration ; la première correspondance l'emporte.
  4. Si aucune expression régulière ne correspond, la location par préfixe mémorisée s'applique.

Deux règles pratiques en découlent. Un blocage de PHP dans les dossiers d'upload doit être défini soit comme préfixe ^~, soit comme expression régulière placée avant le bloc PHP général. Et une location par préfixe ordinaire comme location /uploads/ ne protège pas, car l'expression régulière pour .php la supplante.

Une configuration de base sûre

L'exemple suivant montre un bloc server pour une application à contrôleur frontal, comme c'est courant avec Laravel, Symfony, Shopware 6 ou de nombreux développements internes. Adaptez les chemins, le socket et les certificats.

server {
    listen 443 ssl;
    server_name www.example.de;
    root /var/www/seite1/public;
    index index.php;

    ssl_certificate     /etc/ssl/seite1/fullchain.pem;
    ssl_certificate_key /etc/ssl/seite1/privkey.pem;

    server_tokens off;
    client_max_body_size 32m;

    # Autoriser Let's Encrypt et d'autres chemins connus
    location ^~ /.well-known/ {
        try_files $uri =404;
    }

    # Bloquer les fichiers et dossiers cachés (.git, .env, .htaccess)
    location ~ /\. {
        deny all;
    }

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    # Seul le contrôleur frontal est transmis à PHP-FPM
    location = /index.php {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        fastcgi_hide_header X-Powered-By;
        fastcgi_pass unix:/run/php/seite1.sock;
    }

    # Tout autre fichier PHP n'est pas exécuté
    location ~ \.php$ {
        return 404;
    }
}

$realpath_root résout les liens symboliques. C'est important pour les déploiements avec un lien symbolique changeant vers la release actuelle, afin que PHP-FPM et OPcache voient le bon chemin. Notez en outre : selon la documentation de Nginx, les directives fastcgi_param ne sont héritées des niveaux supérieurs que si le niveau actuel n'en comporte aucune. C'est pourquoi include fastcgi_params; doit figurer dans le même bloc.

Les CMS classiques comme WordPress, TYPO3 ou Contao ont plusieurs points d'entrée. Là, remplacez le bloc du contrôleur frontal par une location PHP générale qui ne transmet que les fichiers existants (try_files $uri =404;) et bloquez l'exécution dans tous les dossiers accessibles en écriture. La manière d'y parvenir est décrite dans Interdire l'exécution de PHP dans les dossiers d'upload.

La seconde protection dans PHP-FPM

Nginx décide de ce qui est transmis, PHP-FPM décide de ce qu'il exécute. Selon le manuel PHP, PHP-FPM exécute par défaut les fichiers .php et .phar et recommande lui-même de limiter cela à .php. Cela rattrape les erreurs de configuration dans le serveur web.

; dans le pool, par ex. /etc/php/8.3/fpm/pool.d/seite1.conf
security.limit_extensions = .php
listen = /run/php/seite1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

Le socket ne devrait être accessible qu'à Nginx. Les ports FPM sur le réseau, par exemple 9000, ne doivent pas être ouverts vers l'extérieur. La manière dont le serveur n'expose globalement que les ports nécessaires est décrite dans Pare-feu pour serveurs web : n'ouvrir que les ports nécessaires.

Noms d'hôte inconnus et autres leviers

Sans serveur par défaut dédié, Nginx répond aux requêtes vers des noms d'hôte inconnus avec le premier bloc server correspondant. Les analyses automatisées qui n'interrogent que l'adresse IP atterrissent alors directement dans votre application. Un serveur par défaut dédié met fin à de telles requêtes sans réponse.

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;
    ssl_reject_handshake on;
    return 444;
}

Autres points qui ont fait leurs preuves :

Levier Recommandation Objectif
server_tokens off aucun numéro de version dans les en-têtes et pages d'erreur
client_max_body_size adapté à post_max_size rejeter tôt les requêtes volumineuses
autoindex désactivé (par défaut) aucune liste de répertoires
fastcgi_hide_header X-Powered-By aucune version de PHP vers l'extérieur
Pages de statut pm.status_path uniquement en interne aucune donnée d'exploitation publique
Fichiers de sauvegarde bloquer ou supprimer .sql, .zip, .bak dans la racine web aucun téléchargement de bases de données

Plus de détails sur les indications de version dans l'article expose_php et en-têtes Server.

Un autre levier concerne les méthodes HTTP autorisées. La plupart des sites n'ont besoin que de GET, HEAD et POST. Avec limit_except, d'autres méthodes peuvent être refusées dans une location ; selon la documentation de Nginx, autoriser GET autorise automatiquement aussi HEAD. Vérifiez au préalable si les interfaces de votre application utilisent par exemple PUT ou DELETE, car les API REST le font souvent.

Tester avant de recharger

Vérifiez chaque modification d'abord avec nginx -t, puis rechargez Nginx seulement ensuite. Testez ensuite le comportement de l'extérieur avec des requêtes normales vers votre propre domaine :

sudo nginx -t && sudo systemctl reload nginx
# Attendu : 403 ou 404
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.git/HEAD
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.env
# Attendu : 404, car seul le contrôleur frontal est exécuté
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/info.php
# Quels blocs transmettent à PHP-FPM ?
sudo nginx -T 2>/dev/null | grep -nE "location|fastcgi_pass"

Avant chaque modification importante, sauvegardez avec la date la sortie complète de nginx -T. Elle contient toute la configuration effective, y compris tous les fichiers inclus. Si quelque chose tourne mal après une modification, la comparaison montre immédiatement ce qui diffère. Lors de la prochaine faille critique, vous disposez en outre d'une base fiable pour l'état des lieux, sans avoir à rassembler les fichiers au préalable.

Astuce

Modifiez d'abord la configuration sur un environnement de test ou sur une copie du bloc server. Un point d'entrée oublié, par exemple un callback de paiement, ne se remarque sinon que lorsque les commandes n'arrivent plus.

Comment PHP SOS aide-t-il concrètement ?

  • Lire la configuration : nous analysons l'ensemble de la configuration Nginx avec nginx -T et rattachons chaque transmission à PHP à son site et à son pool.
  • Définir les points d'entrée : avec vos développeurs, nous déterminons quels scripts doivent être exécutés et bloquons le reste.
  • Mettre en œuvre et tester : nous adaptons les blocs server et les pools, vérifions avec nginx -t et testons formulaires, moyens de paiement et uploads.
  • Sécuriser durablement : la configuration devient partie intégrante de notre durcissement de serveur et de la documentation de votre serveur.

Questions fréquentes

Faut-il redémarrer Nginx après une mise à jour de PHP ?

Pas pour le correctif PHP, car Nginx ne contient pas PHP. Ce sont tous les services PHP-FPM qui doivent être redémarrés. Si vous modifiez aussi la configuration de Nginx, vérifiez-la d'abord avec nginx -t, puis rechargez Nginx.

Pourquoi ne transmettre que index.php à PHP ?

De nombreuses applications modernes ont un unique point d'entrée, le contrôleur frontal. Si seul ce fichier est transmis à PHP-FPM, un fichier PHP injecté ailleurs ne peut pas être exécuté via le navigateur. Pour les systèmes comportant de nombreux points d'entrée, ce schéma n'est pas possible.

Que apporte security.limit_extensions dans PHP-FPM ?

Ce paramètre définit quelles extensions de fichiers PHP-FPM exécute comme script. Selon le manuel PHP, la valeur par défaut est .php .phar ; il est recommandé de se limiter à .php. Cela rattrape les erreurs dans la configuration du serveur web, mais ne la remplace pas.

Les règles Nginx protègent-elles contre PHP2Shell ?

Non. PHP2Shell se trouve dans l'interpréteur PHP et n'est fermé que par la mise à jour vers une version corrigée et le redémarrage de tous les workers. Une configuration Nginx sûre réduit la surface d'attaque et limite les dégâts après une intrusion.

Sources

Contact immédiat

Chaque heure compte. Écrivez-nous maintenant.

Décrivez brièvement votre système, votre version de PHP et ce que vous avez observé. Nous vous répondons, faisons le point et travaillons à distance jusqu'à la clôture et la documentation de l'incident.

Aide immédiate