Stop Nginx from serving PHP source as a download
Configure the PHP-FPM handler, prevent unintended script execution, and distinguish source exposure from an application-generated download.
If the downloaded response contains PHP source, Nginx is probably serving the script as a static file instead of handing it to PHP-FPM. Treat confirmed source exposure as an incident: restrict the affected endpoint, correct routing, and assess whether secrets or private code were disclosed.
A download prompt alone is not conclusive. An executed PHP application can intentionally return a download through its response headers, so inspect a harmless test response before assuming the source was exposed.
Route PHP requests to FPM
For a conventional document root, add this inside the intended server:
index index.php index.html;
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
}
The socket path is an example and must match the active FPM pool. Inspect your distribution's fastcgi_params or fastcgi.conf; do not add duplicate SCRIPT_FILENAME definitions blindly. This simple path mapping assumes root and no PATH_INFO requirement; an alias-based deployment needs a corresponding mapping.
sudo nginx -t && sudo systemctl reload nginx
Check the service and selected location
On a Debian-family installation, installing php-fpm supplies the packaged FPM service. Then inspect the actual installed version:
systemctl list-unit-files '*fpm*'
grep -E '^listen[[:space:]]*=' /etc/php/*/fpm/pool.d/www.conf
Confirm the chosen service is running and its socket is accessible to the Nginx worker. Check whether an exact location, a ^~ prefix, or an earlier regex bypasses the PHP handler. A successful configuration test does not prove a request selects it.
Limit script execution when the application permits it
For an application that executes only /index.php, use an exact allowlist rather than a generic PHP handler:
location = /index.php {
try_files $uri =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
location ~* \.php(?:/|$) {
return 404;
}
This is an alternative to the generic handler, not an additional block to paste beside it. Check other locations for overrides and use the application's intended front-controller routing separately. Keep uploads outside the executable document tree where possible.
If a multi-script application needs a generic handler, a PHP-denial regex for uploads must appear before that handler among competing regex locations. A denial placed after a matching generic PHP regex will not be reached. try_files $uri =404 only checks existence; it does not restrict execution to approved entry points.
Respond to confirmed source exposure
Review access logs and any cached copies, identify secrets present in exposed files, and rotate the affected credentials. Verify that responses now contain executed output rather than source. Fixing the route alone cannot establish that nobody downloaded the earlier response.
FAQ
Why did the result change to 502?
Nginx may now select FastCGI but fail to connect to FPM. Check the exact error, socket path, listener, and permissions. A changed status is progress in diagnosis, not proof the setup is finished.
Is Apache different?
Apache can execute PHP through an installed handler such as mod_php or proxy it to PHP-FPM. It also needs a correctly configured handler; the server name alone does not guarantee execution.
Can FPM use TCP?
Yes, for example a restricted local listener at 127.0.0.1:9000. Separate containers can communicate over a private network or a deliberately shared Unix-socket volume. Protect FPM from untrusted clients in either case.
References: Nginx PHP request processing, PHP-FPM configuration.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan