WAF and CDN rules for PHP2Shell (CVE-2026-109618 and CVE-2026-109619) block known exploit patterns on traffic that passes through the provider. They do not fix PHP, they miss requests that reach your origin directly, and they can lag new variants. Treat them as a stopgap: lock down the origin, patch PHP, restart every worker, then verify.
Our PHP2Shell explainer covers the flaws, the leak and the affected versions. This post answers a narrower question: if your provider has pushed a rule, how much time does it buy?
What the providers said
Several providers published statements on 21 October. The table records what each said about its own rules. It is not a comparison, and we have not tested any of these products.
| Provider | What it says it shipped | On by default? | What it says about patching | Source |
|---|---|---|---|---|
| Cloudflare | Seven "Generic Rules, Anomalous Execution" rules in the Free ruleset, seven in the Managed Ruleset, plus a response-side rule | Yes on Free; paid plans asked to confirm block mode | Patch even with the rules on | Cloudflare blog and changelog |
| Akamai | Adaptive Security Engine rule 3000991 v1 for PHP RCE attacks on both CVEs; existing command injection rules also catching weaponised payloads | The post says App and API Protector customers are protected | Prompt patching is "the most effective defense" | Akamai |
| Fastly | A CVE-specific templated virtual patch in its Next-Gen WAF, for Edge and on-prem deployments | Customers are told to apply it; opted-in customers get updates automatically | Calls active mitigation partial; patching is the fix | Fastly |
| Imperva | A virtual patch; Cloud WAF covered out of the box, Elastic WAF and WAF Gateway via software and content updates | Cloud WAF yes; others need updates | Mitigations "do not replace patching" | Imperva |
We did not find a managed rule published by AWS. Classmethod engineer Suzuki Ryo published a test of custom AWS WAF rules that block multipart POSTs on a load balancer; its results, covered below, apply well beyond AWS.
Cloudflare's rules in detail
- Timing. Cloudflare says it switched the rules on at 03:02 UTC on 21 October, about nine hours before public disclosure. PHP's fixes went live around 13:45 GMT, per Reuters via KSL.
- Scope. Rules apply automatically on every plan with WAF, including Free. Paid customers on the Managed Ruleset are told to confirm the deployment is in block mode.
- Naming. Neither changelog entry names PHP or either CVE, so searching for the CVE finds nothing. Use the rule IDs in Cloudflare's blog post. The changelog text also says four rules (Managed) and two (Free), while both tables list seven. Go by the IDs.
- Response side. A response rule in the Cloudflare Managed Response Firewall Default ruleset uses what Cloudflare calls full transaction detection, matching a request with the origin's response to spot exploits that worked.
- Attack traffic. Cloudflare says it observed attack traffic aimed at its customers after the leak, and that attackers mutated payloads to try to get past the rules. It gave no volumes.
Cloudflare's CTO Dane Knecht put it plainly on X, as quoted by Reuters via KSL: "This is a STOPGAP only." The blog post says to patch even with the rules on, and to keep them on while you do.
What the others added
Fastly's post, by Chief Product Officer Kelly Shortridge, says active mitigation is partial and may block a lot of legitimate traffic. It reports evidence of attacks before disclosure and expects new variants through at least next week.
Imperva says it saw no requests matching the patterns it tracked before disclosure. That differs from Cloudflare and Fastly, but the networks and patterns differ, so both can be true. Assume attempts are under way.
Why a WAF is a stopgap for this bug
This is not a knock on WAFs. The issue is where PHP2Shell lives.
-
The flaw is in the runtime, not a URL. Many web bugs sit behind one endpoint a rule can block. PHP2Shell fires while PHP sets up and tears down a request; on mod_php, PHP says, before any application code runs. Any URL that reaches PHP is a candidate.
-
Rules match shapes, and shapes change. Fastly says its detections look for bodies and parameters "shaped in ways that differ from legitimate traffic". Cloudflare says attackers were already mutating payloads. Today's rule may miss tomorrow's variant.
-
Strict rules break real traffic. Multipart bodies and large form posts are also how real applications work. Every exception you add to keep uploads or checkout working is a gap.
-
A response rule cannot undo code that already ran. Cloudflare's full transaction post says blocking a response cannot undo actions the origin already performed. It may hide the output from the attacker, but a written file or a spawned process stays.
-
The WAF only sees what passes through it. Tenable notes Cloudflare's rules cover only traffic routed through Cloudflare. That holds for every edge product, and the next section is about it.
-
Only the patch changes PHP. Per PHP's GitHub advisory, the fix removes the abrupt jump out of a request on out-of-memory and adds a soft limit and a higher hard exit. No edge rule can do that. Fastly adds that the underlying design issue remains after patching, a reason to keep layers on after you upgrade.
Origin exposure: the traffic your CDN never sees
A CDN protects a hostname. An attacker who finds the server behind it can skip the edge and talk to PHP directly. Common ways an origin stays reachable:
- Direct IP access. The server answers on 80 or 443 to anyone. Cloudflare warns that historical DNS records may still expose pre-CDN addresses and suggests rotating origin IPs.
- DNS-only records. A staging, admin or api hostname points straight at the same server. Cloudflare also says to audit SPF and TXT records, and notes that bounced email can reveal a server's IP.
- A second path. A load balancer's own public endpoint, a cloud default hostname, a second CDN, or an internal network with broad reach.
- Monitoring vhosts. An extra virtual host left open for an uptime checker often serves the same PHP pool.
Check your own origin
Run these from outside your network, against your own hostname and origin IP.
# What the public hostname resolves to (should be CDN addresses)
dig +short www.example.com
# Does the origin answer directly if you skip the CDN?
curl -sk -o /dev/null -w '%{http_code}\n' \
--resolve www.example.com:443:203.0.113.10 https://www.example.com/
# Same check over plain HTTP
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: www.example.com' http://203.0.113.10/
A 200, 301 or 302 means the origin serves anyone who asks. You want a timeout or refused connection. On the server, check listeners and the host firewall:
sudo ss -ltnp | grep -E ':(80|443|8080|8443)\b'
sudo nft list ruleset 2>/dev/null || sudo iptables -S
Close the gap
Cloudflare's origin protection docs list several layers; other providers offer similar ideas under their own names.
| Control | What it proves | Caveat from Cloudflare's docs |
|---|---|---|
| Allowlist the provider's IP ranges at the firewall | Traffic came from the provider's network | Rated moderately secure; vulnerable to IP spoofing; you maintain the list |
| Authenticated Origin Pulls (mutual TLS) | The request came through Cloudflare | Needs Full or Full (strict) SSL; the default certificate only proves it came from Cloudflare, so upload your own for stricter checks |
| Cloudflare Tunnel | The origin has no public listener at all | Needs the cloudflared daemon |
| Secret header validation | The request passed through your edge config | Moderately secure; header handling needs care |
| JWT validation | The request carries a valid token | Very secure, but needs code changes or extra software |
On a patch day, the fastest win is the host firewall: allow 80 and 443 only from your provider's published ranges and your management network, then rerun the curl test. Schedule mutual TLS or a tunnel afterwards.
The interim PHP settings, and why they are partial
PHP's advisory lists settings for teams that cannot upgrade at once, with a warning: "There are no complete mitigations, updating is the only safe choice." Here is what each closes and leaves, per php.net and the two GitHub advisories.
| Setting | Targets | What it closes | What stays open |
|---|---|---|---|
memory_limit = -1, set with php_admin_value | CVE-2026-109618 | The predictable per-request memory limit failure | An underlying allocation failure can still reach the vulnerable path, per php.net |
post_max_size = 8M (32M with uploads), upload_max_filesize below it | CVE-2026-109618 | Large bodies parsed by PHP | GHSA notes a multipart request without a usable declared length can pass the first size check |
| Front-end limits on decoded size, field count, parts, headers, nesting | CVE-2026-109618 | Oversized or deeply nested input before PHP sees it | Lowers OOM and DoS risk; does not replace updating |
| Block multipart at the front end if unused | CVE-2026-109618 | Multipart parsing entirely | Breaks uploads and many forms |
session.upload_progress.enabled = 0 | CVE-2026-109619 | The demonstrated session path | Other lifecycle paths, per GHSA |
fatal_error_backtraces = 0 (8.5 only) | CVE-2026-109619 | The confirmed 8.5 backtrace path | Other request-owned global and extension state |
| Container or cgroup memory limit | CVE-2026-109618 | An alternative to the PHP limit | Does not guarantee allocation failure; can kill the whole pool |
Three warnings from the advisories are easy to miss:
- Do not use RLIMIT_AS or RLIMIT_DATA. They make allocations fail at the limit, which is the exact condition CVE-2026-109618 needs.
- Hiding errors does nothing. php.net says suppressing diagnostics with error_reporting, display_errors or the @ operator does not help.
- Where you set it matters. Use php.ini, the FPM pool or server configuration. php.net says not to rely on
ini_set()or FPM.user.ini, which apply too late.
For an FPM pool, that looks like this:
; /etc/php/8.3/fpm/pool.d/www.conf (path varies by distro)
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 6M
php_admin_flag[session.upload_progress.enabled] = off
And for Apache with mod_php:
# In the vhost or a conf file loaded globally, not .htaccess
php_admin_value memory_limit -1
php_admin_value post_max_size 8M
php_admin_flag session.upload_progress.enabled off
Front-end limits go in the proxy; tune the numbers to your application:
client_max_body_size 8m;
large_client_header_buffers 4 8k;
LimitRequestBody 8388608
LimitRequestFields 50
Settings other vendors suggested
Fastly suggests max_input_nesting_level = 4 and, for PHP-FPM, pm.max_requests = 50. PHP's GitHub advisory mentions lowering nesting and considering pm.max_requests = 1, noting that recycling workers reduces success but does not remove the condition. Imperva suggests testing opcache.enable = 0, which php.net does not list. Test these under load and count none as a fix.
Also, memory_limit = -1 removes a guard rail: a leaking request can grow until the host steps in. Watch memory while it is set and restore a finite limit after patching.
Sequencing: stopgap, patch, restart, verify
Each step fails quietly if the one before was skipped. A patched package with old workers running is unpatched.
| Stage | Action | Done when |
|---|---|---|
| 1. Stopgap | Edge rules on in block mode; origin locked to the provider; interim settings applied | Direct curl to origin fails; events show rule hits |
| 2. Patch | Fixed PHP packages installed or images rebuilt | Changelog or version shows the fix |
| 3. Restart | Every PHP worker and server process replaced | Process start times are after the install |
| 4. Verify | Version, config, origin lock and logs checked | All four checks pass on every host |
| 5. Unwind | Temporary settings reviewed; edge rules kept | Finite memory limit restored; record updated |
1. Stopgap, in parallel, within the hour
- Confirm your provider's rules are present and blocking. On Cloudflare, filter Security Analytics events by the rule IDs from its post. Paid plans should check the Managed Ruleset action.
- Run the origin checks above and close any direct path.
- Apply the interim PHP settings in pool or server config, then restart workers so they take effect.
- If a site cannot be patched today and the origin cannot be locked, follow the UK NCSC's advice as reported by Help Net Security: consider taking it offline. Our post on governments taking websites offline over PHP2Shell shows how public bodies made that call.
2. Patch
Install PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or your distro's backport. On distro packages, the version string may look old, so search the changelog for the CVE:
apt changelog php8.3-fpm | grep -m1 -E 'CVE-2026-10961[89]'
rpm -q --changelog php-fpm | grep -m1 -E 'CVE-2026-10961[89]'
For containers, pull the patched base image and rebuild. Our guide to finding and patching every PHP runtime covers fleets, images and hosting panels in depth.
3. Restart
A new binary on disk does nothing until the old processes exit.
sudo systemctl restart php8.3-fpm
sudo systemctl restart apache2 # or httpd
ps -o pid,lstart,cmd -C php-fpm8.3,apache2,httpd | head
Compare the start times with the time you installed the package. For containers, roll the deployment so every pod is replaced, not just new ones.
4. Verify
# Version of the serving binary, not just the CLI
php-fpm8.3 -v
docker run --rm --entrypoint php your-registry/app:prod -v
# Effective pool settings
grep -R -E 'php_(admin_)?(value|flag)' /etc/php/8.3/fpm/pool.d/
# Origin still locked after the restart
curl -sk -o /dev/null -w '%{http_code}\n' \
--resolve www.example.com:443:203.0.113.10 https://www.example.com/
Then check logs for the signals Fastly described: out-of-memory errors that point at "Unknown on line 0", bursts of segfaults, and PHP workers spawning shell processes. Any of those before you patched means the host needs investigation, not just an upgrade.
5. Unwind carefully
Keep the edge rules on. Cloudflare recommends it, and they cost little. Put a finite memory_limit back once you are patched. PHP's advisory suggests keeping session upload progress off if you do not use it. Record which sites had open origins and fix them properly with mutual TLS or a tunnel.
What would have helped
The teams with the easiest 21 October already knew where PHP ran, whether each origin was reachable directly, and how to rebuild and restart in an hour.
Practices any team can use:
- An origin map. For every public hostname, record the origin, whether it accepts direct traffic, and how the edge authenticates to it. Test it quarterly with the curl check above.
- Origin authentication by default. Mutual TLS or a tunnel on every new site, so the WAF is the only way in.
- A runtime inventory with SAPI and version. mod_php sites go first.
- Interim settings prepared, not invented. A reviewed snippet per server type in the runbook.
- A tested rebuild and roll path, with "process start time is after the patch" in the definition of done.
- Edge rule hygiene. Know which managed rules are in log mode and who can switch them to block.
- A pre-agreed offline decision. Who decides, and what users see.
Where Safeguard fits
Safeguard is not a WAF, does not see your traffic, and would not have blocked a PHP2Shell request. It helps with the part of the sequence that is about knowing what to patch and making the change.
- Finding PHP in what you build.
safeguard scan --image <name>builds an SBOM of a container image, including OS packages such as PHP, and matches components against vulnerability databases.safeguard scan --sbom <file>does the same for SBOMs from vendors, andsafeguard scan --dir .covers repositories. Results are written as JSON under ./output. - Which projects ship this version. The platform shows which projects contain a given component and version, so "which images still carry old PHP" becomes a list.
- Stopping regressions. Policies and gates evaluated in the platform can block a build that still carries a vulnerable PHP package, and
safeguard execruns checks in CI. - Making the change with approval. The Safeguard CLI's assistant can edit a Dockerfile to a patched base tag and run your build and tests with your approval.
/permissionssets how much it may do alone: Ask, Accept edits, Plan, Auto (the default) or Strict. The desktop and phone apps can send that work to a computer running the CLI and approve each step.
Where it does not help: edge rules, origin firewalls, mutual TLS, php.ini settings on hosts, restarting workers, or spotting a compromise. Reachability analysis does not lower priority here, because the bug fires in the runtime before application code. Matching can also lag a same-day advisory, and distro backports keep old version strings, so confirm against your vendor's changelog.
Zero-Day Discovery and Gold
- Zero-Day Discovery looks for previously unknown flaws before a CVE exists, in an agentic mode and a deterministic scanner mode, and issues findings as SGZ advisories before they reach public CVE databases. It covers seven package ecosystems. PHP core is a runtime, not a package in those ecosystems, so it falls outside that scope.
- Gold Open Source (gold.safeguard.sh, free, no login) offers searchable CVE intelligence with CVSS, EPSS, KEV status, affected and fixed versions and a recommended safe version, plus a free read API and a CI gate GitHub Action.
- Gold Registry (registry.safeguard.sh, paid) provides hardened drop-in components and container images held to zero exploitable critical or high CVEs, SLSA L3+ and Sigstore signed, with evidence pages. An artifact loses its badge on regression.
The habit that carries over: pull patched, signed components from a curated source and be able to rebuild and redeploy fast, so the WAF only has to hold for hours.
To try Safeguard, download it at https://safeguard.sh/download and read the CLI docs at https://docs.safeguard.sh/docs/cli .
Common mistakes and troubleshooting
- "The rule is on, so we can patch next week." Every provider quoted here says to patch. The rule is for the gap between the leak and your restart.
- Leaving paid rulesets in log mode. Log mode records the attack and lets it through.
- Forgetting the staging hostname. A DNS-only record pointing to the same server is a full bypass.
- Setting workarounds in .htaccess or .user.ini. Use server or pool config. On mod_php, the GitHub advisory says per-directory settings apply before request startup and can act as an early trigger.
- Blocking all multipart POSTs on an app that takes uploads. Classmethod's test found a multipart block rule caught every variant from the first request, and also blocked forms with no file. Scope it to paths that never take uploads.
- Relying on rate limits. In the same single-run test, rate-based rules let roughly 20 to 30 requests through before the first block. One request can be enough here.
- Patching without restarting. The old code stays in memory.
FAQ
Do Cloudflare's PHP2Shell rules protect free plans?
Yes, according to Cloudflare. The rules deploy automatically on every plan with WAF, including Free, and the response-side rule applies to all plans. Cloudflare still says to patch.
Is a WAF virtual patch enough if I am on PHP 7.4 or older?
No. PHP published minimal source backports for 7.4, 8.0 and 8.1; older versions need a vendor backport or an upgrade. If neither is possible, lock the origin, apply the interim settings, and consider taking the site offline.
My site is behind a CDN. Why worry about the origin?
The CDN only inspects traffic sent to it. If the server answers on its own IP or a DNS-only hostname, an attacker can skip the edge. Run the direct curl test.
Will the response-side rules catch a successful exploit?
They may stop data from reaching the attacker. Cloudflare says blocking a response cannot undo actions the origin already performed, so code that ran still ran.
Should I disable OPcache?
Imperva suggests testing opcache.enable = 0, but php.net does not list it. Measure the performance cost first and do not count it as a mitigation.
Which interim setting helps most?
None is complete. Apply memory_limit = -1, request size limits and disabled session upload progress together, in server or pool config, then restart.
Fix it without waiting for upstream
In incidents like this one, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would have helped
These demos show real Safeguard sessions on other examples. None shows PHP2Shell or a WAF.
The CLI demo fixes a lodash finding: it reads the advisory, asks before each command, edits the version, runs install and tests with approval, and pushes nothing. For PHP2Shell, you would scan your PHP image, then ask the assistant to move the Dockerfile base tag to a fixed release and rebuild, approving each step. Edge rules and origin firewalls stay with your infrastructure team.
Click through it at your own pace, or open it full screen.
The local runner demo sets up the runner on your own machine, approves it with a device code, and scans from the terminal. Here, that fits step 4 of the sequence: after the rebuild, scan the new image locally and confirm the PHP package no longer matches either CVE before you roll it out.
Click through it at your own pace, or open it full screen.
The desktop app demo shows a CLI session in the conversation list, a request for work on your computer, choosing the folder, then Approve or Deny on each command and file edit, ending with an upgrade done and tests passing. On a patch day, the same flow suits a repo of Dockerfiles: you approve each base image change and build.
Click through it at your own pace, or open it full screen.
Quick check
Your site is behind a WAF with PHP2Shell rules, and a curl request sent straight to the origin IP returns 200. Are you protected?
No. The origin is serving PHP to anyone who skips the edge. Restrict the host firewall to your provider and management network, add origin authentication, and patch.
You set session.upload_progress.enabled = 0 in .user.ini. Does it count?
No. php.net says not to rely on ini_set() or FPM .user.ini. Set it in php.ini, the FPM pool or server config, restart every worker, and remember it only covers one CVE-2026-109619 path.
Sources
- Cloudflare: Cloudflare deploys protections against php2shell vulnerability
- Cloudflare: Introducing full transaction detection for Cloudflare WAF
- Cloudflare changelog: WAF Release 2026-10-21, Emergency, Cloudflare Managed Ruleset
- Cloudflare changelog: WAF Release 2026-10-21, Emergency, Cloudflare Free Ruleset
- Cloudflare docs: Protect your origin server
- PHP news archive: advance notice, security advisory and 21 Oct releases (php.net)
- GHSA-w8hw-w9fx-j79w: RCE via OOM corruption, CVE-2026-109618 (php-src on GitHub)
- GHSA-6wmf-4rv5-p3rp: RCE via stale global state, CVE-2026-109619 (php-src on GitHub)
- Akamai: Critical remote code execution vulnerabilities in PHP core
- Fastly: proactive protection for PHP2Shell, CVE-2026-109618 and CVE-2026-109619
- Imperva: PHP2Shell, when PHP runs out of memory at the wrong moment
- Classmethod DevelopersIO: AWS WAF rules on an ALB to block and throttle multipart POSTs after the php2shell advisory
- Tenable: PHP2Shell frequently asked questions
- Help Net Security: PHP2Shell vulnerabilities patched
- KSL (Reuters): Cyber agency warns websites may go dark as PHP prepares critical fix
Last reviewed: 21 October 2026. This is a developing story; check the linked advisories for the latest status.