# Bezpieczna konfiguracja Nginx dla PHP

> Bezpieczna konfiguracja Nginx dla PHP: do PHP-FPM tylko prawdziwe skrypty, odblokować front controller, zablokować pliki z kropką, ustawić limity.

Źródło: https://phpsos.de/pl/blog/nginx-fuer-php-absichern/  
Stan: 25.10.2026  
Dostawca: PHP SOS, Pomoc awaryjna PHP i bezpieczeństwo serwerów. Natychmiastowa pomoc przez WhatsApp: https://wa.me/491736494914?text=Dzie%C5%84%20dobry%2C%20PHP%20SOS%2C%20potrzebujemy%20pomocy%20z%20naszym%20serwerem%20PHP.%20Firma%3A%20%E2%80%A6%20Strona%20internetowa%3A%20%E2%80%A6

**Krótko:** Aby bezpiecznie skonfigurować Nginx dla PHP, przekazują Państwo do PHP-FPM tylko skrypty, które naprawdę istnieją, a w aplikacjach z front controllerem wyłącznie `index.php`. Do tego dochodzą zablokowane pliki z kropką, takie jak `.git` i `.env`, serwer domyślny dla nieznanych nazw hostów, odpowiednie limity rozmiaru i `security.limit_extensions = .php` w puli. Nie chroni to przed lukami w samym PHP, ale ogranicza to, co po błędzie można wykonać.

## Najniebezpieczniejsze wiersze stoją w lokalizacji PHP

Od PHP2Shell wiele zespołów sprawdza swoje wersje PHP-FPM i pule, jak opisuje strona [Zabezpieczanie Nginx z PHP-FPM](https://phpsos.de/pl/server/nginx-php-fpm/), i po aktualizacji [restartuje wszystkie procesy robocze](https://phpsos.de/pl/php2shell/php-fpm-neustart/). Drugie spojrzenie dotyczy samej konfiguracji Nginx. To tam rozstrzyga się, jakie pliki w ogóle są wykonywane jako PHP.

W wielu konfiguracjach stoi blok, który każde żądanie do `.php` przekazuje do PHP-FPM, bez względu na to, gdzie plik leży. Jeśli wstrzyknięty plik PHP trafi do katalogu uploadu, serwer go wykona. Z błędu dotyczącego pliku powstaje w ten sposób tylna furtka.

Ten artykuł koncentruje się na stronie Nginx: które dyrektywy się liczą, jak działa kolejność bloków location i jak przetestować wynik.

## Jak Nginx decyduje, który blok zadziała

Kto chce bezpiecznie skonfigurować Nginx dla PHP, musi znać wybór bloków location. Według dokumentacji Nginx przebiega on tak:

1. Dokładne trafienie z `location =` natychmiast kończy wyszukiwanie.
2. Spośród lokalizacji z prefiksem zapamiętywana jest najdłuższa pasująca. Jeśli ma `^~`, wyrażenia regularne nie są już sprawdzane.
3. W przeciwnym razie wyrażenia regularne (`~`, `~*`) są sprawdzane w kolejności z pliku konfiguracyjnego; wygrywa pierwsze trafienie.
4. Jeśli żadne wyrażenie regularne nie pasuje, obowiązuje zapamiętana lokalizacja z prefiksem.

Wynikają z tego dwie praktyczne zasady. Blokada PHP w katalogach uploadu musi być założona albo jako prefiks `^~`, albo jako wyrażenie regularne przed ogólnym blokiem PHP. A zwykła lokalizacja z prefiksem, taka jak `location /uploads/`, nie chroni, bo wyrażenie regularne dla `.php` ją przebija.

## Bezpieczna konfiguracja podstawowa

Poniższy przykład pokazuje blok serwera dla aplikacji z front controllerem, jak w Laravel, Symfony, Shopware 6 lub wielu własnych rozwiązaniach. Ścieżki, gniazdo i certyfikaty należy dopasować.

```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;

    # Zezwolić na Let's Encrypt i inne znane ścieżki
    location ^~ /.well-known/ {
        try_files $uri =404;
    }

    # Zablokować ukryte pliki i katalogi (.git, .env, .htaccess)
    location ~ /\. {
        deny all;
    }

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    # Do PHP-FPM przekazywany jest tylko front controller
    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;
    }

    # Żaden inny plik PHP nie jest wykonywany
    location ~ \.php$ {
        return 404;
    }
}
```

`$realpath_root` rozwiązuje dowiązania symboliczne. Jest to ważne przy wdrożeniach ze zmieniającym się dowiązaniem do bieżącego wydania, aby PHP-FPM i OPcache widziały właściwą ścieżkę. Proszę ponadto pamiętać: według dokumentacji Nginx dyrektywy `fastcgi_param` są dziedziczone z wyższych poziomów tylko wtedy, gdy na bieżącym poziomie nie ma własnych. Dlatego `include fastcgi_params;` należy do tego samego bloku.

Klasyczne systemy CMS, takie jak WordPress, TYPO3 czy Contao, mają kilka punktów wejścia. W ich przypadku blok front controllera zastępuje się ogólną lokalizacją PHP, która przekazuje tylko istniejące pliki (`try_files $uri =404;`), i blokuje wykonywanie we wszystkich zapisywalnych katalogach. Jak to zrobić, opisuje artykuł [Zakazywanie wykonywania PHP w katalogach uploadu](https://phpsos.de/pl/blog/php-in-uploads-verbieten/).

## Drugie zabezpieczenie w PHP-FPM

Nginx decyduje, co zostanie przekazane, PHP-FPM decyduje, co wykona. Według podręcznika PHP PHP-FPM domyślnie wykonuje pliki z `.php` i `.phar` i sam zaleca ograniczenie do `.php`. Przechwytuje to błędy konfiguracji w serwerze WWW.

```ini
; w puli, np. /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
```

Gniazdo powinno być dostępne tylko dla Nginx. Portów FPM w sieci, na przykład `9000`, nie wolno otwierać na zewnątrz. Jak cały serwer pokazuje tylko niezbędne porty, opisuje artykuł [Zapora dla serwera WWW: otwierać tylko niezbędne porty](https://phpsos.de/pl/blog/firewall-fuer-webserver/).

## Nieznane nazwy hostów i dalsze pokrętła

Bez własnego serwera domyślnego Nginx odpowiada na żądania do nieznanych nazw hostów pierwszym pasującym blokiem serwera. Zautomatyzowane skany, które zwracają się tylko do adresu IP, trafiają wtedy prosto do Państwa aplikacji. Własny serwer domyślny kończy takie żądania bez odpowiedzi.

```nginx
server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;
    ssl_reject_handshake on;
    return 444;
}
```

Dalsze sprawdzone punkty:

| Pokrętło | Zalecenie | Cel |
|---|---|---|
| `server_tokens` | `off` | brak numeru wersji w nagłówku i na stronach błędów |
| `client_max_body_size` | dopasowane do `post_max_size` | wcześnie odrzucać duże żądania |
| `autoindex` | wyłączone (domyślnie) | brak list katalogów |
| `fastcgi_hide_header` | `X-Powered-By` | brak wersji PHP na zewnątrz |
| Strony statusu | `pm.status_path` tylko wewnętrznie | brak danych eksploatacyjnych publicznie |
| Pliki kopii zapasowych | zablokować lub usunąć `.sql`, `.zip`, `.bak` w katalogu głównym | brak pobierania baz danych |

Więcej o informacjach o wersji znajduje się w artykule [expose_php i nagłówki Server](https://phpsos.de/pl/blog/expose-php-versionsangaben/).

Kolejne pokrętło dotyczy dozwolonych metod HTTP. Większość stron potrzebuje tylko GET, HEAD i POST. Za pomocą `limit_except` można w lokalizacji odrzucać inne metody; według dokumentacji Nginx zezwolenie na GET automatycznie zezwala też na HEAD. Proszę wcześniej sprawdzić, czy interfejsy Państwa aplikacji używają na przykład PUT lub DELETE, bo interfejsy REST często to robią.

## Testowanie przed przeładowaniem

Każdą zmianę proszę najpierw sprawdzić poleceniem `nginx -t` i dopiero potem przeładować Nginx. Następnie testują Państwo zachowanie z zewnątrz zwykłymi żądaniami do własnej domeny:

```bash
sudo nginx -t && sudo systemctl reload nginx
# Oczekiwane: 403 lub 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
# Oczekiwane: 404, ponieważ wykonywany jest tylko front controller
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/info.php
# Które bloki przekazują do PHP-FPM?
sudo nginx -T 2>/dev/null | grep -nE "location|fastcgi_pass"
```

Przed każdą większą zmianą proszę zabezpieczyć pełne wyjście `nginx -T` z datą. Zawiera ono całą obowiązującą konfigurację łącznie ze wszystkimi dołączonymi plikami. Jeśli po zmianie coś pójdzie źle, porównanie od razu pokazuje, co się różni. Przy następnej krytycznej luce mają Państwo ponadto wiarygodną podstawę do inwentaryzacji, bez konieczności zbierania plików.

> **Wskazówka:**
> Konfigurację proszę zmieniać najpierw w środowisku testowym lub na kopii bloku serwera. Zapomniany punkt wejścia, na przykład callback płatności, wychodzi inaczej na jaw dopiero wtedy, gdy brakuje zamówień.

## Jak konkretnie pomaga PHP SOS?

- **Odczyt konfiguracji:** analizujemy całą konfigurację Nginx za pomocą `nginx -T` i przyporządkowujemy każde przekazanie do PHP jego stronie i puli.
- **Ustalenie punktów wejścia:** wspólnie z Państwa programistami określamy, które skrypty muszą być wykonywane, i blokujemy resztę.
- **Wdrożenie i test:** dostosowujemy bloki serwera i pule, sprawdzamy za pomocą `nginx -t` oraz testujemy formularze, drogi płatności i uploady.
- **Trwałe zabezpieczenie:** konfiguracja staje się częścią naszego [utwardzania serwerów](https://phpsos.de/pl/leistungen/server-haertung/) i dokumentacji Państwa serwera.


## Częste pytania

**Czy Nginx trzeba zrestartować po aktualizacji PHP?**  
Dla poprawki PHP nie, ponieważ Nginx nie zawiera PHP. Restartu wymagają wszystkie usługi PHP-FPM. Jeśli przy tym zmieniają Państwo także konfigurację Nginx, proszę najpierw sprawdzić ją poleceniem nginx -t, a potem przeładować Nginx.

**Dlaczego przekazywać do PHP tylko index.php?**  
Wiele nowoczesnych aplikacji ma jeden punkt wejścia, front controller. Jeśli do PHP-FPM przekazywany jest tylko ten plik, wstrzyknięty plik PHP w innym miejscu nie może zostać wykonany przez przeglądarkę. W systemach z wieloma punktami wejścia taki wzorzec nie jest możliwy.

**Co daje security.limit_extensions w PHP-FPM?**  
Ustawienie określa, jakie rozszerzenia plików PHP-FPM wykonuje jako skrypt. Według podręcznika PHP domyślnie jest to .php .phar; zaleca się ograniczenie do .php. Przechwytuje to błędy w konfiguracji serwera WWW, ale jej nie zastępuje.

**Czy reguły Nginx chronią przed PHP2Shell?**  
Nie. PHP2Shell tkwi w interpreterze PHP i zamyka się go wyłącznie aktualizacją do naprawionej wersji oraz restartem wszystkich procesów roboczych. Bezpieczna konfiguracja Nginx zmniejsza powierzchnię ataku i ogranicza szkody po włamaniu.

## Źródła

- [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)
- [Podręcznik PHP: konfiguracja FPM](https://www.php.net/manual/en/install.fpm.configuration.php)
- [OWASP Cheat Sheets](https://cheatsheetseries.owasp.org/)
