PHP2Shell mitigations when you cannot patch yet
Every source on PHP2Shell says the same thing first: the only complete fix is to update PHP. This guide is for the hours or days in between, when a server cannot be updated yet and must not simply be left as it is. It sets out the measures the PHP project published, what each one actually covers, and how to apply them so they take effect.
Start with the network, not php.ini
The most effective stop-gap is not a PHP setting. It is making the server unreachable to the people who would attack it. CERT.LV's guidance is explicit: systems that cannot be updated to a safe PHP version must have their access from the internet restricted, and should not be publicly reachable until the risk is removed. Latvia applied that principle to its own state services: on 21 October access to affected systems was limited to Latvian IP addresses while patches were rolled out, and the shared state and municipal website platform was switched off for an evening until it had been updated.
- Put administrative and internal PHP applications behind a VPN or an IP allow-list.
- Put public sites that cannot be closed behind a WAF or CDN that has published PHP2Shell rules, and make sure the origin accepts traffic only from that service. A rule in front does nothing if the origin's own IP address answers requests.
- Where a site is not business-critical, take it offline until it is patched. A maintenance page costs less than a rebuilt server.
How to apply PHP settings so they actually work
Several of the attack paths run while PHP is still parsing the request, before your application starts. A setting applied too late is no setting at all. The PHP advisory is specific about where the session setting must go: in php.ini, an FPM pool, or server configuration that applies before request parsing. Settings an application applies with ini_set(), and PHP-FPM .user.ini files, take effect too late.
- For PHP-FPM, set values in the pool file with
php_admin_valueorphp_admin_flag, which applications cannot override. - For Apache mod_php, use
php_admin_valueorphp_admin_flagin the server or virtual host configuration. - Restart every worker afterwards. A running FPM child or Apache process keeps its old configuration.
- Check the effective value with a
phpinfo()page served through the web server, then delete the page.
; PHP-FPM pool, e.g. /etc/php/8.3/fpm/pool.d/www.conf
php_admin_flag[session.upload_progress.enabled] = off
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 6M
# Apache with mod_php, server or virtual host configuration
php_admin_flag session.upload_progress.enabled off
php_admin_value post_max_size 8M
Against CVE-2026-109619: stale global state
| Measure | What it blocks | Cost |
|---|---|---|
session.upload_progress.enabled=0 | The confirmed session upload progress paths, which work against both mod_php and PHP-FPM and do not need the application to call session_start() | Upload progress bars stop working. Few applications use them |
fatal_error_backtraces=0 (PHP 8.5 only) | The confirmed fatal error backtrace path, the basis of the demonstrated mod_php exploit. The setting is on by default in 8.5 | Fatal errors are logged without a backtrace |
| Remove invalid or deprecated per-request settings | Early warnings that consume stale state before the next request is initialised, for example legacy assert.* settings on PHP 8.3 and later or legacy session settings on PHP 8.4 and later, passed per directory or by the FastCGI front end | Configuration clean-up |
| Fix fatal errors and timeouts an attacker can reach | Ways of ending a request abruptly, which is what leaves stale state behind | Application work, not a switch |
pm.max_requests=1 recycles each FPM worker after one request, which reduces but does not remove the chance that two requests meet in the same worker, at a real performance cost. It also suggests keeping session.upload_progress.enabled=0 after patching if you do not need the feature.Against CVE-2026-109618: out-of-memory corruption
This flaw needs PHP to run out of memory at the wrong moment. The measures either remove PHP's own memory-limit failure, or make it harder for one request to consume that much memory.
| Measure | What it does | Cost |
|---|---|---|
memory_limit=-1, set with php_admin_value | Removes the controllable PHP memory-limit failure. Check that no pool, directory or application restores a finite limit | A runaway request can now exhaust the machine. An underlying allocation failure can still reach the vulnerable path |
| A cgroup or container memory limit | Contains each worker once PHP's own limit is gone, preferably with a policy that kills only the affected worker. The PHP advisory notes it does not guarantee the allocation never fails, and killing a whole container also kills the master and every worker | Requests on a killed worker fail; in threaded setups other requests on that process fail too |
Low post_max_size | 8M where the application needs no uploads, 32M where it does, with upload_max_filesize below it | Larger legitimate uploads are rejected |
| Limits at the web server or proxy | Caps on decoded request size, input field count, multipart part count, header count and nesting depth, including for requests without a usable Content-Length | Needs testing against complex forms |
Lower max_input_nesting_level | Reduces the work deeply nested field names create | Complex forms may need a higher value |
memory_limit=-1 with a cgroup v2 memory.max limit is how the researchers and the PHP project frame it: PHP stops enforcing the limit itself and the operating system contains the damage.What these measures leave open
The PHP advisory for CVE-2026-109619 says there is no configuration-based mitigation that covers every operation, because an application, extension or server path can leave stale state behind or use it without a predictable log entry. The fixes in the released versions address the underlying failure pattern; the settings above address the paths that have been demonstrated. Treat a mitigated server as still vulnerable, with fewer known ways in.
After you patch
Do not simply revert everything. The PHP advisory for CVE-2026-109618 still recommends a conservative post_max_size, limits on decoded request size at the front end, and bounds on input variables, multipart parts and nesting depth, because they reduce the risk of the new hard memory limit being hit and of denial of service. Revisit any temporary memory_limit=-1 once the fixed release is running: the fix introduces a hard memory limit above the normal one that kills a worker outright, without shutdown functions or destructors.
Sources
- Security advisory: Critical remote code execution vulnerabilities, update now The full list of measures for each CVE and where each must be applied.
- GHSA-w8hw-w9fx-j79w: RCE via OOM corruption The memory-limit measures, the warning about RLIMIT_AS and RLIMIT_DATA, and the hardening to keep after patching.
- GHSA-6wmf-4rv5-p3rp: RCE via stale global state The session and backtrace measures, why hiding errors does not help, and the absence of a complete configuration mitigation.
- Saistībā ar globāli atklātām PHP ievainojamībām Latvijā ievieš preventīvus pasākumus Restricting internet access to systems that cannot be updated.
Questions
Related questions
Is setting session.upload_progress.enabled=0 enough?
Can I set these values in .htaccess or with ini_set()?
Will memory_limit=-1 take my server down?
Keep reading
More guides
-
Patching PHP2Shell: platform by platform
Where the fixed PHP packages are for Debian, Ubuntu, RHEL and its rebuilds, Fedora, SUSE, Alpine, Amazon Linux, Docker, control panels and managed hosts, and how to make sure the new binary is the one running.
Read the guide -
PHP2Shell on PHP 8.1, 8.0, 7.x and older
There is no official fixed release below PHP 8.2. What the PHP project provided for 7.4, 8.0 and 8.1, where distribution fixes come from, and what to do with anything older.
Read the guide -
Was PHP2Shell used against you? Detection and response
Where to look, what each signal means and does not mean, and what to do if a server shows signs of compromise, including preserving evidence before you rebuild.
Read the guide