PHP with Apache: PHP-FPM, mod_proxy_fcgi and supported versions
The modern way to run PHP on Apache httpd 2.4 is PHP-FPM, a separate pool of PHP processes that Apache talks to through mod_proxy_fcgi. It works with the event MPM and HTTP/2, lets each site run as its own user, and lets you restart PHP without touching Apache. The PHP manual now says plainly: do not use mod_php for new installations.
Supported PHP versions
Each PHP branch gets two years of active support (bug and security fixes) and then two years of security fixes only. As of November 2026, php.net lists these branches as supported:
| Branch | First release | Active support until | Security fixes until | Latest release |
|---|---|---|---|---|
| 8.5 | Nov 20, 2025 | Dec 31, 2027 | Dec 31, 2029 | 8.5.12 |
| 8.4 | Nov 21, 2024 | Dec 31, 2026 | Dec 31, 2028 | 8.4.27 |
| 8.3 | Nov 23, 2023 | Dec 31, 2025 (ended) | Dec 31, 2027 | 8.3.36 |
| 8.2 | Dec 8, 2022 | Dec 31, 2024 (ended) | Dec 31, 2026 | 8.2.35 |
- 8.5newest stable branch (as of November 2026)
- Dec 31, 2026PHP 8.2 reaches end of life
- Oct 21, 20268.5.12, 8.4.27, 8.3.36 and 8.2.35 fixed two critical remote code execution flaws
PHP 8.1 reached end of life on December 31, 2025, and 8.0 on November 26, 2023. PHP 8.6.0 is at the release-candidate stage (RC4 came out on October 21, 2026, with RC5 planned for November 5). If you build PHP yourself or use packages straight from php.net, upgrade now if you run anything older than the releases in the table: the October 2026 releases fixed CVE-2026-109618 and CVE-2026-109619, which php.net rates as critical.
Distribution packages follow their own calendar. Debian 13 ships PHP 8.4 and Debian 12 ships 8.2; Ubuntu 24.04 ships 8.3 and Ubuntu 26.04 ships 8.5 (the version behind each release’s php-fpm package). Distributions backport security fixes, so a Debian 12 server with PHP 8.2 can still get fixes after php.net stops, for as long as Debian supports that release. Check your version with php -v.
Why PHP-FPM and not mod_php
mod_php loads the PHP interpreter into every Apache process. Unless PHP is compiled with thread safety (ZTS), which distribution packages normally are not, mod_php only works with the prefork MPM: one process per connection, no threads. That costs a lot of memory, and HTTP/2 under prefork handles only one request per connection at a time. On Debian and Ubuntu, installing libapache2-mod-php even switches Apache to prefork automatically.
With PHP-FPM, Apache can use the event MPM. Static files are served by Apache’s threads without loading PHP, and PHP runs in a fixed pool that you size yourself. On Fedora the packaged configuration already sends PHP to FPM by default and calls mod_php deprecated.
Set up PHP-FPM on Debian and Ubuntu
Install the FPM package for your PHP version. php-fpm picks the distribution’s default version; php8.4-fpm names it exactly. The package does not change Apache by itself; it tells you which two commands to run:
sudo apt install php-fpm
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.4-fpm
sudo apache2ctl configtest
sudo systemctl reload apache2
The enabled file /etc/apache2/conf-enabled/php8.4-fpm.conf sends .php, .phar and .phtml files to the socket /run/php/php8.4-fpm.sock, passes the Authorization header on to PHP, and blocks .phps source files. The FPM service is called php8.4-fpm.
If mod_php was installed before, turn it off and go back to the event MPM. This needs a full restart, not a reload:
sudo a2dismod php8.4 mpm_prefork
sudo a2enmod mpm_event
sudo apache2ctl configtest
sudo systemctl restart apache2
Set up PHP-FPM on RHEL, AlmaLinux, Rocky Linux and Fedora
sudo dnf install php php-fpm
sudo systemctl enable --now php-fpm
sudo apachectl configtest
sudo systemctl reload httpd
The packaged /etc/httpd/conf.d/php.conf sends .php and .phar files to /run/php-fpm/www.sock unless mod_php is loaded, and adds index.php to DirectoryIndex. The default pool in /etc/php-fpm.d/www.conf runs as user apache. The mod_proxy_fcgi module is already loaded by 00-proxy.conf.
Write the handler yourself
For a custom setup, or on a system without packaged glue, put this in the virtual host. It needs mod_proxy, mod_proxy_fcgi and (for the first line) mod_setenvif:
<VirtualHost *:443>
ServerName example.com
DocumentRoot /var/www/example.com/public
DirectoryIndex index.php index.html
SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
<FilesMatch "\.php$">
<If "-f %{REQUEST_FILENAME}">
SetHandler "proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost"
</If>
</FilesMatch>
# for front-controller frameworks (Laravel, Symfony, WordPress):
FallbackResource /index.php
# ... SSLEngine and certificate lines ...
</VirtualHost>
SetHandler "proxy:unix:…|fcgi://localhost"(httpd 2.4.10 and later) lets Apache map the URL to a file first and pass that file name to FPM. The part after the pipe is only a label; with a Unix socket the host name is ignored.- The
<If "-f …">test sends only existing files to PHP, so a request for a missing.phpfile gets Apache’s normal 404 instead of FPM’s “File not found.” - If FPM listens on TCP instead of a socket, use
SetHandler "proxy:fcgi://127.0.0.1:9000". Never let an FPM port be reachable from outside: anyone who can talk to it can run code.
Settings in a virtual host override the global ones, because httpd applies sections inside <VirtualHost> after those outside it. That is how one site can use a different PHP version than the global default.
One pool per site, several PHP versions
Give each site its own FPM pool so that a bug in one site cannot read another site’s files. On Debian, create /etc/php/8.4/fpm/pool.d/example.conf (RHEL: /etc/php-fpm.d/example.conf):
[example]
user = example
group = example
listen = /run/php/php8.4-fpm-example.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 10
pm.process_idle_timeout = 10s
On RHEL use apache instead of www-data. Restart FPM (sudo systemctl restart php8.4-fpm), then point that site’s SetHandler at /run/php/php8.4-fpm-example.sock.
Several PHP versions work the same way: install, for example, php8.3-fpm and php8.4-fpm side by side, and each virtual host’s SetHandler names the socket of the version it needs. Your distribution may ship only one version; others then have to come from a separate repository, which also becomes responsible for their security updates.
How many PHP processes?
pm.max_children caps the number of PHP requests running at the same time. Size it from memory, not from guesswork. Measure the average memory of a busy FPM process:
ps -o rss= -C php-fpm8.4 | awk '{s+=$1; n++} END {print s/n/1024 " MB"}'
(On RHEL the process name is php-fpm.) Example: you can give PHP 2 GB and each process uses about 60 MB. 2,048 ÷ 60 ≈ 34, so set pm.max_children = 30 to keep some headroom. Requests above that limit wait in the queue rather than pushing the server into swap.
Check that it works
Create a test file, look at it, and delete it straight away, because it reveals a lot about your server:
echo '<?php phpinfo();' | sudo tee /var/www/example.com/public/info.php
curl -s https://example.com/info.php | grep -o 'FPM/FastCGI' | head -1
sudo rm /var/www/example.com/public/info.php
The Server API line on the page should read FPM/FastCGI. If it reads Apache 2.0 Handler, mod_php is still active.
Common problems
- The browser downloads the PHP file or shows its source
- No handler is set for
.php. On Debian, check thata2enconf php8.4-fpmwas run and that the version in the name matches the installed FPM. - 503 Service Unavailable
- Apache cannot reach the socket. Check that FPM is running (
systemctl status php8.4-fpm), that the socket path inSetHandlermatcheslistenin the pool, and that Apache’s user may open it (listen.owner,listen.group). The Apache error log names the socket it tried. - “File not found.” or “Primary script unknown”
- FPM got a file name it cannot see: the wrong
DocumentRoot, a pool running in a chroot, or a pool user without read permission. - Logins fail behind HTTP authentication or API tokens are ignored
- The
Authorizationheader did not reach PHP. Add theSetEnvIfNoCaseline shown above.
More error cases are in fixing common errors. With PHP on FPM you can move to the event MPM and turn on HTTP/2.
Sources
- Supported Versions, PHP: Hypertext Preprocessor
- Unsupported Branches, PHP: Hypertext Preprocessor
- PHP news: 8.5.12, 8.4.27, 8.3.36 and 8.2.35 releases and PHP 8.6.0 RC4 (October 2026), php.net
- Apache 2.x on Unix systems, PHP Manual
- FPM configuration, PHP Manual
- mod_proxy_fcgi, Apache httpd 2.4 documentation
- mod_proxy (Access via Handler), Apache httpd 2.4 documentation
- HTTP/2 guide (MPM configuration), Apache httpd 2.4 documentation
- Configuration Sections (merging), Apache httpd 2.4 documentation
- php-fpm Apache configuration in Debian’s php8.4 package, Debian Sources
- php-fpm post-install notice in Debian’s php8.4 package, Debian Sources
- libapache2-mod-php post-install script (MPM switch), Debian Sources
- php-fpm package in Debian 13 (trixie), Debian
- php-fpm package in Debian 12 (bookworm), Debian
- php-fpm package in Ubuntu 24.04 (noble), Ubuntu
- php-fpm package in Ubuntu 26.04 (resolute), Ubuntu
- php.conf in the Fedora php package, Fedora Package Sources
- php-fpm www pool in the Fedora php package, Fedora Package Sources