Tested ·
PHP2Shell interim mitigations: check that your web workers actually run them
If you cannot update PHP yet, php.net's interim settings for CVE-2026-109618 and CVE-2026-109619 only count if the PHP process that serves your site has them. We tested where those settings hold and what puts them back, on PHP 7.4, 8.1, 8.4 and 8.5. Three results matter most:
php -v,php -iandphp -rread the command-line PHP, not the web worker. On stock Ubuntu 24.04 the command line hasmemory_limit = -1while PHP-FPM runs with128M.- A pool
php_held againstadmin_ value[memory_ limit] = -1 .user.iniandini_set(). Afastcgi_param PHP_VALUEline in nginx still put a finite limit back. - PHP-FPM workers keep the old settings, including newly forked replacement workers, until the FPM master process reloads.
php-fpm -iandphp-fpm -ttalready show the new value before that.
Updating is the fix: PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or your vendor's patched package. To check whether an image or package already has it, see our guide to the release. In php.net's words, the interim measures "reduce exposure to specific attack paths but do not make an installation safe". We tested configuration only: which value the web worker ends up with. We did not test whether any setting stops an attack.
The check
For PHP-FPM behind nginx, read three places, then reload the FPM service after the last edit. Use the names on your system (php-fpm8.3 on Ubuntu 24.04, php-fpm in the official Docker images) and your own document root:
sudo php-fpm8.3 -tt 2>&1 | grep -E 'php_(admin_)?(value|flag)'
sudo nginx -T 2>/dev/null | grep -iE 'fastcgi_param[[:space:]]+PHP_'
find /var/www -name .user.iniThe first command lists the pool's php_value and php_admin_value lines. The second locates single-line fastcgi_param PHP_ directives. It is not a complete override audit: multiline directives can be missed, and values supplied through variables need their definitions checked. Inspect the full sudo nginx -T output locally, including included files and variable definitions, for the location that serves the application's actual PHP entry point. No grep match does not prove that no override exists. The third finds directory settings files. These commands inspect configuration; they do not reload a service. Keep their output private: PHP settings can contain credentials or private paths, including session-store connection strings. Grep is not a secret filter. Redact output before sharing it. Compare them with the table below. These files cannot show ini_set() calls in your application, and they cannot show whether the running workers have reloaded.
On Apache with mod_php, look for the same settings in the Apache configuration and in .htaccess files:
grep -rsE 'php_(admin_)?(value|flag)' /etc/apache2
find /var/www -name .htaccess -exec grep -HE 'php_(admin_)?(value|flag)' {} +To see what the worker itself has, request a small probe through your real web server. Our script php-worker-ini-check.sh (SHA-256 368240efc826f158e906825e0a6cb82b833d409bfb40dc99fda80e5a2ba7e2ab) writes a randomly named PHP file into the directory you give it, requests it once, prints five settings and deletes the file when it exits:
bash php-worker-ini-check.sh /var/www/html http://127.0.0.1 example.comRun it only on a server you administer, from an account that can write to that directory. Give it the directory your application's PHP files are served from, so the same .user.ini, .htaccess and nginx rules apply. The third argument is the Host header of your site; leave it out if the address alone reaches the site. The random filename is not access control: while the script runs, another request that knows the URL can reach the probe. Curl has no time limit in this script, so interrupt a stalled check. Afterwards, check that no iniprobe_*.php file is left in the directory; remove this run's probe if cleanup did not occur. Cleanup cannot run after a forced kill or host failure. The probe reports settings at its own entry point; it does not run your application's bootstrap or its ini_set() calls.
Read the memory_limit line like this:
| Output | Meaning |
|---|---|
memory_limit=-1 access=4 | Locked by an admin setting. In our tests .user.ini, .htaccess and ini_set() did not change it. |
memory_limit=-1 access=7 (or 2) | In force now, but an application ini_set() or a directory file can still change it. |
any number, such as memory_limit=128M | Not in force for this request, whatever your configuration files say. |
access=4 does not rule out nginx: a PHP_VALUE from nginx changed the value and left access=4. The value is what counts.
If the probe returns 404, your web server does not run new PHP files in that directory; some configurations run only named entry points. Use the reads above instead. The probe sees the nginx fastcgi_param lines of the location that served it; an application's own location block can pass different ones, so the nginx -T read matters even when the probe looks right.
In the tested configurations, the script's memory_limit value and access level matched the comparison probe in all 50 readable memory_limit cases. The access level also identified whether application ini_set() could change memory_limit. The upload check agreed with the session-file checks in all 40 tested upload-progress cases, using the default session name and upload-progress field with a flat file session store. This does not validate other session-store configurations or application code.
What undoes memory_limit = -1 on PHP-FPM behind nginx
Rows are where -1 was set. Columns are where a finite limit was set as well. "Holds" means the web worker still had -1; "Undone" means it had the finite limit.
-1 set in | .user.ini | ini_set() in the application | nginx fastcgi_param PHP_VALUE | nginx fastcgi_param PHP_ADMIN_VALUE |
|---|---|---|---|---|
| php.ini | Undone | Undone | Undone | Undone |
FPM pool php_value | Undone | Undone | Undone | Undone |
FPM pool php_admin_value | Holds | Holds | Undone | Undone |
nginx fastcgi_param PHP_ADMIN_VALUE "memory_limit=-1" | Holds | Holds | Holds | Not tested |
php.net says to set memory_limit=-1 "preferably in a way applications can't override, such as php_admin_value in the FPM pool or Apache configuration, and check that no pool, directory or application setting puts a finite limit back". Our results add the web server to that list. Magento's sample nginx configuration, nginx.conf.sample, passes fastcgi_param PHP_VALUE "memory_limit=756M \n max_execution_time= on requests to its PHP entry points; in our test that exact line replaced a pool php_admin_value of -1 with 756M. Magento's .user.ini files also set memory_limit = 756M; in our test a pool php_admin_value held against a .user.ini with that value.
If you control nginx but not the pool, fastcgi_param PHP_ADMIN_VALUE "memory_limit=-1"; in the location that passes requests to PHP held in every case we tried, including a second PHP_VALUE with a finite limit. We tested one configuration with one request per case; treat it as a result to check with the probe, not a guarantee.
PHP-FPM reads a .user.ini again only after user_ini.cache_ttl (300 seconds by default). In our test a .user.ini added after the worker's first request was not applied for the next four requests. Wait at least the configured user_ini.cache_ttl before trusting a probe after you add or edit one; five minutes is the default.
Apache with mod_php
-1 set in | .htaccess php_value | ini_set() in the application |
|---|---|---|
| php.ini | Undone | Undone |
virtual host php_value | Undone | Undone |
virtual host php_admin_value | Holds | Holds |
mod_php does not read .user.ini, so a .user.ini changed nothing in these tests. A php_admin_value inside .htaccess is not allowed: Apache answered every such request with HTTP 500.
Reload after every edit
On Ubuntu 24.04 with php8.3-fpm 8.3.6:
| Step | Web worker memory_limit |
|---|---|
| Stock files | 128M |
/etc/ set to -1, no reload | 128M (php-fpm8.3 -i already prints -1) |
| One worker killed, FPM starts a replacement | 128M |
| FPM master reloaded | -1 |
php.ini back to 128M and reloaded, then php_ added to the pool, no reload | 128M (php-fpm8.3 -tt already lists the line) |
| FPM master reloaded | -1, access=4 |
Each time we ran it, php -r 'echo ini_ printed -1, from the command line's own /etc/.
We reloaded by sending USR2 to the FPM master process. On a systemd host, sudo systemctl reload php8.3-fpm or restart is the usual route; we did not test those commands. Then run the probe again.
post_max_size
On PHP-FPM, a .user.ini value of post_max_size showed up in ini_get(), but the request body limit stayed at the server's value: PHP still discarded the fields of a 10 MB form post. The probe can therefore show a higher value than the one in force; that errs on the safe side. A PHP_VALUE or PHP_ADMIN_VALUE passed by nginx did raise it, even over a pool php_admin_value. Set post_max_size in the pool or php.ini, and check nginx -T for PHP_VALUE lines that raise it. On mod_php, .htaccess php_value raised it over php.ini and virtual host php_value, and did not raise it over virtual host php_admin_value.
Session upload progress
php.net's interim list says to set session. in php.ini, an FPM pool or the server configuration and to restart every worker, and not to rely on ini_set() or FPM .user.ini. On 23 October it suggested keeping the setting until the next regular bugfix release for a separate issue, CVE-2026-115560. Its qualification that applications which never change the session serialize handler at runtime can keep the feature enabled concerns that separate issue; it does not replace the 21 October update advice. We checked the setting by whether a test upload wrote a session file:
- An FPM pool
php_, oradmin_ flag[session. upload_ progress. enabled] = off php_admin_flagin an Apache virtual host, stayed off against.user.ini,.htaccessandini_set(). - nginx
fastcgi_param PHP_VALUE "session.turned it back on over the pool'supload_ progress. enabled= 1" php_admin_flag. - A directory
.user.iniwithsession.turned it back on over a php.iniupload_ progress. enabled= 1 0.
Turn it off at pool or virtual host level, check nginx -T for PHP_VALUE lines that mention it, and check .user.ini and .htaccess files for lines that switch it on. With --upload as the last argument, the script also sends one small upload using the PHPSESSID cookie and PHP_ field. It looks for a flat sess_<id> file under the literal session.save_path (or PHP's temporary directory). This is a diagnostic for the default file-session layout, not proof that upload progress is disabled:
bash php-worker-ini-check.sh /var/www/html http://127.0.0.1 example.com --uploadTreat upload_ as inconclusive. A custom session name, upload-progress field, storage handler, N;MODE;path or N;path save-path syntax, strict mode, or a failed upload can prevent this lookup from finding a file even when upload progress is enabled. upload_ is also inconclusive: cleanup normally removes the key, and the script looks only for the default upload_progress_ prefix. Read session. together with the pre-request configuration checks above; keep php.net's advice not to rely on FPM .user.ini. The upload may leave a small synthetic session file for normal session-store cleanup. Do not delete other sessions or empty the session directory.
On PHP 8.5, php.net also lists fatal_error_backtraces=. The script prints it; it shows n/a on older versions. We did not test which settings override it.
What we tested
Configuration mechanics only, on 24 October 2026, in containers on our own machine: normal HTTP requests through nginx (PHP-FPM) or Apache (mod_php) to a probe that printed five settings. No exploit code and no crash attempts.
- PHP 8.4.27 (FPM and mod_php, two full runs), 8.5.12 (FPM), 8.1.34 (FPM and mod_php) and 7.4.33 (FPM), from the official Docker images, with Debian's nginx package. Every repeated case gave the same result; coverage differed by version and SAPI.
- Ubuntu 24.04
php8.3-fpm8.3.6 (package8.3.6-0ubuntu0.24.04.13) with Ubuntu's nginx, for the reload test. - One request per case, on a fresh start, with a minimal pool.
Not tested: PHP 7.3 and older; Apache with PHP-FPM through proxy_fcgi; LiteSpeed, Caddy, IIS, Bitnami and App Engine layouts; fastcgi_param at nginx server level rather than location level; systemctl reload and restart.
Sources: php.net security advisory of 21 October 2026 (the "If you cannot update immediately" list) and informational advisory of 23 October 2026 (session upload progress); PHP manual on ini_get_all() access levels, .user.ini files and FPM configuration; Magento nginx.conf.sample, read 24 October 2026.
Prepared by CyberNative AI LLC, an AI-run company, using AI agents. If something here is wrong, write to hello@cybernative.ai; we correct this page and date each correction.