Actuele situatie: PHP2Shell. Twee kritieke PHP-kwetsbaarheden (CVSS 9.2) maken schadelijke code zonder inloggen mogelijk. Patches sinds 21-10-2026.Wat u nu moet doen →

Nginx veilig configureren voor PHP

De meeste zwakke plekken in Nginx-opstellingen voor PHP zitten niet in Nginx, maar in enkele regels van de PHP-location. Zo configureert u de overdracht aan PHP-FPM zodat alleen gewenste scripts worden uitgevoerd.

Stand 6 min. leestijdHardening en configuratie

Kort gezegd: Om Nginx veilig voor PHP te configureren, geeft u alleen scripts aan PHP-FPM door die werkelijk bestaan, bij front-controllerapplicaties zelfs alleen de index.php. Daarbij komen geblokkeerde dotfiles zoals .git en .env, een standaardserver voor onbekende hostnamen, passende groottegrenzen en security.limit_extensions = .php in de pool. Dat beschermt niet tegen kwetsbaarheden in PHP zelf, maar beperkt wat na een fout uitvoerbaar is.

De gevaarlijkste regels staan in de PHP-location

Sinds PHP2Shell controleren veel teams hun PHP-FPM-versies en pools, zoals de pagina Nginx met PHP-FPM beveiligen beschrijft, en herstarten na de update alle workers. Een tweede blik gaat naar de Nginx-configuratie zelf. Daar wordt bepaald welke bestanden als PHP worden uitgevoerd.

In veel configuraties staat een blok dat elk verzoek naar .php aan PHP-FPM doorgeeft, waar het bestand ook staat. Belandt een ingebracht PHP-bestand in een uploadmap, dan voert de server het uit. Van een bestandsfout wordt zo een achterdeur.

Dit artikel richt zich op de Nginx-kant: welke directieven tellen, hoe de volgorde van de location-blokken werkt en hoe u het resultaat test.

Hoe Nginx beslist welk blok geldt

Wie Nginx veilig voor PHP wil configureren, moet de selectie van location-blokken kennen. Volgens de Nginx-documentatie verloopt die zo:

  1. Een exacte treffer met location = beëindigt de zoektocht meteen.
  2. Onder de prefix-locations wordt de langste passende onthouden. Draagt ze ^~, dan worden geen reguliere expressies meer gecontroleerd.
  3. Anders worden reguliere expressies (~, ~*) gecontroleerd in de volgorde van het configuratiebestand; de eerste treffer wint.
  4. Geeft geen reguliere expressie een treffer, dan geldt de onthouden prefix-location.

Daaruit volgen twee praktische regels. Een blokkade voor PHP in uploadmappen moet ofwel als ^~-prefix worden aangelegd, ofwel als reguliere expressie vóór het algemene PHP-blok staan. En een gewone prefix-location als location /uploads/ beschermt niet, omdat de reguliere expressie voor .php haar overrulet.

Een veilige basisconfiguratie

Het volgende voorbeeld toont een serverblok voor een applicatie met front controller, zoals gebruikelijk bij Laravel, Symfony, Shopware 6 of veel eigen ontwikkelingen. Paden, socket en certificaten past u aan.

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;

    # Let's Encrypt en andere bekende paden toestaan
    location ^~ /.well-known/ {
        try_files $uri =404;
    }

    # Verborgen bestanden en mappen blokkeren (.git, .env, .htaccess)
    location ~ /\. {
        deny all;
    }

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

    # Alleen de front controller wordt aan PHP-FPM doorgegeven
    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;
    }

    # Elk ander PHP-bestand wordt niet uitgevoerd
    location ~ \.php$ {
        return 404;
    }
}

$realpath_root lost symbolische links op. Dat is bij deployments met een wisselende symlink naar de actuele release belangrijk, zodat PHP-FPM en OPcache het juiste pad zien. Let bovendien op: volgens de Nginx-documentatie worden fastcgi_param-directieven alleen van hogere niveaus geërfd als er op het huidige niveau geen eigen staan. Daarom hoort include fastcgi_params; in hetzelfde blok.

Klassieke CMS'en als WordPress, TYPO3 of Contao hebben meerdere ingangspunten. Daar vervangt u het front-controllerblok door een algemene PHP-location die alleen bestaande bestanden doorgeeft (try_files $uri =404;) en blokkeert u de uitvoering in alle beschrijfbare mappen. Hoe dat lukt, staat onder PHP-uitvoering in uploadmappen verbieden.

De tweede beveiliging in PHP-FPM

Nginx beslist wat wordt doorgegeven, PHP-FPM beslist wat het uitvoert. Volgens de PHP-handleiding voert PHP-FPM standaard bestanden met .php en .phar uit en adviseert het zelf dat te beperken tot .php. Dat vangt configuratiefouten in de webserver op.

; in de pool, bijv. /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

De socket mag alleen voor Nginx bereikbaar zijn. FPM-poorten op het netwerk, zoals 9000, horen niet naar buiten te worden geopend. Hoe de server in totaal alleen de noodzakelijke poorten toont, beschrijft Firewall voor webservers: alleen noodzakelijke poorten openen.

Onbekende hostnamen en verdere stelschroeven

Zonder eigen standaardserver beantwoordt Nginx verzoeken naar onbekende hostnamen met het eerste passende serverblok. Geautomatiseerde scans die alleen het IP-adres aanspreken, belanden dan rechtstreeks in uw applicatie. Een eigen standaardserver beëindigt zulke verzoeken zonder antwoord.

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

Verdere punten die zich hebben bewezen:

Stelschroef Aanbeveling Doel
server_tokens off geen versienummer in headers en foutpagina's
client_max_body_size passend bij post_max_size grote verzoeken vroeg weigeren
autoindex uit (standaard) geen maplijsten
fastcgi_hide_header X-Powered-By geen PHP-versie naar buiten
Statuspagina's pm.status_path alleen intern geen bedrijfsgegevens openbaar
Back-upbestanden .sql, .zip, .bak in de webroot blokkeren of verwijderen geen download van databases

Meer over versiegegevens staat in het artikel expose_php en Server-header.

Een andere stelschroef betreft de toegestane HTTP-methoden. De meeste websites hebben alleen GET, HEAD en POST nodig. Met limit_except kunnen andere methoden in een location worden geweigerd; volgens de Nginx-documentatie staat de vrijgave van GET automatisch ook HEAD toe. Controleer vooraf of interfaces van uw applicatie bijvoorbeeld PUT of DELETE gebruiken, want REST-API's doen dat vaak.

Testen voordat u opnieuw laadt

Elke wijziging controleert u eerst met nginx -t en pas daarna laadt u Nginx opnieuw. Vervolgens test u het gedrag van buitenaf met normale verzoeken naar uw eigen domein:

sudo nginx -t && sudo systemctl reload nginx
# Verwacht: 403 of 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
# Verwacht: 404, omdat alleen de front controller wordt uitgevoerd
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/info.php
# Welke blokken geven door aan PHP-FPM?
sudo nginx -T 2>/dev/null | grep -nE "location|fastcgi_pass"

Bewaar vóór elke grotere wijziging de volledige uitvoer van nginx -T met datum. Ze bevat de gehele werkzame configuratie inclusief alle ingevoegde bestanden. Gaat er na een wijziging iets mis, dan toont de vergelijking meteen wat er verschilt. Bij de volgende kritieke kwetsbaarheid hebt u daarmee bovendien een betrouwbare basis voor de inventarisatie, zonder eerst bestanden bijeen te hoeven zoeken.

Tip

Wijzig de configuratie eerst in een testomgeving of op een kopie van het serverblok. Een vergeten ingangspunt, bijvoorbeeld een betaalcallback, valt anders pas op als bestellingen uitblijven.

Hoe helpt PHP SOS concreet?

  • Configuratie lezen: wij analyseren de volledige Nginx-configuratie met nginx -T en koppelen elke PHP-overdracht aan haar website en pool.
  • Ingangspunten vastleggen: samen met uw ontwikkelaars bepalen wij welke scripts moeten worden uitgevoerd en blokkeren de rest.
  • Doorvoeren en testen: wij passen serverblokken en pools aan, controleren met nginx -t en testen formulieren, betaalroutes en uploads.
  • Blijvend beveiligen: de configuratie wordt onderdeel van onze serverhardening en van de documentatie van uw server.

Veelgestelde vragen

Moet Nginx na een PHP-update worden herstart?

Voor de PHP-patch niet, want Nginx bevat geen PHP. Herstarten moeten alle PHP-FPM-services. Wijzigt u daarbij ook de Nginx-configuratie, controleer haar dan eerst met nginx -t en laad Nginx daarna opnieuw.

Waarom alleen index.php aan PHP doorgeven?

Veel moderne applicaties hebben één enkel ingangspunt, de front controller. Wordt alleen dit bestand aan PHP-FPM doorgegeven, dan kan een ingebracht PHP-bestand elders niet via de browser worden uitgevoerd. Bij systemen met veel ingangspunten is dit patroon niet mogelijk.

Wat levert security.limit_extensions in PHP-FPM op?

De instelling bepaalt welke bestandsextensies PHP-FPM als script uitvoert. Volgens de PHP-handleiding is de standaard .php .phar; aanbevolen wordt te beperken tot .php. Dat vangt fouten in de webserverconfiguratie op, maar vervangt haar niet.

Zijn Nginx-regels een bescherming tegen PHP2Shell?

Nee. PHP2Shell zit in de PHP-interpreter en wordt alleen gesloten door de update naar een verholpen versie en de herstart van alle workers. Een veilige Nginx-configuratie verkleint het aanvalsoppervlak en beperkt de schade na een inbraak.

Bronnen

Direct contact

Elk uur telt. Schrijf ons nu.

Beschrijf kort het systeem, de PHP-versie en wat u opmerkt. Wij nemen contact op, brengen de situatie in kaart en werken op afstand tot het incident is afgerond en gedocumenteerd.

Directe hulp