mod_php vs. PHP-FPM: Securing UK Servers Post-PHP2Shell
PHP identified mod_php as the worst-case scenario in PHP2Shell. Discover architectural changes and how to migrate to FPM without downtime.
by Cleverson Gouvêa

Choosing between mod_php or PHP-FPM is no longer just a performance decision; it's now a risk management decision. Following the 21/10/2026 advisory regarding PHP2Shell, the PHP security team themselves stated that the risk is higher with mod_php, as the demonstrated attack triggers before any application code runs. In this comparison, I'll outline the architectural, security, and performance differences, and explain how to migrate without taking your site offline.
TL;DR
- PHP2Shell (CVE-2026-109618 and CVE-2026-109619) involves unauthenticated remote code execution, with a CVSS 4.0 score of 9.2. The fix was released on 21/10/2026 for versions 8.5.12, 8.4.27, 8.3.36, and 8.2.35.
- All modes of running PHP for the web are affected: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP, and LiteSpeed. However, PHP classifies mod_php as the highest risk scenario.
- Migrating to PHP-FPM does not replace the patch. The correct order is to update PHP first, then migrate at your leisure.
- The official PHP manual already advises against using mod_php for new installations: it locks Apache into the prefork MPM, which limits concurrency.
- Migration on Apache involves just a few commands, but there are real pitfalls:
php_valuein.htaccessstops working, and the FPM pool needs to be correctly sized.
What Changes in the mod_php or PHP-FPM Choice After PHP2Shell
Until recently, the discussion around mod_php or PHP-FPM revolved around memory, concurrency, and convenience. mod_php was Apache's 'just works' solution, and many older servers remained on it due to inertia.
PHP2Shell has shifted the weight of that choice. The facts, confirmed by official sources:
- Two vulnerabilities, both unauthenticated. CVE-2026-109618 is a memory corruption flaw in out-of-memory handling: PHP is interrupted with partially constructed internal request data, and the end-of-request cleanup treats this data as valid. CVE-2026-109619 is a stale global state (use-after-free): internal data outlives the memory it pointed to, and the next request in the same process uses this garbage.
- Severity. Both have a CVSS 4.0 score of 9.2, according to the PHP security team. The Tenable FAQ notes that identifiers with a 10.0 score circulated previously and were incorrect.
- Timeline. Amazon observed an exploitation attempt on its honeypot on 17/10, four days before disclosure, according to Tenable. Details leaked on 20/10, ahead of the coordinated release date, and PHP brought forward the fix to 21/10 at 14:00 UTC, according to the Huntress.
- Exposure. Unit 42, from Palo Alto Networks, counted over 22.6 million potentially vulnerable instances via Cortex Xpanse.
And the statement that fundamentally alters the mod_php or PHP-FPM equation for infrastructure decision-makers comes from the official PHP advisory: the risk is considered higher for mod_php, "with a demonstrated exploit where an unauthenticated request can trigger the attack before any application code runs".
Translating this for those footing the bill: with mod_php, it doesn't matter if your WordPress, Moodle, or Laravel installation is updated and well-written. The attack occurs before the application even has a chance to do anything.
Architecture: How Each Model Runs PHP
To understand why the risk differs between mod_php or PHP-FPM in practice, it's worth looking at where PHP resides in each model.
mod_php: PHP Inside Apache
With mod_php (the apache2handler module), the PHP interpreter is loaded within each Apache process. Each child process of the web server simultaneously acts as both an HTTP server and a PHP interpreter.
This has two direct consequences:
- Apache is locked into the prefork MPM. The PHP manual states that, without PHP compiled with thread safety (ZTS), mod_php requires prefork, "which significantly limits concurrency". Most distribution packages do not come with ZTS.
- Every process loads PHP, even to serve an image. A request for
logo.pngoccupies an entire process with the interpreter loaded.
PHP-FPM: PHP as a Separate Service
With PHP-FPM (FastCGI Process Manager), PHP runs as its own service, utilising process pools. The web server (Apache with mod_proxy_fcgi or Nginx) simply forwards .php requests to the pool, either via Unix socket or TCP.
Each pool has its own user, process limit, and configurations. The FPM configuration documentation highlights the directives that make a practical difference: user/group, pm.max_children, pm.max_requests, request_terminate_timeout, and php_admin_value – the latter with values that scripts cannot override using ini_set().
What About Nginx Users?
Nginx does not have an embedded PHP module. If your site runs on Nginx, you're already using PHP-FPM (or another FastCGI process), so the mod_php or PHP-FPM question doesn't apply to your current setup. However, this does not exempt you from PHP2Shell: FPM is also on the list of affected configurations.
Security: Why mod_php is the Worst-Case Scenario
The central point in the mod_php or PHP-FPM comparison isn't that FPM is immune. It isn't. The point is that mod_php combines three detrimental factors within the same process.
The Attack Runs Before the Application
The technical report from Datadog Security Labs details what PHP confirmed. In the upload path for CVE-2026-109619, command execution occurred in both mod_php and PHP-FPM, but with FPM, the attack also requires the application to perform a specific operation after initialisation is complete. With mod_php, this condition is absent.
For CVE-2026-109618, Huntress reports that PHP validated full RCE via HTTP against mod_php with standard input and memory limits and normal prefork configurations. The Datadog lab also used Apache prefork with mod_php.
One User for All Sites
With mod_php, every site on the server runs as the Apache user (www-data on Debian/Ubuntu, apache on Red Hat). If an attacker executes commands via one site, they gain Apache's permissions, which typically allow reading files from all sites on that server, including wp-config.php and config.php with database credentials.
With PHP-FPM, the healthy standard is one pool per site, each with its own Linux user. An intrusion into one site is thus contained within that user's permissions. This adheres to the same principle of least privilege that I advocate in the Linux server hardening checklist.
What PHP-FPM Doesn't Solve
Here lies the commercial pitfall: some are marketing "migrate to FPM and be secure". This is not true.
- FPM remains vulnerable without the patch. The official advisory lists PHP-FPM among those affected.
- Datadog explains that a larger pool reduces the chance of success per attempt but does not eliminate the flaw, and that attackers may try to occupy all but one process to consistently land on the same one.
- A successful exploit doesn't necessarily crash the process. The attack might only generate two to four memory errors and then remain silent.
The order is always: patch first, architecture second.
Performance and Memory Consumption
Beyond the incident, the choice between mod_php or PHP-FPM also impacts your budget, as it dictates how much server capacity you require.
Prefork vs. Event
With mod_php, Apache runs in prefork mode: one process per connection, each with PHP loaded. Slow connections, keep-alive, and static files all occupy heavy processes.
With PHP-FPM, Apache can use the **event** MPM, which has been the default since Apache 2.4, as the PHP manual reminds us. Event handles many connections with few lightweight threads, and only PHP-specific requests go to the pool. The manual even refers to the threaded MPM as "recommended for best performance under load".
Where FPM Wins in Operation
- Restart PHP without restarting Apache. Applying a patch, changing
php.ini, or killing a stuck process only affects PHP. This is highly beneficial in a week like this, where PHP requests all workers be restarted after the patch. - Per-site limits. A site with a problematic plugin won't consume all server processes, as its pool has its own ceiling via
pm.max_children. - Diagnostics.
request_slowlog_timeoutlogs the backtrace of slow requests, andpm.status_pathshows in real-time how many processes are occupied. - Multiple PHP versions side-by-side. One pool on 8.3 and another on 8.4 on the same server, something mod_php cannot do.
Where mod_php Still 'Wins'
To be fair: in the mod_php or PHP-FPM balance, mod_php has fewer moving parts. There's no socket, no second service to monitor, and .htaccess with php_value works directly. On a lab server with a single site and minimal traffic, the performance difference is small. However, after PHP2Shell, simplicity doesn't outweigh remaining in the highest risk scenario.
Comparison Table: mod_php or PHP-FPM
A summary of the mod_php or PHP-FPM comparison based on criteria that weigh heavily in the operation of a production server:
| Criterion | mod_php (Apache) | PHP-FPM |
|---|---|---|
| Where PHP Runs | Within each Apache process | Separate service, with pools |
| Apache MPM | Prefork (without ZTS) | Event (Apache 2.4 default) |
| PHP2Shell Risk, according to PHP | Highest: exploit before application code | Affected; in CVE-2026-109619 upload path, depends on application operation |
| Execution User | Single (Apache user) for all sites | One per pool, isolating sites |
| Restarting PHP | Restarts entire Apache | Restarts only FPM (or a pool) |
| Multiple PHP Versions | No | Yes, one pool per version |
| Per-site Process Limit | No | pm.max_children per pool |
| Per-directory Configuration | php_value in .htaccess |
.user.ini or php_admin_value in pool |
| PHP Manual Recommendation | Do not use for new installations | Recommended for modern deployments |
| Nginx Support | Does not exist | Standard |
How to Discover What Your Server Uses Today
Before deciding between mod_php or PHP-FPM for your specific case, confirm your current mode. Here are three ways, from quickest to most comprehensive:
- Via Apache. Run
apachectl -M | grep -i php. Ifphp_module(orphp7_module,php8_module) appears, mod_php is loaded. - Via MPM. Run
apachectl -V | grep -i mpm.preforkis a strong indicator of mod_php;eventindicates PHP is external to Apache. - Using a temporary
phpinfo(). The Server API field will show eitherApache 2.0 Handler(mod_php) orFPM/FastCGI. Delete the file immediately afterwards: an exposedphpinfo()reveals version and path information to anyone scanning the internet.
Take the opportunity to note down the exact PHP version. If it's below 8.5.12, 8.4.27, 8.3.36, or 8.2.35 (or your distribution's patched package), that's the urgent issue, not the architecture.
How to Migrate from mod_php to PHP-FPM Without Taking Your Site Down
If the answer to mod_php or PHP-FPM for your server is to migrate, the roadmap below applies to Debian and Ubuntu with Apache 2.4, which is the most common scenario we encounter. Replace 8.3 with your specific version.
Step 0: Patch Before Anything Else
Update PHP to your distribution's patched version and restart Apache. This immediately closes the PHP2Shell window for mod_php. The migration comes later, during a planned window. If you haven't done this yet, this is the only step that matters today. The mod_php or PHP-FPM question can wait a few days; the patch cannot. In the post on how to protect your web server against attacks, I explain the WAF and rate limit layers that help in the interim, without replacing the fix.
Step 1: Install FPM and Create the Pool
Install php8.3-fpm with the same extensions that mod_php uses (compare php -m before and after). Create one pool per site in /etc/php/8.3/fpm/pool.d/, with its own user, socket, and limits:
- Site's
userandgroup, notwww-data listen = /run/php/site1.sockpm = dynamicandpm.max_childrencalculated based on memory (next step)pm.max_requests = 500, to recycle processes periodicallyrequest_terminate_timeoutconsistent withmax_execution_timephp_admin_value[session.upload_progress.enabled] = 0, if the application does not use an upload progress bar
This last item comes from the official advisory: it blocks the confirmed session paths for CVE-2026-109619, but not other variants. PHP advises applying this in php.ini, the FPM pool, or the server configuration, and to not rely on ini_set() or .user.ini.
Step 2: Size the Pool
The basic calculation is pm.max_children = available memory for PHP ÷ average memory per process. Measure actual consumption with ps during peak hours; do not use memory_limit as an estimate. A pool that's too small generates queues and 503 errors; one that's too large will cause the server to use swap.
If the server runs Moodle, the calculation changes significantly between normal hours and exam week. Reference figures are available in the Moodle 5.x server requirements guide.
Step 3: Change Apache Mode
On Debian/Ubuntu, the typical sequence is:
a2enmod proxy_fcgi setenvifa2enconf php8.3-fpm(or aSetHandler "proxy:unix:/run/php/site1.sock|fcgi://localhost"in the vhost, for per-site pools)a2dismod php8.3a2dismod mpm_preforkanda2enmod mpm_eventapachectl configtestand, if all is well,systemctl restart apache2
The switch will drop connections for a few seconds. Perform this during low-traffic hours and have the rollback path noted: the same commands, reversed.
Step 4: Pitfalls That Break Your Site
php_valueandphp_flagin.htaccess. These directives only exist in mod_php. With FPM, Apache returns a 500 error. Move these values to the pool (php_admin_value) or to.user.ini.- File permissions. With the new pool user, upload and cache folders that belonged to
www-datawill no longer be writable. Adjust ownership before the switch. - Environment variables and HTTP authentication. Some applications read authorisation headers in a way that changes with FastCGI. Test login and API integrations.
- Timeouts. Apache's
ProxyTimeoutmust be greater than the duration of long-running tasks, such as reports and imports.
Step 5: Validate and Monitor
Confirm in the temporary phpinfo() that the Server API has changed to FPM, run critical workflows (login, checkout, upload, cron), and monitor the slowlog and the pool status page for the first 48 hours. Only consider the mod_php or PHP-FPM switch complete after this alert-free period.
When Not to Migrate Now
In the mod_php or PHP-FPM decision, the long-term answer is FPM, but there are times when migrating now would be a mistake.
- When PHP has not yet been updated. Migrating a vulnerable PHP to FPM exchanges the highest risk scenario for one that is still vulnerable. Patch first.
- When the server runs PHP 7.3 or older. For versions 5.4 to 7.3, there is no project fix, and for 7.4 to 8.1, only minimal source code backports exist, according to Tenable. Here, the project is to update PHP and the application. CISA recommends taking PHP systems offline that cannot be updated or that run 8.1 or earlier.
- When the server is managed by a control panel. cPanel, Plesk, and HestiaCP have their own methods for changing PHP mode per domain. Manual Apache changes might be undone in the next panel update. Use the panel's option.
- During a critical period. Enrolment week, a sales campaign, or an exam period in your VLE is not the time to change architecture. Update PHP, apply official mitigations, and schedule the migration.
And a point about mitigations: memory_limit=-1, session.upload_progress.enabled=0, and, in PHP 8.5, fatal_error_backtraces=0 reduce specific attack paths. PHP explicitly states that these do not make the installation secure and that updating is the only secure choice. On 23/10, a third alert was released, CVE-2026-115560, linked to session upload progress. The vector is unlikely: it requires the application to change session.serialize_handler at runtime. The fix will come in the next regular version, and the recommendation is to keep session.upload_progress.enabled=0 until then.
How Agathas Web Addresses This
I've administered Linux servers with PHP since 2008: Moodle for educational institutions, WordPress, Laravel, and systems that communicate with the official WhatsApp API. For me, the mod_php or PHP-FPM question has had a standard answer for years: FPM, one pool per application, each with its own user. PHP2Shell has only clarified why this matters.
With Agathas Web's managed hosting, what's included for a scenario like this:
- PHP runtime updates during a pre-agreed window, with prior staging tests, as part of our routine operating system, PHP, and database patching.
- Revised architecture: diagnosis of the current mode (mod_php, FPM, control panel), migration plan to PHP-FPM with isolated pools,
pm.max_childrensizing based on real consumption, and rollback plan. - Protection layers already included in the service: Cloudflare WAF, fail2ban, and operating system hardening, to reduce exposure while a patch is applied.
- 24/7 monitoring with alerts to our technical team's WhatsApp, to detect signs like bursts of memory errors and PHP processes opening a shell.
- Incident response with a documented plan, and verified backups with tested restoration.
How it works: you provide us with server access (or we start with a migration to new infrastructure), we inventory PHP versions and modes, present the plan with a window and rollback strategy, and execute it with your written approval. Migration from your current provider is included in the setup. To enquire, speak to our team via the managed hosting page and tell us how many sites and which applications run on your server.
Conclusion: mod_php or PHP-FPM, the Answer is Now Simple
For internet-facing servers, the answer to mod_php or PHP-FPM is PHP-FPM, with an isolated pool per site. The PHP manual already discouraged mod_php for new installations due to performance and operational concerns, and PHP2Shell has added a security argument: it's the scenario the PHP team itself identifies as the highest risk.
Just don't reverse the order. First, update PHP to 8.5.12, 8.4.27, 8.3.36, 8.2.35, or your distribution's patched package, with all workers restarted. Then, the planned migration, with testing and a rollback path. And remember that PHP 8.2 reaches end-of-life on 31/12/2026: if your server is on it, it's worth combining the FPM migration with a version upgrade.
If you don't have a sysadmin to handle this now, Agathas Web's managed hosting can take on the patching, migration, and monitoring.
Sources: PHP security advisory and news archive, PHP manual on Apache, PHP-FPM configuration, Tenable, Unit 42, Datadog Security Labs, Huntress, watchTowr and Help Net Security.
Related posts

PHP2Shell Vulnerability: Essential Mitigation for UK Servers and Why It's Not Enough
The official three adjustments buy time, not security. Discover where to apply them, how to restart each PHP worker, and what to do until the full patch arrives.

PHP Docker Vulnerability: Rebuild Your Images Post-PHP2Shell
docker pull doesn't correct PHP2Shell. See how to rebuild php:8.x and FrankenPHP images and what to change in CI/CD for the patch to reach production.

Server Patch Management: Your 24/7 Post-PHP2Shell Strategy
From the 17/10 attack to the 21/10 patch: understanding the PHP2Shell exposure window and the part that's entirely within your control.