The classic Apache setup loads PHP inside every Apache process with mod_php, which forces the prefork MPM. Each process then carries the full weight of PHP, even when it only sends a CSS file. Running PHP in a separate PHP-FPM service lets Apache use the lighter event MPM and hand only PHP requests to FPM. This guide makes that switch on Ubuntu, Debian, AlmaLinux and Rocky Linux.
Before you start
- Apache serving at least one PHP site through a virtual host.
- PHP-FPM installed for the version you want, for example
php8.3-fpmon Ubuntu. To run several versions side by side, see Install multiple PHP versions on Ubuntu and Debian. - A maintenance window of a few minutes. Apache restarts during the switch.
- A copy of your config:
sudo cp -a /etc/apache2 ~/apache2-backup # Ubuntu and Debian
sudo cp -a /etc/httpd ~/httpd-backup # AlmaLinux and RockySwitch MPM and modules on Ubuntu and Debian
Check which MPM and PHP module are active:
apache2ctl -M | grep -E "mpm|php"If you see mpm_prefork_module and php8.3_module, disable them and enable the event MPM and the FastCGI proxy. Replace 8.3 with your version:
sudo apt install -y php8.3-fpm
sudo a2dismod php8.3 mpm_prefork
sudo a2enmod mpm_event proxy_fcgi setenvifThe php8.3-fpm package ships a ready-made Apache snippet. Enabling it routes every .php file on the server to that FPM version:
sudo a2enconf php8.3-fpmTest and restart (a reload is not enough when changing MPM):
sudo apachectl configtest
sudo systemctl restart apache2Warning: If configtest fails after disabling mod_php, do not restart. A restart with a broken config takes every site down. Fix the reported line, or restore your backup folder.
Switch MPM and modules on AlmaLinux and Rocky Linux
On these systems the php package already installs php-fpm and a handler file, /etc/httpd/conf.d/php.conf, that sends .php to the FPM socket /run/php-fpm/www.sock. The event MPM is the default. Check:
httpd -M | grep -E "mpm|proxy_fcgi"You want mpm_event_module and proxy_fcgi_module. If prefork is active, edit /etc/httpd/conf.modules.d/00-mpm.conf so only the mod_mpm_event.so line is uncommented. Then start FPM and restart Apache:
sudo systemctl enable --now php-fpm
sudo apachectl configtest
sudo systemctl restart httpdSet the PHP handler per site
A global handler is fine for one PHP version. To control PHP per virtual host, put a SetHandler in the vhost instead. Add this inside <VirtualHost>:
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>The part before the | is the socket of the FPM pool. The fcgi://localhost part is a label Apache needs; it does not open a network connection.
Find the right socket path for each version:
ls /run/php/ # Ubuntu and Debian, e.g. php8.2-fpm.sock, php8.3-fpm.sock
ls /run/php-fpm/ # AlmaLinux and Rocky, e.g. www.sockUsing <FilesMatch> rather than ProxyPassMatch means Apache only passes files that really exist, so a request for /missing.php gets a normal 404 from Apache.
Choose a PHP version per virtual host
With two FPM versions installed, point each site at its own socket. One vhost:
<VirtualHost *:80>
ServerName legacy.example.com
DocumentRoot /var/www/legacy/public
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
</FilesMatch>
</VirtualHost>And another vhost uses /run/php/php8.3-fpm.sock. If you enabled a global phpX.Y-fpm conf earlier, the vhost setting overrides it for that site. To keep things clear, you can disable the global one with sudo a2disconf php8.3-fpm once every vhost has its own handler.
Tip: For full isolation between sites, give each one its own FPM pool and Linux user. See Run each site in its own PHP-FPM pool.
Check it works
Confirm the MPM:
apachectl -V | grep -i mpmIt should say Server MPM: event. Then create a temporary test file in a site's document root:
echo '<?php echo php_sapi_name(), " ", PHP_VERSION;' | sudo tee /var/www/example.com/public/sapi.php
curl http://example.com/sapi.phpThe output should start with fpm-fcgi followed by the version you expect. Delete the file afterwards:
sudo rm /var/www/example.com/public/sapi.phpTroubleshooting
AH00534: Apache is running a threaded MPM, but your PHP Module is not compiled to be threadsafe:mod_phpis still enabled. Disable it witha2dismod phpX.Yand restart.- PHP files download instead of running: no handler matches. Check the
SetHandlerblock, or that the global FPM conf is enabled. See PHP files downloading instead of executing. - 503 Service Unavailable: Apache cannot reach the socket. Make sure
phpX.Y-fpmis running and the socket path in the vhost exists. - Settings in
.htaccesssuch asphp_valuecause a 500 error: those lines only work withmod_php. Move them to the pool'sphp_admin_valuesettings or a.user.inifile. - Permission denied on the socket (AlmaLinux and Rocky): the socket's
listen.ownerandlisten.groupmust allow theapacheuser.
Next steps
- Size Apache for your RAM now that PHP is separate: Tune Apache MPM settings.
