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 .php to PHP-FPM with fastcgi_pass
  • Check the socket path with ls /run/php/ and the listen line in www.conf, and don't copy php7.4-fpm.sock from old articles
  • On Ubuntu/Debian, the two lines include snippets/fastcgi-php.conf; and fastcgi_pass are enough; adding SCRIPT_FILENAME is 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 -t and php-fpm8.3 -t before 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)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲