If you can’t upgrade today

Temporary mitigations

These reduce exposure to specific known attack paths while you get the upgrade out. None of them is complete, and some have real costs. Test before rolling out.

Last reviewed 2026-10-22

There is no complete configuration fix

Both PHP advisories say so explicitly. Use these to buy hours or days, not weeks. Patch guide →

What each measure does

MeasureEffectCVECost / side effects

memory_limit = -1 (set administratively) + cgroup memory cap

Primary
The primary config mitigation in the advisory. Removes PHP’s own OOM abort, which most attack paths rely on.BothThe OS kills runaway workers instead, interrupting requests. Without a cgroup cap one request can exhaust the host.

session.upload_progress.enabled = 0

Partial
Blocks the confirmed session paths, “but not other variants” (php.net). Must be set in php.ini, FPM pool or server config — not .user.ini or ini_set().…619Breaks upload progress bars that rely on it (rare). Worth keeping off after patching if unused.

fatal_error_backtraces = 0 (PHP 8.5)

Partial
Blocks the confirmed fatal-backtrace path only — the strongest demonstrated CVE-2026-109619 case.…619Fatal errors no longer include a backtrace.

Lower post_max_size + front-end body limit

Partial
Less attacker-controlled memory pressure during multipart parsing, before your app runs. php.net suggests 8M without uploads, 32M with.…618Also caps uploads — test legitimate upload flows.

Bound input: max_input_nesting_level, max_input_vars, max_file_uploads, max_multipart_body_parts

Partial
Limits other parser-driven memory growth.…618Large or deeply nested forms may fail.

Remove attacker-reachable fatals and per-request invalid/deprecated settings

Partial
Removes producers of the stale state behind CVE-2026-109619: diagnostics emitted before initialisation, fatal errors and timeouts in callbacks, uploads, imports and cleanup.…619Code and config review effort.

Lower pm.max_requests (FPM)

Partial
Recycles workers so corrupted state survives fewer requests. 1 is the extreme setting the advisory mentions.…619Real CPU and latency cost; does nothing for single-request attacks.

display_errors = 0

Limits impact
Stops error output from leaking details to remote users. Note: hiding diagnostics does not stop the CVE-2026-109619 trigger.ImpactNone in production.

SELinux / AppArmor confinement of the PHP worker

Limits impact
Limits what injected code can execute or read.ImpactPolicy work; does not stop memory disclosure.

WAF / CDN managed rules

Partial
Blocks known payload shapes at the edge. Cloudflare covers Free plans too.BothAttackers are already mutating payloads; coverage varies by provider.

Do not do this

Don’t replace memory_limit with RLIMIT_AS / RLIMIT_DATA

That means ulimit -v / ulimit -d, systemd LimitAS= / LimitDATA=, or rlimit_* tricks. They make mmap() fail inside PHP, which triggers the same vulnerable OOM path. Use a cgroup limit (MemoryMax=, container --memory) instead.

Copy-paste configuration

php.ini drop-in
; /etc/php/8.3/fpm/conf.d/99-php2shell.ini  (path varies)
; TEMPORARY PHP2Shell mitigations — revisit after upgrading.
; Tune every number to what your application really needs.

session.upload_progress.enabled = 0
fatal_error_backtraces = 0      ; PHP 8.5 only, ignored elsewhere
display_errors = 0

post_max_size = 8M              ; php.net: 8M without uploads, 32M with
upload_max_filesize = 6M        ; keep below post_max_size
max_input_nesting_level = 32    ; default 64
max_input_vars = 1000
max_file_uploads = 5            ; default 20
max_multipart_body_parts = 200
php-fpm pool
; /etc/php/8.3/fpm/pool.d/www.conf  (one block per pool)
; php_admin_* cannot be overridden by ini_set() in application code.
; WordPress and others call ini_set('memory_limit', ...) — a plain
; php.ini value would be silently replaced with a finite limit again.
php_admin_value[memory_limit] = -1
php_admin_flag[session.upload_progress.enabled] = off

; Recycle workers more often (performance cost — measure it)
pm.max_requests = 50
apache / nginx
# Apache + mod_php (VirtualHost or global config)
php_admin_value memory_limit -1
php_admin_flag session.upload_progress.enabled off
# 32 MiB request body cap (match post_max_size)
LimitRequestBody 33554432

# nginx in front of PHP-FPM
client_max_body_size 32m;
cgroup memory cap
# Cap total PHP-FPM memory with cgroup v2 (systemd's MemoryMax = memory.max)
sudo systemctl edit php8.3-fpm
#   [Service]
#   MemoryHigh=3G
#   MemoryMax=4G
sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryMax

# Containers: set a memory limit on the container instead
docker run --memory=4g ...

Restart PHP-FPM / Apache after any change. Confirm the effective value with php-fpm8.3 -i | grep -E 'memory_limit|upload_progress' — keeping in mind that -i reads php.ini but not pool overrides, so also check phpinfo() from the site.

After you upgrade

  • Put memory_limit back to a finite value that fits your app. The patched PHP adds a hard limit above it.
  • Keep post_max_size comfortably below the memory budget, and max_input_nesting_level at what you actually need.
  • Keep session.upload_progress.enabled = 0 if you do not use it. Less surface, no cost.
  • Return pm.max_requests to your normal value.
  • Optional: expose_php = 0 hides your version from scanners. It is hygiene, not a fix.