Console9

Nginx downloads PHP files instead of executing them

Fix the Nginx issue where PHP files download instead of executing by configuring the fastcgi_pass directive, PHP-FPM socket path, and server block order.

Nginx downloads PHP files to the user's device instead of executing them when the nginx.conf configuration does not pass PHP requests to the PHP-FPM process manager.

When Nginx Produces This Error

Nginx downloads PHP files instead of executing them when a visitor requests a .php URL such as example.com/index.php. Instead of seeing the rendered web page, the browser downloads the raw PHP source file. This behavior occurs because Nginx treats the PHP file as static content and serves it directly without forwarding it to PHP-FPM for processing.

Nginx also downloads PHP files when the fastcgi_pass directive exists but points to the wrong PHP-FPM socket or TCP port. The connection fails silently and Nginx falls back to serving the file as a download.

What Causes Nginx to Download PHP Files Instead of Executing Them

Nginx downloads PHP files when the fastcgi_pass directive is missing from the server block.Without a fastcgi_pass directive in the location block that matches .php files, Nginx has no instruction to forward the request to PHP-FPM. Nginx serves the file using its default handler, which triggers a download in the browser.

Nginx downloads PHP files when the PHP location block is in the wrong server block.If the location ~ \.php$ block is defined in a different server block than the one handling the domain, Nginx matches the request to the correct server block but finds no PHP handler. The request falls through to the default file handler.

Nginx downloads PHP files when the fastcgi_pass path does not match the PHP-FPM listen address.PHP-FPM can listen on a Unix socket (e.g., /var/run/php/php8.2-fpm.sock) or a TCP port (e.g., 127.0.0.1:9000). A mismatch between the nginx.conf fastcgi_pass value and the PHP-FPM listen value prevents the connection.

Nginx downloads PHP files when cgi.fix_pathinfo is set to 1 and the script path is invalid.PHP's cgi.fix_pathinfo setting controls how PHP resolves the script filename. When set to 1, PHP may attempt to execute a non-PHP file or fail to locate the correct script, causing unexpected behavior.

How to Fix Nginx Downloading PHP Files

  1. Verify that the fastcgi_pass directive exists inside the correct server block. The PHP location block must appear within the server block that handles the domain.
server {
    listen 80;
    server_name example.com;
    root /var/www/example.com;

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}
  1. Verify the PHP-FPM listen address. Open the PHP-FPM pool configuration and check the listen directive:
sudo vi /etc/php/8.2/fpm/pool.d/www.conf

Match the listen value to the fastcgi_pass value in nginx.conf. If PHP-FPM listens on a TCP port:

location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

If PHP-FPM listens on a Unix socket:

location ~ \.php$ {
    fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}
  1. Set cgi.fix_pathinfo to 0 in php.ini to prevent PHP from executing unintended scripts:
sudo vi /etc/php/8.2/fpm/php.ini
cgi.fix_pathinfo = 0
  1. Verify that the file ownership matches the Nginx and PHP-FPM user. Check the Nginx worker user:
ps aux | grep nginx

Set the PHP-FPM pool configuration to run as the same user:

user = www-data
group = www-data
listen.owner = www-data
listen.group = www-data
  1. For PHP frameworks like WordPress or Laravel, add a try_files directive that routes all requests through index.php with the query string:
location / {
    try_files $uri $uri/ /index.php?$query_string;
}
  1. Restart PHP-FPM and reload Nginx:
sudo systemctl restart php8.2-fpm
sudo nginx -t
sudo systemctl reload nginx

How to Verify the Fix

Nginx executes PHP files and returns rendered HTML instead of downloading them. Send a request to a PHP page:

curl http://example.com/index.php

The response should contain rendered HTML, not raw PHP source code. Check the Content-Type header:

curl -I http://example.com/index.php

The header should show Content-Type: text/html when PHP-FPM processes the file correctly. A Content-Type: application/octet-stream or similar value indicates Nginx is still serving the file as a download.

Edge Cases and Variations

Nginx downloads PHP files only for specific URLs.The location ~ \.php$ block may not match requests that use URL rewriting. For applications that rewrite all URLs through a front controller (e.g., WordPress, Laravel), the try_files directive must route non-matching requests to index.php.

Nginx downloads PHP files after adding a new server block.A new default_server block without a PHP location handler intercepts requests intended for another server block. Verify which server block Nginx selects by checking the server_name and listen directives.

Nginx: 502 Bad Gateway-- Nginx returns 502 when it connects to PHP-FPM but receives an invalid response. The download behavior occurs when Nginx does not attempt the connection at all.

Nginx: connect() to php-fpm.sock failed (Permission denied)-- A permission error prevents the connection to PHP-FPM and may produce a 502 error instead of a download, depending on the nginx.conf fallback configuration.