<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: EveryHost</title>
    <description>The latest articles on DEV Community by EveryHost (@everyhostuk).</description>
    <link>https://dev.to/everyhostuk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4232767%2F040661a9-b394-443b-ab6c-8954f3c7562e.png</url>
      <title>DEV Community: EveryHost</title>
      <link>https://dev.to/everyhostuk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/everyhostuk"/>
    <language>en</language>
    <item>
      <title>Magento's nginx sample overrides php_admin_value, and that breaks the PHP2Shell memory_limit stopgap</title>
      <dc:creator>EveryHost</dc:creator>
      <pubDate>Sun, 08 Nov 2026 08:30:56 +0000</pubDate>
      <link>https://dev.to/everyhostuk/magentos-nginx-sample-overrides-phpadminvalue-and-that-breaks-the-php2shell-memorylimit-stopgap-icn</link>
      <guid>https://dev.to/everyhostuk/magentos-nginx-sample-overrides-phpadminvalue-and-that-breaks-the-php2shell-memorylimit-stopgap-icn</guid>
      <description>&lt;p&gt;PHP's main stopgap for the first PHP2Shell bug (CVE-2026-109618) is to remove the memory limit by setting &lt;code&gt;memory_limit=-1&lt;/code&gt;, somewhere applications can't change it. The usual advice is &lt;code&gt;php_admin_value&lt;/code&gt; in the FPM pool.&lt;/p&gt;

&lt;p&gt;On a Magento store using the stock nginx config, that doesn't work. The value comes back as 756M, and &lt;code&gt;ini_set()&lt;/code&gt; failing makes it look locked in.&lt;/p&gt;

&lt;p&gt;The real fix is updating PHP. PHP's words: "There are no complete mitigations, updating is the only safe choice." The fixed releases are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. But if you're waiting on a vendor backport and using this stopgap in the meantime, check it's actually applied.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Magento sets memory_limit
&lt;/h2&gt;

&lt;p&gt;Magento 2.4.9 sets it to 756M in five files:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Line&lt;/th&gt;
&lt;th&gt;Applies when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.user.ini&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PHP-FPM or CGI, if the document root is the Magento root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pub/.user.ini&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PHP-FPM or CGI, if the document root is &lt;code&gt;pub&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pub/.htaccess&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;45, 69&lt;/td&gt;
&lt;td&gt;Apache with mod_php only (inside &lt;code&gt;IfModule&lt;/code&gt; guards)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.htaccess.sample&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;Apache with mod_php, if you use the sample&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nginx.conf.sample&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;58, 218&lt;/td&gt;
&lt;td&gt;nginx, passed as &lt;code&gt;fastcgi_param PHP_VALUE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Line 218 is the one that matters. It sits in the location block for &lt;code&gt;index.php&lt;/code&gt; and the other main entry points:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;fastcgi_param&lt;/span&gt;  &lt;span class="s"&gt;PHP_VALUE&lt;/span&gt; &lt;span class="s"&gt;"memory_limit=756M&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;n&lt;/span&gt; &lt;span class="s"&gt;max_execution_time=18000"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What we tested
&lt;/h2&gt;

&lt;p&gt;We ran a throwaway PHP-FPM 8.4.13 pool on one worker, bound to localhost, with a one-line script printing &lt;code&gt;ini_get('memory_limit')&lt;/code&gt;. A small FastCGI client sent the same parameters nginx would, including Magento's exact &lt;code&gt;PHP_VALUE&lt;/code&gt; string. nginx itself wasn't in the loop.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pool / php.ini&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;.user.ini&lt;/code&gt; 756M&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;PHP_VALUE&lt;/code&gt; from nginx&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;php.ini &lt;code&gt;-1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;php.ini &lt;code&gt;-1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;&lt;code&gt;756M&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pool &lt;code&gt;php_value -1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;&lt;code&gt;756M&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pool &lt;code&gt;php_admin_value -1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pool &lt;code&gt;php_admin_value -1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;Magento's&lt;/td&gt;
&lt;td&gt;&lt;code&gt;756M&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pool &lt;code&gt;php_admin_value -1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;Magento's&lt;/td&gt;
&lt;td&gt;&lt;code&gt;756M&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;php.ini &lt;code&gt;-1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;memory_limit=2G&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;756M&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PHP_VALUE&lt;/code&gt; 756M plus &lt;code&gt;PHP_ADMIN_VALUE -1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pool &lt;code&gt;php_admin_value -1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PHP_VALUE&lt;/code&gt; 756M plus &lt;code&gt;PHP_ADMIN_VALUE -1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So the order of who wins looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;PHP_ADMIN_VALUE&lt;/code&gt; from the web server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.user.ini&lt;/code&gt;, unless an admin value has locked the setting&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PHP_VALUE&lt;/code&gt; from the web server, which also replaces a pool &lt;code&gt;php_admin_value&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Pool &lt;code&gt;php_value&lt;/code&gt; and &lt;code&gt;php.ini&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Rows 5 and 6 are the surprise. &lt;code&gt;.user.ini&lt;/code&gt; can't touch a pool &lt;code&gt;php_admin_value&lt;/code&gt;, but &lt;code&gt;PHP_VALUE&lt;/code&gt; can.&lt;/p&gt;

&lt;p&gt;In those two cases &lt;code&gt;ini_set('memory_limit', '512M')&lt;/code&gt; also returned &lt;code&gt;false&lt;/code&gt;. The setting is locked, just at the wrong value. If you tested the lock with &lt;code&gt;ini_set()&lt;/code&gt; you'd think you were fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it happens
&lt;/h2&gt;

&lt;p&gt;It's in &lt;code&gt;sapi/fpm/fpm&lt;/code&gt;. &lt;code&gt;fpm_main.c&lt;/code&gt; reads &lt;code&gt;PHP_VALUE&lt;/code&gt; with mode &lt;code&gt;ZEND_INI_USER&lt;/code&gt; and then &lt;code&gt;PHP_ADMIN_VALUE&lt;/code&gt; with mode &lt;code&gt;ZEND_INI_SYSTEM&lt;/code&gt;. Both go through &lt;code&gt;fpm_php_zend_ini_alter_master()&lt;/code&gt; in &lt;code&gt;fpm_php.c&lt;/code&gt;, which writes the new value without checking whether the setting is already locked. It only tightens the lock when the mode is &lt;code&gt;ZEND_INI_SYSTEM&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So a later &lt;code&gt;PHP_VALUE&lt;/code&gt; overwrites the value and leaves the lock in place. The PHP manual promises that &lt;code&gt;php_admin_value&lt;/code&gt; can't be overridden by &lt;code&gt;.htaccess&lt;/code&gt; or &lt;code&gt;ini_set()&lt;/code&gt;. It doesn't say anything about FastCGI parameters, so we wouldn't call this a bug, but it's easy to miss.&lt;/p&gt;

&lt;p&gt;We only ran this on 8.4.13, which predates the fix. The same order and the same function are in the current PHP-8.2 to PHP-8.5 branches of php-src, so we'd expect the same result, but please test your own build.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to fix it
&lt;/h2&gt;

&lt;p&gt;Either option works. Option 1 is row 4 of the table and option 2 is the last two rows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: take memory_limit out of PHP_VALUE.&lt;/strong&gt; Keep the pool setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;fastcgi_param&lt;/span&gt;  &lt;span class="s"&gt;PHP_VALUE&lt;/span&gt; &lt;span class="s"&gt;"max_execution_time=18000"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="na"&gt;php_admin_value&lt;/span&gt;&lt;span class="nn"&gt;[memory_limit]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;-1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2: add PHP_ADMIN_VALUE next to the existing line:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;fastcgi_param&lt;/span&gt;  &lt;span class="s"&gt;PHP_VALUE&lt;/span&gt; &lt;span class="s"&gt;"memory_limit=756M&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;n&lt;/span&gt; &lt;span class="s"&gt;max_execution_time=18000"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;fastcgi_param&lt;/span&gt;  &lt;span class="s"&gt;PHP_ADMIN_VALUE&lt;/span&gt; &lt;span class="s"&gt;"memory_limit=-1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put it in the same location block, not at server level. nginx only inherits &lt;code&gt;fastcgi_param&lt;/code&gt; from the level above when the current level defines none, and this block defines several.&lt;/p&gt;

&lt;p&gt;Reload nginx, and restart FPM if you changed the pool.&lt;/p&gt;

&lt;p&gt;PHP's other warnings still apply. Memory use will climb, so put a container or cgroup limit around PHP, and don't use &lt;code&gt;RLIMIT_AS&lt;/code&gt; or &lt;code&gt;RLIMIT_DATA&lt;/code&gt;, because PHP says those can trigger the vulnerable path themselves. Once PHP is updated, take the &lt;code&gt;-1&lt;/code&gt; back out.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to check what you've really got
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;php -i&lt;/code&gt; on the command line won't help. The CLI has its own ini file and doesn't read &lt;code&gt;.user.ini&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You need to ask through the web server, and Magento's sample config gets in the way: it only runs &lt;code&gt;index&lt;/code&gt;, &lt;code&gt;get&lt;/code&gt;, &lt;code&gt;static&lt;/code&gt;, &lt;code&gt;errors/report&lt;/code&gt;, &lt;code&gt;errors/404&lt;/code&gt;, &lt;code&gt;errors/503&lt;/code&gt; and &lt;code&gt;health_check&lt;/code&gt;, and denies every other &lt;code&gt;.php&lt;/code&gt; file. So for a quick check:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create &lt;code&gt;pub/probe-x7f3.php&lt;/code&gt;, using your own random name:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;   &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type: text/plain'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="kc"&gt;PHP_VERSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;PHP_SAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;' memory_limit='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;ini_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'memory_limit'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;probe-x7f3&lt;/code&gt; to the regex on the main entry point location and reload nginx.&lt;/li&gt;
&lt;li&gt;Request it over HTTPS.&lt;/li&gt;
&lt;li&gt;Delete the file, undo the regex and reload again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you changed a &lt;code&gt;.user.ini&lt;/code&gt;, allow for &lt;code&gt;user_ini.cache_ttl&lt;/code&gt;, which defaults to 300 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is anyone attacking Magento with this?
&lt;/h2&gt;

&lt;p&gt;CISA added both CVEs to its Known Exploited Vulnerabilities catalog on 26 October 2026. We haven't seen an authoritative source report attacks aimed specifically at Magento or Adobe Commerce. The attack can land before any application code runs, though, so that isn't a reason to wait.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.php.net/archive/2026.php#2026-10-21-5" rel="noopener noreferrer"&gt;PHP security advisory, 21 October 2026&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w" rel="noopener noreferrer"&gt;GHSA-w8hw-w9fx-j79w, CVE-2026-109618&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/magento/magento2/blob/2.4.9/nginx.conf.sample" rel="noopener noreferrer"&gt;Magento 2.4.9 nginx.conf.sample&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.php.net/manual/en/install.fpm.configuration.php" rel="noopener noreferrer"&gt;PHP manual: FPM configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.php.net/manual/en/configuration.file.per-user.php" rel="noopener noreferrer"&gt;PHP manual: .user.ini files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/php/php-src/blob/PHP-8.5/sapi/fpm/fpm/fpm_php.c" rel="noopener noreferrer"&gt;php-src: fpm_php.c&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We keep a longer guide covering which PHP version each Magento release needs and where the control panels stand: &lt;a href="https://everyhost.co.uk/insights/php2shell-magento-cve-2026-109618" rel="noopener noreferrer"&gt;PHP2Shell and Magento&lt;/a&gt;. There's also a &lt;a href="https://everyhost.co.uk/magento-end-of-life" rel="noopener noreferrer"&gt;Magento and Mage-OS version and patch tracker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was written with AI. The tests were run locally, and the results and sources were checked before publishing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>magento</category>
      <category>nginx</category>
      <category>security</category>
    </item>
  </channel>
</rss>
