# Configurare Nginx per PHP in sicurezza

> Configurare Nginx per PHP in sicurezza: passare a PHP-FPM solo script reali, abilitare il front controller, bloccare i dotfile e impostare limiti di dimensione adatti.

Fonte: https://phpsos.de/it/blog/nginx-fuer-php-absichern/  
Stato: 25/10/2026  
Fornitore: PHP SOS, Assistenza di emergenza PHP e sicurezza dei server. Assistenza immediata su WhatsApp: https://wa.me/491736494914?text=Buongiorno%20PHP%20SOS%2C%20abbiamo%20bisogno%20di%20aiuto%20con%20il%20nostro%20server%20PHP.%20Azienda%3A%20%E2%80%A6%20Sito%20web%3A%20%E2%80%A6

**In breve:** per configurare Nginx per PHP in sicurezza, passi a PHP-FPM solo script che esistono davvero, nelle applicazioni con front controller addirittura solo `index.php`. A ciò si aggiungono dotfile bloccati come `.git` e `.env`, un server predefinito per hostname sconosciuti, limiti di dimensione adatti e `security.limit_extensions = .php` nel pool. Non protegge dalle vulnerabilità in PHP stesso, ma limita ciò che è eseguibile dopo un errore.

## Le righe più pericolose stanno nella location PHP

Da PHP2Shell molti team verificano le proprie versioni e i pool di PHP-FPM, come descrive la pagina [Mettere in sicurezza Nginx con PHP-FPM](https://phpsos.de/it/server/nginx-php-fpm/), e dopo l'aggiornamento [riavviano tutti i worker](https://phpsos.de/it/php2shell/php-fpm-neustart/). Un secondo sguardo va alla configurazione di Nginx stessa. Lì si decide quali file vengano eseguiti come PHP.

In molte configurazioni c'è un blocco che inoltra a PHP-FPM ogni richiesta a `.php`, ovunque si trovi il file. Se un file PHP iniettato finisce in una cartella di upload, il server lo esegue. Da un errore di file nasce così una backdoor.

Questo articolo si concentra sul lato Nginx: quali direttive contano, come agisce l'ordine dei blocchi location e come testare il risultato.

## Come Nginx decide quale blocco si applica

Chi vuole configurare Nginx per PHP in sicurezza deve conoscere la scelta dei blocchi location. Secondo la documentazione di Nginx, si svolge così:

1. Una corrispondenza esatta con `location =` termina subito la ricerca.
2. Tra le location con prefisso viene memorizzata la più lunga corrispondente. Se porta `^~`, non vengono più controllate espressioni regolari.
3. Altrimenti le espressioni regolari (`~`, `~*`) vengono controllate nell'ordine del file di configurazione; vince la prima corrispondenza.
4. Se nessuna espressione regolare corrisponde, vale la location con prefisso memorizzata.

Ne derivano due regole pratiche. Un blocco per PHP nelle cartelle di upload deve essere creato come prefisso `^~` oppure stare, come espressione regolare, prima del blocco PHP generale. E una normale location con prefisso come `location /uploads/` non protegge, perché l'espressione regolare per `.php` la scavalca.

## Una configurazione di base sicura

L'esempio seguente mostra un blocco server per un'applicazione con front controller, come è usuale in Laravel, Symfony, Shopware 6 o in molti sviluppi propri. Percorsi, socket e certificati li adatti.

```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;

    # Consentire Let's Encrypt e altri percorsi noti
    location ^~ /.well-known/ {
        try_files $uri =404;
    }

    # Bloccare file e cartelle nascosti (.git, .env, .htaccess)
    location ~ /\. {
        deny all;
    }

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    # Solo il front controller viene passato a 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;
    }

    # Ogni altro file PHP non viene eseguito
    location ~ \.php$ {
        return 404;
    }
}
```

`$realpath_root` risolve i link simbolici. È importante nei deployment con un symlink variabile verso la release corrente, affinché PHP-FPM e OPcache vedano il percorso giusto. Tenga presente inoltre: secondo la documentazione di Nginx, le direttive `fastcgi_param` vengono ereditate dai livelli superiori solo se al livello corrente non ce ne sono di proprie. Per questo `include fastcgi_params;` va nello stesso blocco.

I CMS classici come WordPress, TYPO3 o Contao hanno più punti di ingresso. Lì sostituisca il blocco del front controller con una location PHP generale che passa solo file esistenti (`try_files $uri =404;`) e blocchi l'esecuzione in tutte le cartelle scrivibili. Come ottenerlo lo spiega [Vietare l'esecuzione di PHP nelle cartelle di upload](https://phpsos.de/it/blog/php-in-uploads-verbieten/).

## La seconda protezione in PHP-FPM

Nginx decide che cosa viene inoltrato, PHP-FPM decide che cosa esegue. Secondo il manuale PHP, PHP-FPM per impostazione predefinita esegue file con `.php` e `.phar` e raccomanda esso stesso di limitarsi a `.php`. Ciò intercetta errori di configurazione nel server web.

```ini
; nel pool, ad es. /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
```

Il socket dovrebbe essere raggiungibile solo da Nginx. Le porte FPM nella rete, ad esempio `9000`, non vanno aperte verso l'esterno. Come il server mostri in generale solo le porte necessarie lo descrive [Firewall per server web: aprire solo le porte necessarie](https://phpsos.de/it/blog/firewall-fuer-webserver/).

## Hostname sconosciuti e altre regolazioni

Senza un server predefinito proprio, Nginx risponde alle richieste a hostname sconosciuti con il primo blocco server corrispondente. Le scansioni automatiche che si rivolgono solo all'indirizzo IP finiscono allora direttamente nella Sua applicazione. Un server predefinito proprio chiude tali richieste senza risposta.

```nginx
server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;
    ssl_reject_handshake on;
    return 444;
}
```

Altri punti che si sono dimostrati validi:

| Regolazione | Raccomandazione | Scopo |
|---|---|---|
| `server_tokens` | `off` | nessun numero di versione in header e pagine di errore |
| `client_max_body_size` | adatto a `post_max_size` | rifiutare presto le richieste grandi |
| `autoindex` | disattivato (predefinito) | nessun elenco di directory |
| `fastcgi_hide_header` | `X-Powered-By` | nessuna versione di PHP verso l'esterno |
| Pagine di stato | `pm.status_path` solo interno | nessun dato operativo pubblico |
| File di backup | bloccare o rimuovere `.sql`, `.zip`, `.bak` nel webroot | nessun download di database |

Maggiori informazioni sulle indicazioni di versione sono nell'articolo [expose_php e header Server](https://phpsos.de/it/blog/expose-php-versionsangaben/).

Un'altra regolazione riguarda i metodi HTTP consentiti. La maggior parte dei siti ha bisogno solo di GET, HEAD e POST. Con `limit_except` si possono rifiutare altri metodi in una location; secondo la documentazione di Nginx, l'abilitazione di GET consente automaticamente anche HEAD. Verifichi prima se le interfacce della Sua applicazione usano ad esempio PUT o DELETE, perché le API REST lo fanno spesso.

## Testare prima di ricaricare

Ogni modifica la verifichi prima con `nginx -t` e ricarichi Nginx solo dopo. Poi testi il comportamento dall'esterno con normali richieste al Suo dominio:

```bash
sudo nginx -t && sudo systemctl reload nginx
# Atteso: 403 o 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
# Atteso: 404, perché viene eseguito solo il front controller
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/info.php
# Quali blocchi passano a PHP-FPM?
sudo nginx -T 2>/dev/null | grep -nE "location|fastcgi_pass"
```

Prima di ogni modifica più grande metta al sicuro con la data l'intero output di `nginx -T`. Contiene l'intera configurazione effettiva, compresi tutti i file inclusi. Se dopo una modifica qualcosa va storto, il confronto mostra subito che cosa differisce. Alla prossima vulnerabilità critica avrà inoltre una base affidabile per l'inventario, senza dover prima raccogliere i file.

> **Consiglio:**
> Modifichi la configurazione prima su un ambiente di test o su una copia del blocco server. Un punto di ingresso dimenticato, ad esempio un callback di pagamento, altrimenti si nota solo quando mancano gli ordini.

## Come aiuta PHP SOS in concreto?

- **Leggere la configurazione:** analizziamo l'intera configurazione di Nginx con `nginx -T` e assegniamo ogni passaggio a PHP al relativo sito e pool.
- **Definire i punti di ingresso:** insieme ai Suoi sviluppatori stabiliamo quali script devono essere eseguiti e blocchiamo il resto.
- **Attuare e testare:** adattiamo blocchi server e pool, verifichiamo con `nginx -t` e testiamo moduli, percorsi di pagamento e upload.
- **Proteggere nel tempo:** la configurazione diventa parte del nostro [hardening del server](https://phpsos.de/it/leistungen/server-haertung/) e della documentazione del Suo server.


## Domande frequenti

**Nginx deve essere riavviato dopo un aggiornamento di PHP?**  
Non per la patch di PHP, perché Nginx non contiene PHP. Devono essere riavviati tutti i servizi PHP-FPM. Se modifica anche la configurazione di Nginx, la verifichi prima con nginx -t e poi ricarichi Nginx.

**Perché passare a PHP solo index.php?**  
Molte applicazioni moderne hanno un unico punto di ingresso, il front controller. Se solo questo file viene passato a PHP-FPM, un file PHP iniettato altrove non può essere eseguito tramite il browser. Nei sistemi con molti punti di ingresso questo schema non è possibile.

**Che cosa serve security.limit_extensions in PHP-FPM?**  
L'impostazione stabilisce quali estensioni di file PHP-FPM esegue come script. Secondo il manuale PHP il valore predefinito è .php .phar; si raccomanda di limitarsi a .php. Intercetta errori nella configurazione del server web, ma non la sostituisce.

**Le regole di Nginx proteggono da PHP2Shell?**  
No. PHP2Shell si trova nell'interprete PHP e si chiude solo con l'aggiornamento a una versione corretta e il riavvio di tutti i worker. Una configurazione sicura di Nginx riduce la superficie d'attacco e limita il danno dopo un'intrusione.

## Fonti

- [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)
- [Manuale PHP: configurazione di FPM](https://www.php.net/manual/en/install.fpm.configuration.php)
- [OWASP Cheat Sheet](https://cheatsheetseries.owasp.org/)
