Websites & apps

Use a different PHP version per website in Nginx

Run each Nginx site on its own PHP version by pointing its server block at a different PHP-FPM socket, give each site a separate pool, and confirm the version without leaving phpinfo exposed.

IntermediateLinux server4 min read

Nginx does not care which PHP version runs a site. It sends PHP requests to whatever socket the server block names in fastcgi_pass. So once several PHP-FPM versions are installed, choosing a version per site is a one-line change. This guide sets that up, adds a dedicated pool per site for cleaner separation, and shows a safe way to confirm which version each site uses.

Before you start

Paths below use Ubuntu and Debian. On AlmaLinux or Rocky Linux with Remi, extra versions have sockets like /var/opt/remi/php82/run/php-fpm/www.sock and pool files in /etc/opt/remi/php82/php-fpm.d/.

See which sockets you have

Each running PHP-FPM version creates its own socket:

Bash
ls -l /run/php/
php8.2-fpm.sock
php8.4-fpm.sock

If a version is missing, start its service:

Bash
sudo systemctl enable --now php8.2-fpm

Point each server block at a socket

Open the first site's config:

Bash
sudo nano /etc/nginx/sites-available/old.example.com

In the PHP location, set fastcgi_pass to the PHP 8.2 socket:

Nginx
location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}

Do the same for the second site, using PHP 8.4:

Nginx
location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}

The snippets/fastcgi-php.conf file ships with the Debian and Ubuntu Nginx packages. It sets SCRIPT_FILENAME and checks the file exists. Test and reload:

Bash
sudo nginx -t && sudo systemctl reload nginx

That is enough to run the two sites on different versions. The next section makes the setup cleaner.

Give each site its own pool

With the steps above, every site on the same version shares the default www pool. A dedicated pool per site lets you set separate worker limits, PHP settings and error logs, and later a separate Linux user.

Create a pool for new.example.com under PHP 8.4:

Bash
sudo nano /etc/php/8.4/fpm/pool.d/new.example.com.conf
INI
[new.example.com]
user = www-data
group = www-data

listen = /run/php/php8.4-fpm-new.example.com.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

php_admin_value[error_log] = /var/log/php/new.example.com-error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 256M

Create the log folder, check the config and restart that version:

Bash
sudo mkdir -p /var/log/php
sudo chown www-data:www-data /var/log/php
sudo php-fpm8.4 -t
sudo systemctl restart php8.4-fpm

Then point the site at the new socket:

Nginx
fastcgi_pass unix:/run/php/php8.4-fpm-new.example.com.sock;
Bash
sudo nginx -t && sudo systemctl reload nginx

The pm.max_children value above is a placeholder. Work out the right number for your RAM with Tune PHP-FPM pools (pm.max_children). To also run each site as its own Linux user, see Run each site in its own PHP-FPM pool and user.

Tip: Name sockets after the site and version. When you move a site to a newer PHP later, you copy its pool file to the new version's pool.d, rename the socket and update one fastcgi_pass line.

Use an upstream name for easier switching

If many server blocks share a version, define the sockets once in /etc/nginx/conf.d/php-upstreams.conf:

Nginx
upstream php82 {
    server unix:/run/php/php8.2-fpm.sock;
}

upstream php84 {
    server unix:/run/php/php8.4-fpm.sock;
}

Then each site uses a short name:

Nginx
fastcgi_pass php84;

Moving every PHP 8.2 site to a new socket is then a single edit.

Check the version safely

The quickest check is a tiny file that prints only the version, not the full phpinfo page, which reveals paths and settings useful to attackers. Use a hard-to-guess name and delete it straight after:

Bash
echo '<?php echo PHP_VERSION;' | sudo tee /var/www/new.example.com/v-7f3a.php
curl -s https://new.example.com/v-7f3a.php; echo
sudo rm /var/www/new.example.com/v-7f3a.php

The output should be 8.4.x. Repeat for the other site and expect 8.2.x.

You can also confirm from the server side which pools are active:

Bash
ps -o user,cmd -C php-fpm8.4 | grep pool

Each line shows the pool name and the user it runs as.

Troubleshooting

  • 502 Bad Gateway: the socket in fastcgi_pass does not exist. Check ls /run/php/ and that the PHP-FPM service for that version is running. sudo tail /var/log/nginx/error.log shows the exact path Nginx tried.
  • Site still shows the old version: you edited a file in sites-available that is not linked in sites-enabled, or you forgot to reload Nginx.
  • "Permission denied" connecting to the socket: listen.owner and listen.group in the pool must allow the Nginx user (www-data on Ubuntu and Debian, nginx on AlmaLinux and Rocky).
  • PHP-FPM fails to restart after adding a pool: two pools use the same socket path or pool name. Run php-fpm8.4 -t to see the error line.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.