# Nginxin turvallinen asetus PHP:lle

> Nginx PHP turvallisesti: välittäkää PHP-FPM:lle vain oikeat skriptit, sallikaa front controller, estäkää piilotiedostot ja asettakaa kokorajat sopiviksi.

Lähde: https://phpsos.de/fi/blog/nginx-fuer-php-absichern/  
Tilanne: 25.10.2026  
Palveluntarjoaja: PHP SOS, PHP-hätäapu ja palvelimen tietoturva. Pikaapu WhatsAppilla: https://wa.me/491736494914?text=Hei%20PHP%20SOS%2C%20tarvitsemme%20apua%20PHP-palvelimemme%20kanssa.%20Yritys%3A%20%E2%80%A6%20Verkkosivusto%3A%20%E2%80%A6

**Lyhyesti:** Asettaaksenne Nginxin PHP:lle turvallisesti välitätte PHP-FPM:lle vain skriptit, jotka todella ovat olemassa, front controller -sovelluksissa jopa vain tiedoston `index.php`. Lisäksi tarvitaan estetyt piilotiedostot kuten `.git` ja `.env`, oletuspalvelin tuntemattomille isäntänimille, sopivat kokorajat ja `security.limit_extensions = .php` poolissa. Se ei suojaa PHP:n omilta aukoilta, mutta rajaa, mitä virheen jälkeen voidaan suorittaa.

## Vaarallisimmat rivit ovat PHP-locationissa

PHP2Shellin jälkeen monet tiimit tarkistavat PHP-FPM-versionsa ja poolinsa, kuten sivu [Nginxin ja PHP-FPM:n suojaus](https://phpsos.de/fi/server/nginx-php-fpm/) kuvaa, ja käynnistävät päivityksen jälkeen [kaikki workerit uudelleen](https://phpsos.de/fi/php2shell/php-fpm-neustart/). Toinen katse kohdistuu Nginxin asetuksiin itseensä. Siellä ratkaistaan, mitkä tiedostot ylipäätään suoritetaan PHP:nä.

Monessa asetuksessa on lohko, joka välittää jokaisen `.php`-pyynnön PHP-FPM:lle riippumatta siitä, missä tiedosto sijaitsee. Jos sisäänajettu PHP-tiedosto päätyy upload-kansioon, palvelin suorittaa sen. Tiedostovirheestä tulee näin takaportti.

Tämä kirjoitus keskittyy Nginxin puoleen: mitkä direktiivit ratkaisevat, miten location-lohkojen järjestys vaikuttaa ja miten testaatte tuloksen.

## Miten Nginx päättää, mikä lohko pätee

Joka haluaa asettaa Nginxin PHP:lle turvallisesti, tarvitsee tuntea location-lohkojen valinta. Nginxin dokumentaation mukaan se etenee näin:

1. Tarkka osuma `location =` -lohkoon lopettaa haun heti.
2. Etuliite-locationeista muistetaan pisin sopiva. Jos siinä on `^~`, säännöllisiä lausekkeita ei enää tarkisteta.
3. Muuten säännölliset lausekkeet (`~`, `~*`) tarkistetaan asetustiedoston järjestyksessä; ensimmäinen osuma voittaa.
4. Jos mikään säännöllinen lauseke ei osu, voimaan tulee muistettu etuliite-location.

Tästä seuraa kaksi käytännön sääntöä. PHP:n esto upload-kansioissa on joko luotava `^~`-etuliitteenä tai sen on oltava säännöllisenä lausekkeena ennen yleistä PHP-lohkoa. Ja tavallinen etuliite-location kuten `location /uploads/` ei suojaa, koska `.php`-päätteen säännöllinen lauseke ohittaa sen.

## Turvallinen perusasetus

Seuraava esimerkki näyttää palvelinlohkon sovellukselle, jossa on front controller, kuten Laravelissa, Symfonyssa, Shopware 6:ssa tai monissa omissa kehityksissä on tavallista. Polut, socketin ja varmenteet mukautatte omiin tarpeisiinne.

```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;

    # Salli Let's Encrypt ja muut tunnetut polut
    location ^~ /.well-known/ {
        try_files $uri =404;
    }

    # Estä piilotetut tiedostot ja kansiot (.git, .env, .htaccess)
    location ~ /\. {
        deny all;
    }

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    # Vain front controller välitetään PHP-FPM:lle
    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;
    }

    # Mitään muuta PHP-tiedostoa ei suoriteta
    location ~ \.php$ {
        return 404;
    }
}
```

`$realpath_root` ratkaisee symboliset linkit. Se on tärkeää käyttöönotoissa, joissa symlink vaihtuu nykyiseen julkaisuun, jotta PHP-FPM ja OPcache näkevät oikean polun. Huomatkaa lisäksi: Nginxin dokumentaation mukaan `fastcgi_param`-direktiivit periytyvät ylemmiltä tasoilta vain, jos nykyisellä tasolla ei ole omia. Siksi `include fastcgi_params;` kuuluu samaan lohkoon.

Klassisilla CMS:illä kuten WordPress, TYPO3 tai Contao on useita sisääntulopisteitä. Niissä korvaatte front controller -lohkon yleisellä PHP-locationilla, joka välittää vain olemassa olevat tiedostot (`try_files $uri =404;`), ja estätte suorituksen kaikissa kirjoitettavissa kansioissa. Miten se onnistuu, kerrotaan kohdassa [PHP:n suorituksen kielto upload-kansioissa](https://phpsos.de/fi/blog/php-in-uploads-verbieten/).

## Toinen varmistus PHP-FPM:ssä

Nginx päättää, mitä välitetään, PHP-FPM päättää, mitä se suorittaa. PHP-käsikirjan mukaan PHP-FPM suorittaa oletuksena tiedostot päätteillä `.php` ja `.phar` ja suosittelee itse rajaamaan sen arvoon `.php`. Se sieppaa verkkopalvelimen asetusvirheet.

```ini
; poolissa, esim. /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
```

Socketin pitäisi olla saavutettavissa vain Nginxille. FPM-portteja verkossa, kuten `9000`, ei pidä avata ulospäin. Miten palvelin yleisesti näyttää vain tarvittavat portit, kuvaa [Palomuuri verkkopalvelimelle: avatkaa vain tarvittavat portit](https://phpsos.de/fi/blog/firewall-fuer-webserver/).

## Tuntemattomat isäntänimet ja muut säätöruuvit

Ilman omaa oletuspalvelinta Nginx vastaa tuntemattomien isäntänimien pyyntöihin ensimmäisellä sopivalla palvelinlohkolla. Automatisoidut skannaukset, jotka puhuttelevat vain IP-osoitetta, päätyvät silloin suoraan sovellukseenne. Oma oletuspalvelin päättää tällaiset pyynnöt ilman vastausta.

```nginx
server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;
    ssl_reject_handshake on;
    return 444;
}
```

Muita hyviksi havaittuja kohtia:

| Säätöruuvi | Suositus | Tarkoitus |
|---|---|---|
| `server_tokens` | `off` | ei versionumeroa otsakkeessa ja virhesivuilla |
| `client_max_body_size` | sopiva arvoon `post_max_size` | hylkää suuret pyynnöt aikaisin |
| `autoindex` | pois (oletus) | ei hakemistolistauksia |
| `fastcgi_hide_header` | `X-Powered-By` | ei PHP-versiota ulospäin |
| Tilasivut | `pm.status_path` vain sisäisesti | ei käyttötietoja julkisesti |
| Varmuuskopiotiedostot | estäkää tai poistakaa `.sql`, `.zip`, `.bak` verkkojuuresta | ei tietokantojen latausta |

Lisää versiotiedoista kirjoituksessa [expose_php ja Server-otsakkeet](https://phpsos.de/fi/blog/expose-php-versionsangaben/).

Yksi säätöruuvi koskee sallittuja HTTP-metodeja. Useimmat verkkosivustot tarvitsevat vain GET-, HEAD- ja POST-metodit. Direktiivillä `limit_except` muut metodit voidaan hylätä locationissa; Nginxin dokumentaation mukaan GET-metodin salliminen sallii automaattisesti myös HEAD-metodin. Tarkistakaa etukäteen, käyttävätkö sovelluksenne rajapinnat esimerkiksi PUT- tai DELETE-metodia, sillä REST-rajapinnat tekevät niin usein.

## Testatkaa ennen uudelleenlatausta

Tarkistatte jokaisen muutoksen ensin komennolla `nginx -t` ja ladatte Nginxin uudelleen vasta sen jälkeen. Sen jälkeen testaatte toiminnan ulkopuolelta tavallisilla pyynnöillä omaan verkkotunnukseenne:

```bash
sudo nginx -t && sudo systemctl reload nginx
# Odotettu: 403 tai 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
# Odotettu: 404, koska vain front controller suoritetaan
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/info.php
# Mitkä lohkot välittävät PHP-FPM:lle?
sudo nginx -T 2>/dev/null | grep -nE "location|fastcgi_pass"
```

Varmistakaa ennen jokaista suurempaa muutosta komennon `nginx -T` täydellinen tuloste päivämäärällä. Se sisältää koko voimassa olevan asetuksen kaikkine liitettyine tiedostoineen. Jos muutoksen jälkeen jokin menee pieleen, vertailu näyttää heti, mikä eroaa. Seuraavan kriittisen aukon kohdalla teillä on lisäksi luotettava perusta inventaariolle ilman tiedostojen keräilyä.

> **Vinkki:**
> Muuttakaa asetuksia ensin testiympäristössä tai palvelinlohkon kopiossa. Unohtunut sisääntulopiste, esimerkiksi maksun callback, huomataan muuten vasta, kun tilaukset jäävät tulematta.

## Miten PHP SOS auttaa konkreettisesti?

- **Asetusten lukeminen:** Käymme läpi koko Nginx-asetuksen komennolla `nginx -T` ja yhdistämme jokaisen PHP-välityksen sen verkkosivustoon ja pooliin.
- **Sisääntulopisteiden määrittäminen:** Määritämme yhdessä kehittäjienne kanssa, mitkä skriptit on suoritettava, ja estämme loput.
- **Toteutus ja testaus:** Mukautamme palvelinlohkot ja poolit, tarkistamme komennolla `nginx -t` ja testaamme lomakkeet, maksureitit ja lataukset.
- **Pysyvä suojaus:** Asetuksesta tulee osa [palvelimen koventamistamme](https://phpsos.de/fi/leistungen/server-haertung/) ja palvelimenne dokumentaatiota.


## Usein kysytyt kysymykset

**Pitääkö Nginx käynnistää uudelleen PHP-päivityksen jälkeen?**  
PHP-korjausta varten ei, sillä Nginx ei sisällä PHP:tä. Uudelleen on käynnistettävä kaikki PHP-FPM-palvelut. Jos muutatte samalla Nginxin asetuksia, tarkistakaa ne ensin komennolla nginx -t ja ladatkaa Nginx sen jälkeen uudelleen.

**Miksi vain index.php välitetään PHP:lle?**  
Monilla nykyaikaisilla sovelluksilla on yksi ainoa sisääntulopiste, front controller. Jos vain tämä tiedosto välitetään PHP-FPM:lle, muualle ujutettua PHP-tiedostoa ei voi suorittaa selaimen kautta. Järjestelmissä, joissa on monta sisääntulopistettä, tämä malli ei ole mahdollinen.

**Mitä security.limit_extensions tuo PHP-FPM:ssä?**  
Asetus määrää, mitkä tiedostopäätteet PHP-FPM suorittaa skripteinä. PHP-käsikirjan mukaan oletus on .php .phar; suositus on rajata arvoon .php. Se sieppaa verkkopalvelimen asetusvirheet mutta ei korvaa niitä.

**Ovatko Nginx-säännöt suoja PHP2Shelliä vastaan?**  
Eivät. PHP2Shell on PHP-tulkissa, ja se suljetaan vain päivityksellä korjattuun versioon ja kaikkien workerien uudelleenkäynnistyksellä. Turvallinen Nginx-asetus pienentää hyökkäyspintaa ja rajaa vahingon murron jälkeen.

## Lähteet

- [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-käsikirja: FPM-asetukset](https://www.php.net/manual/en/install.fpm.configuration.php)
- [OWASP Cheat Sheets](https://cheatsheetseries.owasp.org/)
