This is not a fix
The PHP project says there is no complete mitigation for CVE-2026-109618 or CVE-2026-109619. Upgrading to 8.5.12, 8.4.27, 8.3.36 or 8.2.35 is the only safe option. The steps below reduce exposure while you arrange that upgrade. A server with every setting on this page can still be vulnerable.
Handle the two CVEs one at a time, because each has its own settings and its own trade-offs. Every setting below goes in php.ini, the FPM pool file or the web server config. Do not put them in ini_set(), .user.ini or .htaccess: application code and users can change those, and some of these settings are not read from there at all.
CVE-2026-109619: stale global state
Turn off upload progress
The advisory's main setting for this CVE is session.upload_progress.enabled=0. It blocks the early session path the demonstrated attack used. It does not cover the other lifecycle paths the advisory describes.
In php.ini:
session.upload_progress.enabled = 0
In an FPM pool file (for example /etc/php/8.3/fpm/pool.d/www.conf), locked so scripts cannot change it:
php_admin_flag[session.upload_progress.enabled] = off
In Apache with mod_php, in the server or virtual host config (not .htaccess, where php_admin_flag is refused):
php_admin_flag session.upload_progress.enabled off
Then restart every worker: PHP-FPM, Apache with mod_php, LiteSpeed. The restart commands on the after-patching page cover each one.
Keep this setting off after you patch if you do not use upload progress bars. The advisory calls the feature hardly used and hardly tested.
PHP 8.5: disable fatal error backtraces
On PHP 8.5 only, add:
fatal_error_backtraces = 0
The advisory says this removes the producer used by the confirmed attack case. It does not cover other request-owned global or extension state. Restart every worker after the change.
Optional: recycle every worker after one request
pm.max_requests = 1
Set this as a plain FPM pool directive, without php_admin_value. Each child handles one request and exits, so state cannot carry over to the next request. The advisory suggests it only where performance does not matter: PHP-FPM then forks a process for every request, and busy sites slow down badly. It also notes that recycling lowers the attacker's odds without removing the condition.
Audit with logs, and know what does not help
During an isolated audit, turn on logging in startup config:
log_errors = On
error_log = /var/log/php/error.log
error_reporting = E_ALL
In the PHP, web server and proxy logs, look for warnings or deprecations that repeat before scripts run, diagnostics during cleanup, fatal errors, and Maximum execution time exceeded. A clean log is not proof of safety.
Hiding diagnostics does nothing here. The advisory says changing error_reporting, using @, or turning off display_errors or log_errors does not protect against this flaw, because the global state is touched before PHP decides whether to report anything.
Two more risk reducers from the advisory: remove fatal errors and timeouts from paths an attacker can reach (upload, import, callbacks, shutdown handlers), and drop invalid or deprecated per-request INI overrides that produce warnings before initialization.
CVE-2026-109618: out-of-memory corruption
memory_limit=-1, and why it sounds wrong
The advisory's main configuration workaround is the opposite of normal hardening:
memory_limit = -1
Locked in an FPM pool:
php_admin_value[memory_limit] = -1
Or for mod_php:
php_admin_value memory_limit -1
The reasoning is that the confirmed attack relies on hitting a predictable per-request memory limit. With no limit, that trigger is gone. The advisory still calls it "not a complete mitigation", because PHP can reach the same bad path if the operating system fails an allocation.
This has a cost. A runaway script, a bug or a large legitimate request can now use all memory on the host. Compensate at other layers: the front-end size limits below, and a per-worker memory ceiling (see the cgroup note). Restart all workers, then check that no pool, directory or application setting puts a finite limit back. Once you have patched, return to a sane memory_limit.
Shrink request size
post_max_size = 8M
upload_max_filesize = 2M
Use 8M if the site accepts no uploads and 32M if it does, and keep upload_max_filesize smaller than post_max_size. The 2M above is an example: set it to the largest file your users really send. Also lower max_input_nesting_level (default 64) as far as your forms and APIs tolerate, and consider max_input_vars and max_file_uploads.
Limit requests at the front end
PHP's own limits fall short here. The advisory says to enforce the decoded request size at the front end, and to cap field count, multipart parts, header count and nesting, including for requests that arrive without a Content-Length header (chunked bodies). If your application uses no multipart forms, block multipart/form-data before it reaches PHP.
nginx:
server {
client_max_body_size 8m;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
}
nginx has no setting for field count or multipart parts. Use a WAF or an application gateway for those. Test on staging that oversized chunked bodies get a 413.
Apache:
LimitRequestBody 8388608
LimitRequestFields 50
LimitRequestFieldSize 8190
Apache counts chunked bodies as it reads them and rejects them once the total passes the limit. LimitRequestFields defaults to 100.
Container and cgroup limits
A memory limit on the container or cgroup can stand in for memory_limit, but the advisory says it does not guarantee an allocation failure never happens. It only helps if the kill hits one worker. If the whole container or cgroup is killed, the FPM master goes with it and the site is down until something restarts it.
Do not use RLIMIT_AS or RLIMIT_DATA
The advisory is explicit: do not use RLIMIT_AS or RLIMIT_DATA, whether through ulimit -v, systemd LimitAS= or LimitDATA=, or a container runtime. Hitting those limits makes mmap() fail, and that failure is what triggers the vulnerable path.
Verify the settings that are actually running
The file you edited may not be what PHP loaded. Per-pool overrides, included files and a separate php.ini per SAPI are common.
Check the FPM configuration PHP-FPM parsed (the binary is php-fpm8.3 on Debian-style packages):
sudo php-fpm -tt 2>&1 | grep -E 'upload_progress|memory_limit|pm.max_requests|fatal_error'
On the command line, php -i shows the CLI configuration, which usually comes from a different file. Ask the web SAPI instead. Put this file behind an IP allowlist or basic auth, request it once, then delete it:
<?php
header('Content-Type: text/plain');
echo PHP_VERSION, ' ', PHP_SAPI, "\n";
foreach (['memory_limit','post_max_size','upload_max_filesize','max_input_nesting_level','session.upload_progress.enabled','fatal_error_backtraces'] as $k) {
echo $k, ' = ', var_export(ini_get($k), true), "\n";
}
phpinfo() shows the same plus a lot more, including paths and environment, so keep it off any public URL and remove it right away. fatal_error_backtraces prints false on PHP below 8.5, where the setting does not exist.
Plan the real upgrade
The measures above are a stopgap for a few days. Decide where each server is going:
- Pick the target. We recommend PHP 8.4, or 8.3. Do not move to 8.2: it reaches end of life on 31 December 2026.
- For PHP 7.4, 8.0 and 8.1, upstream published source backports and no releases. Ask your distribution or long-term support vendor for their patched package.
- Follow the general fix page for your distribution or panel.
- After the upgrade, restart and confirm with after patching, then decide which of these settings to keep.
session.upload_progress.enabled=0stays.memory_limit=-1andpm.max_requests=1go.
Last reviewed on 22 October 2026.