Deploy a PHP Website with Nginx on Ubuntu
A working Nginx server block for PHP-FPM, explained line by line — including the two mistakes that cause a raw PHP file to download instead of run.
By DevStudio Online Team · Published September 3, 2026
Unlike Apache, Nginx has no built-in PHP support — it hands .php requests off to PHP-FPM over a socket. Missing that handoff is the single most common reason a PHP site "downloads the file instead of running it."
Install PHP-FPM
sudo apt install php8.3-fpm
sudo systemctl enable --now php8.3-fpmThe server block
server {
listen 80;
server_name example.com;
root /var/www/example.com/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}Line by line, the parts that actually matter:
try_files $uri $uri/ /index.php?$query_string;— this is what makes clean URLs work for frameworks with a front controller (Laravel, WordPress, etc.). Without it, only/index.phpitself would load; every other route would 404.fastcgi_pass unix:/run/php/php8.3-fpm.sock;— this is the actual handoff to PHP-FPM. Get the socket path wrong (or leave it pointing at a PHP version that isn't installed) and Nginx will serve the raw PHP source as a downloadable file instead of executing it, since Nginx itself doesn't know what to do with.phpcontent on its own.location ~ /\.ht— blocks any.htaccess/.htpasswdfile from ever being served over HTTP (an Apache-era convention that Nginx ignores by default, but the files sometimes linger in a migrated codebase).
Enable the site
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t # always test before reloading
sudo systemctl reload nginxnginx -t catches a syntax error before it takes down your currently-running config — always run it before reload, not after something breaks.
If PHP files still download instead of running
Two things to check, in order:
- Confirm the socket path actually matches your installed PHP-FPM version:
ls /run/php/and make sure the filename in your config matches what's actually there. - Confirm PHP-FPM is actually running:
sudo systemctl status php8.3-fpm. A stopped FPM service produces the exact same "downloads instead of runs" symptom as a wrong socket path.