2026年11月13日
2026年11月13日
Run PHP on Nginx with PHP-FPM: Setup for Ubuntu 24.04 and RHEL
This article is a localized edition of the Japanese original. Read the Japanese original
Nginx does not execute PHP itself. When it receives a PHP request, it hands the work to PHP-FPM (PHP FastCGI Process Manager) over FastCGI and returns the result to the browser. So to run PHP with Nginx, you install PHP-FPM and tell Nginx to "pass requests for .php to PHP-FPM."
The steps in this article were tested on Ubuntu 24.04 (Nginx 1.24, PHP 8.3) as of November 2026. Differences on RHEL-family systems (AlmaLinux/Rocky Linux 9) are covered later.
Setting it up on Ubuntu/Debian
1. Install Nginx and PHP-FPM
sudo apt update
sudo apt install nginx php-fpm
php-fpm is a package that installs FPM for the distribution's default PHP version. On Ubuntu 24.04 it installs php8.3-fpm. Other distributions such as Debian ship a different version, so check the actual name with the next step.
2. Check the PHP-FPM socket
Nginx connects to PHP-FPM through a file called a UNIX domain socket. The socket path changes with the PHP version, so check it rather than guessing.
ls /run/php/
grep '^listen =' /etc/php/*/fpm/pool.d/www.conf
systemctl status php8.3-fpm --no-pager
On Ubuntu 24.04 it is listen = /run/php/php8.3-fpm.sock. You will use this value in the Nginx configuration next.
Older articles on the internet, and the comments left in Ubuntu 24.04's /etc/nginx/sites-available/default, say php7.4-fpm.sock. PHP 7.4 isn't installed, so copying that as is results in a 502 error.
3. Write the Nginx server block
Create a configuration file for the site.
sudo nano /etc/nginx/sites-available/example.conf
server {
listen 80;
server_name example.com;
root /var/www/example;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
location ~ \.php$ is the part that passes requests ending in .php to PHP-FPM. Put the socket you checked in step 2 in fastcgi_pass.
The snippets/fastcgi-php.conf shipped with Ubuntu/Debian contains the following:
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
try_files $fastcgi_script_name =404;
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
fastcgi_index index.php;
include fastcgi.conf;
The final fastcgi.conf includes SCRIPT_FILENAME ($document_root$fastcgi_script_name), which tells PHP-FPM which file to execute. So when you include this snippet, you don't need to add fastcgi_param SCRIPT_FILENAME or include fastcgi_params; yourself.
In addition, try_files $fastcgi_script_name =404; makes sure "PHP files that don't exist are not passed to PHP-FPM; a 404 is returned instead." This also blocks the long-known trick of appending /x.php to an uploaded image or similar file to get it executed as PHP. In our test, both /nope.php (nonexistent) and /up.txt/x.php returned 404.
location ~ /\.ht keeps leftover Apache files such as .htaccess from being read from outside.
4. Enable and apply
sudo ln -s /etc/nginx/sites-available/example.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
If the default site (/etc/nginx/sites-enabled/default) is also running on port 80, requests that don't match your server_name go to the default site. If you don't need it, disable it with sudo rm /etc/nginx/sites-enabled/default.
Checking that PHP works
Put a test file in the public directory.
sudo mkdir -p /var/www/example
echo '<?php echo "OK ", PHP_VERSION, " ", php_sapi_name();' | sudo tee /var/www/example/index.php
curl -H 'Host: example.com' http://127.0.0.1/
If you see output like the following, PHP is being executed by PHP-FPM (fpm-fcgi is the sign that it runs through PHP-FPM).
OK 8.3.6 fpm-fcgi
You can also view detailed settings with phpinfo(), but it exposes server information to the outside, so delete it as soon as you are done checking.
Differences on RHEL-family systems (AlmaLinux/Rocky Linux 9)
On RHEL-family systems, the php-fpm package also installs configuration files for Nginx.
sudo dnf install nginx php-fpm
sudo systemctl enable --now php-fpm nginx
| Item | Ubuntu 24.04 | RHEL family 9 |
|---|---|---|
| Pool configuration | /etc/php/8.3/fpm/pool.d/www.conf |
/etc/php-fpm.d/www.conf |
| Socket | /run/php/php8.3-fpm.sock |
/run/php-fpm/www.sock |
| Run-as user | www-data |
apache |
| Service name | php8.3-fpm |
php-fpm |
| Nginx configuration | Write it yourself (a snippet is provided) | /etc/nginx/conf.d/php-fpm.conf and /etc/nginx/default.d/php.conf are installed |
/etc/nginx/conf.d/php-fpm.conf defines /run/php-fpm/www.sock as upstream php-fpm, and /etc/nginx/default.d/php.conf contains a location that passes .php to fastcgi_pass php-fpm;. Nginx's standard server block includes default.d/*.conf, so PHP works without any extra configuration. If you write your own server block, adding include /etc/nginx/default.d/*.conf; inside it reuses the same settings.
In www.conf, listen.acl_users = apache,nginx lets the nginx user connect to the socket too. If SELinux is enabled and you serve a directory other than /var/www, the file labels also need to match.
Tuning the PHP-FPM pool
The pool (www.conf) defines how many processes run PHP and which user they run as. The Ubuntu 24.04 defaults are:
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
The setting with the biggest impact is pm.max_children (the maximum number of PHP processes running at once). Concurrent requests beyond it have to wait, and if the wait is long, Nginx times out and returns a 504 or similar error. Set it too high, on the other hand, and the server runs out of memory and becomes unstable.
A rule of thumb is "memory available for PHP ÷ memory per process." Measure the memory per process while the site is actually running.
ps -o rss= -C php-fpm8.3 | awk '{ s += $1; n++ } END { if (n) printf "%d procs, avg %.0f MB\n", n, s/n/1024 }'
For example, if PHP can use 1 GB and each process averages 60 MB, a pm.max_children of around 15 is the upper guideline.
After changing settings, check the syntax and then reload.
sudo php-fpm8.3 -t
sudo systemctl reload php8.3-fpm
For php.ini settings (such as upload limits), edit /etc/php/8.3/fpm/php.ini, the file used by PHP-FPM. Editing /etc/php/8.3/cli/php.ini, which is for the command line, has no effect on the web.
Troubleshooting
Start with the Nginx error log.
sudo tail -n 50 /var/log/nginx/error.log
sudo journalctl -u php8.3-fpm -n 50 --no-pager
| Symptom | Example log | Main cause and fix |
|---|---|---|
| 502 Bad Gateway | connect() to unix:/run/php/php8.3-fpm.sock failed (2: No such file or directory) |
PHP-FPM is stopped, or the socket path is wrong. Check with systemctl status and ls /run/php/ |
| 502 Bad Gateway | connect() ... failed (13: Permission denied) |
The socket's owner doesn't match the user Nginx runs as. Check listen.owner/listen.group |
| "File not found." is shown (404) | Primary script unknown |
The file path passed to PHP-FPM (SCRIPT_FILENAME) is wrong. This tends to happen with hand-written configuration that doesn't use the snippet. With the snippet, a wrong root becomes Nginx's own 404 instead |
| PHP files are downloaded | None | There is no location ~ \.php$, or another location matches first |
| 504 Gateway Time-out | upstream timed out |
PHP takes too long, or requests are waiting because pm.max_children is too low |
If you stop PHP-FPM as a test, you can reproduce the 502 and the log in the first row. It is also good practice for isolating causes.
How to investigate 502 errors is covered in How to fix Nginx 502 Bad Gateway, PHP-FPM's own settings in Installing and configuring PHP-FPM, and the configuration for running WordPress in Configuring Nginx for WordPress.
Summary
- Nginx passes requests for
.phpto PHP-FPM withfastcgi_pass - Check the socket path with
ls /run/php/and thelistenline inwww.conf, and don't copyphp7.4-fpm.sockfrom old articles - On Ubuntu/Debian, the two lines
include snippets/fastcgi-php.conf;andfastcgi_passare enough; addingSCRIPT_FILENAMEis unnecessary - On RHEL-family systems, the php-fpm package installs the Nginx configuration and the socket is
/run/php-fpm/www.sock - After changes, check with
nginx -tandphp-fpm8.3 -tbefore reloading, and use the error logs to isolate any problem
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)