Apache httpd, mod_php and PHP-FPM
Apache and PHP: patching mod_php today, moving to PHP-FPM next
If Apache runs PHP through mod_php, PHP sits inside every Apache child process: the configuration the PHP project validated PHP2Shell against, and the one it rates highest risk. Install the fixed module package and fully restart Apache now, then plan the move to PHP-FPM, which php.net recommends for all modern deployments.
Last reviewed against php.net, the Apache httpd documentation, Red Hat and Debian's Apache and PHP packaging.
Two set-ups
Two ways Apache can run PHP
Apache either carries PHP inside itself or passes PHP requests to a separate service. The choice is tied to Apache's multi-processing module (MPM), and it decides both what you restart after an update and how exposed you were to PHP2Shell.
mod_php: PHP inside Apache
PHP compiled as an Apache module and loaded into Apache's own processes. Every Apache child carries a PHP interpreter, and PHP reports its server API as apache2handler.
mod_proxy_fcgi: PHP handed to PHP-FPM
Apache keeps serving HTTP and passes each PHP request over FastCGI to a separate PHP-FPM service. PHP reports fpm-fcgi, and it can be restarted without touching Apache.
The prefork MPM
Apache's non-threaded, pre-forking model, which keeps each request in a process of its own. mod_php needs it unless PHP was built thread safe, and Debian's mod_php package declares that dependency.
The event MPM
Apache's threaded model, and the default it picks on almost every modern system. It suits PHP-FPM; it cannot host a mod_php build that is not thread safe.
| mod_php | PHP-FPM through mod_proxy_fcgi | |
|---|---|---|
| Where PHP runs | Inside each Apache process | In separate PHP-FPM worker processes |
| Apache MPM | prefork, unless PHP is a thread-safe (ZTS) build | event, Apache's usual default |
| PHP_SAPI reports | apache2handler | fpm-fcgi |
| Debian 13 package | libapache2-mod-php8.4 | php8.4-fpm |
| Restart after a PHP update | Apache | PHP-FPM; Apache can stay up |
| php.net's position | Do not use for new installations | Recommended for modern deployments |
| PHP2Shell | Remote code execution validated; rated highest risk | Likely susceptible; one route validated |
Highest risk
Why mod_php servers come first
The PHP project's advisories were tested against Apache with mod_php, default limits and the prefork MPM. For CVE-2026-109618 that is the setup where remote code execution was validated. For CVE-2026-109619 both confirmed routes, PHP 8.5's fatal error backtraces and session upload progress, were shown against mod_php.
php.net's advisory names mod_php as the highest-risk setup, and Red Hat's bulletin says environments running mod_php with the default configuration are at higher risk. If your servers mix set-ups, patch the mod_php ones before anything else.
php.net's position on mod_php
The PHP manual's Apache installation page opens with "Do not use mod_php for new installations". It keeps the mod_php steps for reference and for the rare cases that truly need PHP embedded in Apache, and points everyone else to PHP-FPM with mod_proxy_fcgi, citing better resource management, process isolation, restarting PHP without restarting Apache, and compatibility with the event MPM.
Can the site not be patched or moved quickly? Use the risk calculator to decide whether it should stay online meanwhile.
Check first
Find out which one your server uses
Ask Apache which MPM and modules it has loaded. Debian and Ubuntu
load mod_php under the name php_module.
# Debian and Ubuntu: the MPM Apache is using
a2query -M
# Loaded modules: php_module means mod_php, proxy_fcgi_module means FastCGI
sudo apache2ctl -M | grep -E 'php|proxy_fcgi'
# RHEL and its rebuilds
sudo httpd -M | grep -E 'php|proxy_fcgi'
A prefork MPM with php_module is
mod_php. An event MPM with proxy_fcgi_module
is PHP-FPM. On RHEL 8 and later, Apache runs PHP through PHP-FPM by
default. A hosting control panel can also tell you, and so can the
server API the site reports, explained on
how to check your PHP version.
Step by step
Updating PHP while it still runs as mod_php
Do this today, even if a move to PHP-FPM is planned. The commands are for Debian and Ubuntu; panel-managed servers update through cPanel or Plesk instead.
-
1. Install the fixed packages
sudo apt update sudo apt upgrade -
2. Check the module package you now have
# The exact mod_php package version, to compare with the advisory dpkg-query -W 'libapache2-mod-php*'On Debian 13 the fixed version is
8.4.27-1~deb13u1. Other releases keep older-looking numbers with the fix backported; the Linux guide lists them. -
3. Test the configuration and restart Apache in full
sudo apache2ctl configtest sudo systemctl restart apache2 # RHEL and its rebuilds: httpdWith mod_php, restarting Apache is restarting PHP. A full service restart replaces the parent process as well as every child; see restarting Apache for why a graceful restart is not the step to rely on.
-
4. Confirm
Use the checks below.
If the fixed package is not there yet
Until the patched package arrives, the advisories offer stopgaps,
none of them a full mitigation. Under mod_php they can go in
Apache's configuration,
where application code cannot undo them, or in a file under
/etc/php/8.4/apache2/conf.d/.
LimitRequestBody is the request
size limit in front of PHP that the advisory suggests; its default
is 1 GiB from httpd 2.4.54, and unlimited before that. Restart
Apache after any change.
# Server or virtual host configuration, not .htaccess:
# php.net says php_admin_flag cannot be used in .htaccess files
<IfModule php_module>
php_admin_flag session.upload_progress.enabled off
# PHP 8.5 only
php_admin_flag fatal_error_backtraces off
</IfModule>
# A ceiling on request bodies before PHP sees them (in bytes; 10 MiB here)
LimitRequestBody 10485760 The longer-term change
Moving a site from mod_php to PHP-FPM
On Debian and Ubuntu the move takes a handful of commands, because the FPM package already ships the Apache configuration it needs. Stay on the PHP branch you run today so that only one thing changes, and test on a staging copy first if you have one.
Before you start: settings that live in Apache
php.net documents php_value, php_flag, php_admin_value and
php_admin_flag as settings for PHP running as an Apache module.
Search the Apache configuration and every .htaccess
file for them and move each one: per-directory values into
.user.ini files, which PHP reads only
under CGI and FastCGI, and server-wide values into the FPM pool or
php.ini.
# .htaccess under mod_php
php_flag display_errors off ; .user.ini in the same directory, read by PHP under FastCGI
display_errors = Off
PHP re-reads .user.ini files every
300 seconds by default, so allow five minutes before judging a
change.
The switch
# 1. Install PHP-FPM for the branch you already run (Debian 13: 8.4)
sudo apt install php8.4-fpm
# 2. Swap mod_php and prefork for FastCGI and the event MPM
sudo a2dismod php8.4 mpm_prefork
sudo a2enmod mpm_event proxy_fcgi setenvif
sudo a2enconf php8.4-fpm
# 3. Check the configuration, then restart Apache
sudo apache2ctl configtest
sudo systemctl restart apache2 - Order matters. Debian's mod_php module file declares that it depends on prefork, and the event MPM's file declares a conflict with prefork, so mod_php and prefork go first.
- Debian's a2enmod asks for a restart, not a reload, after module changes, and Apache refuses to start with more than one MPM loaded. The configuration test catches most mistakes first.
-
The enabled
php8.4-fpmconfiguration sends files ending .php, .phar and .phtml to/run/php/php8.4-fpm.sock, and refuses requests for raw .phps source files. -
mod_php's php.ini changes do not follow you: PHP-FPM reads
/etc/php/8.4/fpm/, not/etc/php/8.4/apache2/.
If you need to go back
sudo a2disconf php8.4-fpm
sudo a2dismod mpm_event
sudo a2enmod mpm_prefork php8.4
sudo systemctl restart apache2 Rolling back puts the site on mod_php again, so only do it on a patched build. On RHEL 8 and later there is usually nothing to move: PHP-FPM is already the default. Pools, restarts and running several branches are covered in the PHP-FPM guide.
Restarting Apache
Graceful, restart, or a full service restart?
Apache has three ways to start again, and they replace different
amounts of the running server. apachectl's graceful and restart
run a syntax check first and refuse to go ahead if it fails. A
systemd restart stops Apache before it starts it again, and
apachectl's start does no such check, so a broken configuration
leaves the site down: run apache2ctl configtest
first, every time.
| Command | What Apache does | After a PHP update |
|---|---|---|
sudo apache2ctl graceful | Sends SIGUSR1. Each child finishes its current request and is then replaced; the parent process stays up and re-reads its configuration. | Not the step to rely on after updating PHP. |
sudo apache2ctl restart | Sends SIGHUP. Children are stopped at once and replaced; the parent stays up and re-reads its configuration. | Every child is new, but the original parent process is still running. |
sudo systemctl restart apache2 | systemd stops the whole service, parent included, then starts it again. On Debian and Ubuntu the stop is apachectl graceful-stop, so requests already running are allowed to finish first. | The one to use after updating mod_php. On RHEL the service is httpd. |
Red Hat's bulletin gives
systemctl restart httpd for mod_php and
rejects a reload, as workers that survive one may still carry
affected state. On Debian and Ubuntu,
systemctl reload apache2 is
apachectl graceful under another name, so it falls short in the same
way. Under PHP-FPM the restart that matters is of the FPM service,
and Apache can carry on.
Confirm it worked
Check what Apache is serving now
# Which PHP modules Apache has loaded now
sudo apache2ctl -M | grep -E 'php|proxy_fcgi'
# "Active: active (running) since" should be after your restart
systemctl status apache2 Then ask a page on the site. Upload this file, load it once and delete it straight away:
<?php
// sapi-check.php: open it once, then delete it from the server
echo phpversion(), ' via ', php_sapi_name(); -
apache2handlermeans mod_php is still in use;fpm-fcgimeans the move to PHP-FPM worked. - Expect your branch's fixed release. On a distribution build, the package version from step 2 is the one to compare with the advisory.
- Check every virtual host that matters. A site with its own handler configuration can end up on a different PHP from its neighbours.
Since 26 October 2026 both vulnerabilities have been in CISA's Known Exploited Vulnerabilities catalogue. If this server was reachable while it ran a vulnerable mod_php, the update does not remove anything already left behind. Has your PHP site been compromised?
Troubleshooting
When Apache or the site misbehaves
a2enmod says a module "is enabled - cannot proceed due to conflicts"
Debian's a2enmod will not enable an MPM while another is enabled. Disable php8.4 and mpm_prefork first, as in the move above, then enable mpm_event.
Apache will not start: "More than one MPM loaded"
Apache allows exactly one MPM. Something still loads the old one: check mods-enabled for leftover prefork links, or on RHEL the LoadModule lines in the MPM configuration file.
Pages return a 500 error after the move to PHP-FPM
Look for php_value or php_flag lines in .htaccess files. They belong to mod_php, and Apache's own .htaccess guide shows that a directive it cannot use produces a server error. The error log names the file. Move the settings to .user.ini or the FPM pool.
PHP files are sent to the browser instead of running
Apache has no PHP handler for the site. Check that the php8.4-fpm configuration is enabled with a2enconf, and that proxy_fcgi_module appears in apache2ctl -M.
Your php.ini changes disappeared after the move
Each server API has its own configuration tree on Debian and Ubuntu: /etc/php/8.4/apache2/ for mod_php and /etc/php/8.4/fpm/ for PHP-FPM. Copy the changes you made into the FPM tree.
The site still reports apache2handler
mod_php is still enabled, or Apache was not restarted. a2query -m php8.4 tells you whether the module is on; disable it and restart Apache in full.
Quick answers
Apache, mod_php and PHP2Shell: questions, answered
What is mod_php?
mod_php is PHP built as a module for the Apache web server. Apache loads it into its own worker processes, so PHP runs inside Apache rather than as a separate service. On Debian and Ubuntu it is packaged as libapache2-mod-php followed by the branch, and PHP reports its server API as apache2handler.
Is mod_php deprecated?
php.net now says not to use mod_php for new installations, keeps its mod_php instructions for historical reference and for the rare cases that need PHP embedded in Apache, and recommends PHP-FPM with mod_proxy_fcgi for modern deployments. Existing mod_php servers still receive the fixed releases.
Is apachectl graceful enough after updating mod_php?
Use a full restart instead. A graceful restart lets children finish their requests and keeps the parent process running. Red Hat's advice for these vulnerabilities is a full restart, systemctl restart httpd for mod_php, rather than any kind of reload.
Does moving to PHP-FPM fix PHP2Shell on its own?
No. PHP-FPM is affected too: the PHP project treats it as likely susceptible and validated one of the CVE-2026-109619 routes against it. The fix is still the patched PHP release for your branch, or the fixed package from your distribution. Moving to FPM is the longer-term change that takes you off the highest-risk setup.
Can I keep mod_php once it is patched?
The patched release closes both vulnerabilities whichever way PHP runs. Keeping mod_php is a decision about the next flaw: php.net advises against it for new installations, it ties you to the prefork MPM, and the PHP project rated it the riskiest setup this time.
What does Apache use on RHEL?
On RHEL 8 and later the default deployment runs PHP through PHP-FPM, not mod_php; Red Hat names mod_php as the default only on RHEL 7. Restart php-fpm after updating PHP there, and httpd only if you changed its configuration.
Running the official Docker php image's apache tags? They use mod_php with prefork too: see the Docker guide. Hosting panels, Windows and the other routes are on the update hub.
Know your exposure
Find every PHP server you run
Patching the server you know about is the easy part. A vulnerability scan finds the forgotten staging site, the old microsite and the appliance with PHP inside, then tells you which ones are still exposed.