# Konfigurér Nginx sikkert til PHP

> Konfigurér Nginx sikkert til PHP: giv kun rigtige scripts videre til PHP-FPM, frigiv front-controlleren, spær dotfiles og sæt størrelsesgrænser passende.

Kilde: https://phpsos.de/da/blog/nginx-fuer-php-absichern/  
Status: 25.10.2026  
Udbyder: PHP SOS, PHP-nødhjælp og serversikkerhed. Akut hjælp på WhatsApp: https://wa.me/491736494914?text=Hej%20PHP%20SOS%2C%20vi%20har%20brug%20for%20hj%C3%A6lp%20med%20vores%20PHP-server.%20Firma%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**Kort sagt:** For at konfigurere Nginx sikkert til PHP giver I kun scripts videre til PHP-FPM, som virkelig findes, ved front-controller-applikationer endda kun `index.php`. Hertil kommer spærrede dotfiles som `.git` og `.env`, en standardserver til ukendte værtsnavne, passende størrelsesgrænser og `security.limit_extensions = .php` i poolen. Det beskytter ikke mod sårbarheder i selve PHP, men begrænser, hvad der kan udføres efter en fejl.

## De farligste linjer står i PHP-location'en

Siden PHP2Shell kontrollerer mange teams deres PHP-FPM-versioner og pools, som siden [Afsikr Nginx med PHP-FPM](https://phpsos.de/da/server/nginx-php-fpm/) beskriver, og genstarter efter opdateringen [alle workers](https://phpsos.de/da/php2shell/php-fpm-neustart/). Et andet blik gælder selve Nginx-konfigurationen. Dér afgøres det, hvilke filer der overhovedet udføres som PHP.

I mange konfigurationer står der en blok, der sender hver forespørgsel til `.php` videre til PHP-FPM, uanset hvor filen ligger. Havner en indsmuglet PHP-fil i en upload-mappe, udfører serveren den. Af en filfejl bliver der således en bagdør.

Dette indlæg koncentrerer sig om Nginx-siden: hvilke direktiver der tæller, hvordan rækkefølgen af location-blokke virker, og hvordan I tester resultatet.

## Sådan afgør Nginx, hvilken blok der gælder

Den, der vil konfigurere Nginx sikkert til PHP, må kende udvælgelsen af location-blokke. Ifølge Nginx-dokumentationen forløber den således:

1. Et nøjagtigt match med `location =` afslutter søgningen med det samme.
2. Blandt præfiks-locations huskes den længste passende. Bærer den `^~`, kontrolleres ingen regulære udtryk mere.
3. Ellers kontrolleres regulære udtryk (`~`, `~*`) i konfigurationsfilens rækkefølge; det første match vinder.
4. Passer intet regulært udtryk, gælder den huskede præfiks-location.

Heraf følger to praktiske regler. En spærring for PHP i upload-mapper skal enten oprettes som `^~`-præfiks eller stå som regulært udtryk før den generelle PHP-blok. Og en normal præfiks-location som `location /uploads/` beskytter ikke, fordi det regulære udtryk for `.php` overtrumfer den.

## En sikker grundkonfiguration

Eksemplet nedenfor viser en server-blok til en applikation med front-controller, som den er almindelig ved Laravel, Symfony, Shopware 6 eller mange egenudviklinger. Stier, socket og certifikater tilpasser I.

```nginx
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;

    # Tillad Let's Encrypt og andre kendte stier
    location ^~ /.well-known/ {
        try_files $uri =404;
    }

    # Spær skjulte filer og mapper (.git, .env, .htaccess)
    location ~ /\. {
        deny all;
    }

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    # Kun front-controlleren overdrages til 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;
    }

    # Enhver anden PHP-fil udføres ikke
    location ~ \.php$ {
        return 404;
    }
}
```

`$realpath_root` opløser symbolske links. Det er vigtigt ved deployments med skiftende symlink til den aktuelle release, så PHP-FPM og OPcache ser den rigtige sti. Bemærk desuden: Ifølge Nginx-dokumentationen nedarves `fastcgi_param`-direktiver kun fra højere niveauer, hvis der ikke står egne på det aktuelle niveau. Derfor hører `include fastcgi_params;` hjemme i den samme blok.

Klassiske CMS som WordPress, TYPO3 eller Contao har flere indgangspunkter. Dér erstatter I front-controller-blokken med en generel PHP-location, der kun overdrager eksisterende filer (`try_files $uri =404;`), og spærrer udførelsen i alle skrivbare mapper. Hvordan det lykkes, står under [Forbyd PHP-udførelse i upload-mapper](https://phpsos.de/da/blog/php-in-uploads-verbieten/).

## Den anden sikring i PHP-FPM

Nginx afgør, hvad der gives videre, PHP-FPM afgør, hvad det udfører. Ifølge PHP-manualen udfører PHP-FPM som standard filer med `.php` og `.phar` og anbefaler selv at begrænse det til `.php`. Det opfanger konfigurationsfejl i webserveren.

```ini
; i poolen, f.eks. /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
```

Socketten bør kun kunne nås af Nginx. FPM-porte i netværket, f.eks. `9000`, hører ikke hjemme åbnet udadtil. Hvordan serveren samlet kun viser de nødvendige porte, beskriver [Firewall til webservere: Åbn kun nødvendige porte](https://phpsos.de/da/blog/firewall-fuer-webserver/).

## Ukendte værtsnavne og flere justeringsskruer

Uden egen standardserver besvarer Nginx forespørgsler til ukendte værtsnavne med den første passende server-blok. Automatiserede scanninger, der kun henvender sig til IP-adressen, havner så direkte i jeres applikation. En egen standardserver afslutter sådanne forespørgsler uden svar.

```nginx
server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;
    ssl_reject_handshake on;
    return 444;
}
```

Yderligere punkter, der har vist sig at fungere:

| Justeringsskrue | Anbefaling | Formål |
|---|---|---|
| `server_tokens` | `off` | intet versionsnummer i header og fejlsider |
| `client_max_body_size` | passende til `post_max_size` | afvis store forespørgsler tidligt |
| `autoindex` | fra (standard) | ingen mappelister |
| `fastcgi_hide_header` | `X-Powered-By` | ingen PHP-version udadtil |
| Statussider | `pm.status_path` kun internt | ingen driftsdata offentligt |
| Backupfiler | spær eller fjern `.sql`, `.zip`, `.bak` i webroden | ingen download af databaser |

Mere om versionsangivelser står i indlægget [expose_php og Server-headeren](https://phpsos.de/da/blog/expose-php-versionsangaben/).

En yderligere justeringsskrue angår de tilladte HTTP-metoder. De fleste websites har kun brug for GET, HEAD og POST. Med `limit_except` kan andre metoder afvises i en location; ifølge Nginx-dokumentationen tillader frigivelsen af GET automatisk også HEAD. Kontrollér på forhånd, om grænseflader i jeres applikation bruger f.eks. PUT eller DELETE, for REST-API'er gør det ofte.

## Test, før I genindlæser

Hver ændring kontrollerer I først med `nginx -t` og genindlæser først derefter Nginx. Bagefter tester I adfærden udefra med normale forespørgsler til jeres eget domæne:

```bash
sudo nginx -t && sudo systemctl reload nginx
# Forventet: 403 eller 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
# Forventet: 404, fordi kun front-controlleren udføres
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/info.php
# Hvilke blokke overdrager til PHP-FPM?
sudo nginx -T 2>/dev/null | grep -nE "location|fastcgi_pass"
```

Sikr før hver større ændring det fuldstændige output fra `nginx -T` med dato. Det indeholder hele den virksomme konfiguration inklusive alle indbundne filer. Går noget galt efter en ændring, viser sammenligningen straks, hvad der er forskelligt. Ved næste kritiske sårbarhed har I desuden et pålideligt grundlag for beholdningsopgørelsen uden først at skulle samle filer sammen.

> **Tip:**
> Ændr først konfigurationen i et testmiljø eller på en kopi af server-blokken. Et glemt indgangspunkt, f.eks. et betalings-callback, opdages ellers først, når ordrer udebliver.

## Hvordan hjælper PHP SOS konkret?

- **Læs konfigurationen:** Vi analyserer hele Nginx-konfigurationen med `nginx -T` og henfører hver PHP-overdragelse til sit website og sin pool.
- **Fastlæg indgangspunkter:** Sammen med jeres udviklere bestemmer vi, hvilke scripts der skal udføres, og spærrer resten.
- **Gennemfør og test:** Vi tilpasser server-blokke og pools, kontrollerer med `nginx -t` og tester formularer, betalingsveje og uploads.
- **Afsikr varigt:** Konfigurationen bliver en del af vores [serverhærdning](https://phpsos.de/da/leistungen/server-haertung/) og dokumentationen af jeres server.


## Ofte stillede spørgsmål

**Skal Nginx genstartes efter en PHP-opdatering?**  
Ikke for PHP-patchen, for Nginx indeholder ikke PHP. Genstartes skal alle PHP-FPM-tjenester. Ændrer I samtidig Nginx-konfigurationen, kontrollerer I den først med nginx -t og genindlæser derefter Nginx.

**Hvorfor kun give index.php videre til PHP?**  
Mange moderne applikationer har ét enkelt indgangspunkt, front-controlleren. Overdrages kun denne fil til PHP-FPM, kan en indsmuglet PHP-fil et andet sted ikke udføres via browseren. Ved systemer med mange indgangspunkter er dette mønster ikke muligt.

**Hvad giver security.limit_extensions i PHP-FPM?**  
Indstillingen fastlægger, hvilke filendelser PHP-FPM udfører som script. Ifølge PHP-manualen er standarden .php .phar; anbefalet er at begrænse til .php. Det opfanger fejl i webserverkonfigurationen, men erstatter den ikke.

**Er Nginx-regler en beskyttelse mod PHP2Shell?**  
Nej. PHP2Shell sidder i PHP-fortolkeren og lukkes kun af opdateringen til en rettet version og genstart af alle workers. En sikker Nginx-konfiguration formindsker angrebsfladen og begrænser skaden efter et indbrud.

## Kilder

- [Nginx: ngx_http_core_module](https://nginx.org/en/docs/http/ngx_http_core_module.html)
- [Nginx: ngx_http_fastcgi_module](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html)
- [PHP-manual: FPM-konfiguration](https://www.php.net/manual/en/install.fpm.configuration.php)
- [OWASP Cheat Sheets](https://cheatsheetseries.owasp.org/)
