Install PHP-FPM for a website
Nginx cannot execute PHP: it forwards requests to a dedicated service, PHP-FPM. This separation lets you restart PHP without stopping Nginx, limit resources per pool and avoid running files with the web server's privileges. Here is PHP 8.4-FPM on Debian 13, the socket configuration and the matching Nginx block.
Install PHP 8.4
sudo apt update
sudo apt install -y php8.4-fpm php8.4-cli php8.4-mysql
systemctl status php8.4-fpm
ls -l /run/php/The package creates the /run/php/php8.4-fpm.sock socket and the php8.4-fpm service. Install only the extensions you need (php8.4-curl, php8.4-mbstring, php8.4-xml, php8.4-gd), then reload the service. php -v on the command line uses the CLI client, which may not share the same configuration as FPM.
The pool and the socket
/etc/php/8.4/fpm/pool.d/www.conf sets user = www-data, group = www-data and listen = /run/php/php8.4-fpm.sock. A Unix socket avoids exposing PHP on a TCP port. listen.owner, listen.group and listen.mode = 0660 must let Nginx, which also runs as www-data, access it.
sudo php-fpm8.4 -t
sudo systemctl reload php8.4-fpmphp-fpm8.4 -t checks the configuration syntax. If you change listen, update the Nginx block immediately: a missing socket produces a 502.
Nginx to PHP-FPM
server {
listen 80;
listen [::]:80;
server_name your-domain.com;
root /var/www/site;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
}snippets/fastcgi-php.conf includes try_files and computes SCRIPT_FILENAME. Do not replace it with an incomplete version. Without try_files, a nonexistent PHP URL can be passed to FPM. Validate and reload:
sudo nginx -t
sudo systemctl reload nginxPermissions
Files belong to the deployment user, with the www-data group. Directories are 750, files are 640. PHP only needs to read the code. An uploads directory must be writable by www-data, but never executable.
sudo chown -R deploy:www-data /var/www/site
sudo find /var/www/site -type d -exec chmod 750 {} \;
sudo find /var/www/site -type f -exec chmod 640 {} \;777 permissions are not a solution: they open the code to every account on the server.
Test and troubleshoot
Create info.php with <?php phpinfo();, open it, then delete it immediately. A 502 means a missing socket, wrong path or permission issue; 403, index or rights; 404, root or try_files. Check /var/log/nginx/error.log, /var/log/php8.4-fpm.log and journalctl -u php8.4-fpm. Verify that www-data can traverse every parent directory with namei -l /var/www/site/index.php.
Advertisement
Was this article helpful?
Read next
- Enable HTTPS with CertbotObtain and renew a Let's Encrypt certificate with Certbot and Nginx, then enable HSTS carefully.
- Install and secure RedisInstall Redis, restrict it to local listening, add authentication and back up its data.
- Keep a Node.js app online with PM2Run a Node.js application under PM2, restart it at boot and read its logs.