How to update the PHP version under WordPress after PHP2Shell: check it, patch it, verify it, and pick a version that lasts past December
At 14:00 UTC on October 21, 2026, PHP shipped emergency fixes for two flaws rated 9.2 that can let a stranger run code on a web server without logging in. The bugs, nicknamed PHP2Shell and tracked as CVE-2026-109618 and CVE-2026-109619, sit inside the PHP engine itself, so no WordPress core update, plugin or theme can fix them. The fixed releases are PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35, and the PHP project ships nothing at all for PHP 8.1 or older - PHP security advisory.
Here is the problem: roughly one WordPress site in three runs a PHP branch that the PHP project no longer patches. WordPress.org's live statistics on October 23 put 34.7% of WordPress sites on PHP 8.1 or older, including 15.5% on PHP 7.4 alone - WordPress.org statistics. For those sites, "update PHP" is not a button that quietly installs a fix. It is a version jump that can break old plugins, a hunt for a vendor backport, or a decision to take the site offline for a day, which is exactly what the US cybersecurity agency told owners of unpatchable systems to do - CISA alert.
The story also kept moving after the release. On October 23 the PHP project disclosed a third, lower-risk issue, CVE-2026-115560, and asked everyone to keep one of the stopgap settings switched on even after patching, until the next regular PHP release - PHP informational advisory. Attack traffic has been heavy, yet two days in, the firms watching it report no confirmed compromise from PHP2Shell itself, a gap this guide explains rather than celebrates.
This guide explains what PHP2Shell actually does in plain language, how exposed WordPress sites really are, and exactly how to update the PHP version WordPress runs on: on managed hosts, in cPanel, Plesk and hPanel, and on servers and containers you run yourself. It covers how to verify the fix is really loaded (the version number can mislead you in both directions), which PHP version to move to now that PHP 8.2 itself loses security support on December 31, 2026, what to do if you cannot patch today, and how to check whether anyone got in before you patched. It ends with the structural question this week raises for every site owner: who actually owns the runtime under your website.
Contents
- What PHP2Shell Is, in Plain Terms
- How It Unfolded: From a 2004 Bug to a Leak on October 20
- How Exposed WordPress Is: The Numbers
- How Dangerous It Is in Practice: The Honest Range
- Step 1: Find the PHP That Serves Your Site, and Prove It Is Patched
- Step 2: Update PHP on Your WordPress Host, Panel by Panel
- Step 3: Self-Managed Servers, VPS and Containers
- Step 4: Pick a PHP Version That Lasts, Without Breaking the Site
- If You Cannot Patch Today: Stopgaps and Their Limits
- After the Patch: Check Whether You Were Hit
- The Bigger Lesson: Who Owns the Runtime Under Your Site
- Conclusion: A Decision Framework for This Week
Every Way to Deal With PHP2Shell, Scored
Before the detail, here is every realistic response a WordPress site owner has this week, scored on the four things that matter. Most owners will end up combining two of these (a stopgap today, a real fix within hours), so read the table as a ranking of where each option belongs in that plan rather than as a menu of alternatives.
| # | Approach | What It Does | Closes the bug (40%) | Effort today (20%) | Break risk (20%) | Lasts past 2026 (20%) | Final |
|---|---|---|---|---|---|---|---|
| 1 | Patch in place (8.2 to 8.5) | Install 8.2.35, 8.3.36, 8.4.27 or 8.5.12 and restart PHP | 10 - the official fix for both CVEs | 9 - host does it, or one package update and a restart | 9 - same branch, near-zero behaviour change | 7 - fine on 8.3+, but 8.2 loses support Dec 31, 2026 | 9.0 |
| 2 | Upgrade to PHP 8.3 or 8.4 | Move an 8.1-or-older site to a supported, patched branch | 10 - lands on a fixed release | 7 - one dropdown on most panels, plus a compatibility test | 6 - 7.4 to 8.x breaks unmaintained plugins | 10 - 8.3 supported to end 2027, 8.4 to end 2028 | 8.6 |
| 3 | Move to a managed host | Let a host with a security team own PHP | 9 - hosts shipped patched builds, including old branches | 4 - a migration takes days, not hours | 7 - migrations carry their own risk | 9 - the next runtime patch is theirs too | 7.6 |
| 4 | Vendor backport on 8.1 or older | Install a distro or LTS package with the fix backported | 8 - fix backported, but coverage varies by vendor | 7 - a package update where the vendor offers one | 9 - same PHP version, same behaviour | 4 - the next bug may not get a backport; some are paid | 7.2 |
| 5 | Rebuild off PHP | Move to a static, headless or non-PHP stack | 10 - no PHP runtime left to exploit | 1 - weeks of work | 3 - a new site is a new set of risks | 7 - a different runtime still needs patching | 6.2 |
| 6 | Take the site offline | Maintenance mode or shutdown until patched | 9 - no traffic, no exploit | 9 - minutes to do | 2 - lost revenue and visibility | 1 - a pause, not a plan | 6.0 |
| 7 | Build PHP from source backports | Compile the PHP team's 7.4, 8.0 or 8.1 patch branches | 8 - minimal official backports | 2 - requires compiling and packaging PHP | 7 - same version, but a hand-built binary | 3 - no future fixes on that branch | 5.6 |
| 8 | WAF rules only | Rely on Cloudflare, Sucuri or host firewall rules | 3 - blocks known payloads; attackers mutate them | 9 - automatic on Cloudflare, even Free | 8 - rare false positives | 2 - a stopgap by its makers' own words | 5.0 |
| 9 | php.ini mitigations only | memory_limit=-1, upload progress off, block multipart | 3 - closes confirmed paths, not variants | 6 - server config access and restarts | 4 - breaks uploads, risks memory exhaustion | 1 - temporary by design | 3.4 |
How the criteria were weighted. "Closes the bug" carries 40% because it is the only thing that changes whether an attacker can get in; everything else is cost. "Effort today" (20%) reflects that this is an hours-scale emergency. "Break risk" (20%) captures the real reason sites stay on old PHP: fear of a white screen. "Lasts past 2026" (20%) matters because PHP 8.2, the oldest patched branch, reaches end of life in ten weeks, so a fix that strands you there only buys a short reprieve. The two top rows are not competitors: patching in place is what you do this hour, and upgrading to 8.3 or 8.4 is what you schedule this week if the first step left you on 8.2 or on an unsupported branch.
1. What PHP2Shell Is, in Plain Terms
Every time someone loads a page on a WordPress site, the web server hands the request to PHP. PHP sets aside memory for that request, reads the incoming data, runs WordPress, sends back the page and then cleans up. On a busy server, the same PHP worker process handles thousands of requests in a row, cleaning up after each one. PHP2Shell is what happens when that cleanup goes wrong at exactly the wrong moment, and an attacker learns to make it go wrong on purpose.
The first bug, CVE-2026-109618, lives in how PHP reacts when a request hits its memory_limit. Before the fix, PHP jumped straight out of whatever internal function was running (a C mechanism called longjmp), even at moments when request memory was reachable but not yet fully initialized. Request cleanup would then treat that half-built data as if it were valid - GitHub advisory GHSA-w8hw-w9fx-j79w. The second bug, CVE-2026-109619, is about leftovers: a fatal error can leave a worker holding pointers into memory that belonged to a previous request, and the next request handled by that worker can use them before they are reset - GitHub advisory GHSA-6wmf-4rv5-p3rp. Both are classic use-after-free memory bugs, the kind that can turn a crash into code execution.
The way attackers reach the memory limit is the part that makes this alarming for WordPress. PHP parses incoming form data and file uploads (multipart/form-data) before WordPress runs a single line. Calif's researchers showed that an upload field with an empty filename does not count toward PHP's upload limit, yet still builds six internal entries, and that nested field names multiply the work. Hundreds of such records can fill the default 128 MiB heap while the request body stays under the default 8 MiB post_max_size - Calif research. In other words, a small, unauthenticated request can push PHP to fail at a moment the attacker chooses.
Which server setup you run decides how bad that is. PHP's own advisory says every installation serving web requests is at risk, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed, but it rates mod_php as the highest risk because a demonstrated exploit fires before any application code runs - PHP security advisory. PHP-FPM, the more common modern setup, kills the worker when request startup fails, which blocks that particular path, yet the PHP Foundation says code execution was still confirmed on FPM through application-level code paths - PHP Foundation.
Four facts matter most for a WordPress owner:
- It is a runtime bug: updating WordPress, plugins or themes does nothing
- No login is needed: the attack arrives as an ordinary web request
- It can leak secrets: worker memory can come back inside a cookie
- Old branches are on their own: php.net ships no fix for 8.1 or older
The secret-leak point deserves more attention than it has received. Wiz and Sansec both describe a path where PHP's session upload-progress handler returns freed worker memory to the attacker inside a PHPSESSID cookie, even on sites that never call session_start(). That memory can hold passwords, session tokens, API keys and even TLS private keys from other visitors' requests - Sansec research. A server does not need to be fully taken over to lose its secrets, which changes what "check whether you were hit" means later in this guide.
One naming trap is worth clearing up before going further. PHP2Shell is not wp2shell, the WordPress core exploit chain from July 2026 (CVE-2026-63030 and CVE-2026-60137) that abused the REST API's batch feature and was fixed in WordPress 7.0.2, 6.9.5 and 6.8.6 - BleepingComputer. That one was a WordPress bug, so automatic core updates closed it. PHP2Shell is a PHP bug, so they do not, and a site can be fully patched against the first and wide open to the second. Wiz's official artwork for the disclosure, below, is the image most people saw first this week.
Why this matters in practice: the WordPress mental model ("keep core and plugins updated and you are fine") stops at the application layer, and PHP2Shell sits one layer below it. How to apply this: treat the PHP version as something you must check yourself, every time a runtime advisory lands, rather than something your auto-updates cover. The researchers who found the bugs walk through the technical story in Wiz's official briefing, recorded the day of disclosure, with Wiz Research's Sagi Tzadik and Calif's Thai Duong.
The conversation is worth watching for one reason above the rest: it makes clear that both research teams treated the WordPress and Drupal exploits as proof of reach, not as the point. The bug is in the engine, so any PHP application that accepts form posts was a candidate, and the specific CMS only determined how much work the final exploit took. That framing explains why the next section's timeline involves so many organizations that have nothing to do with WordPress, and why the fix took weeks rather than days.
2. How It Unfolded: From a 2004 Bug to a Leak on October 20
PHP2Shell did not appear out of nowhere this month. The root idea, that hitting the memory limit at the wrong moment can corrupt PHP's state, has a long paper trail. Calif's write-up traces its search back to a 2004 memory-limit bug found by Stefan Esser, and says the earliest affected path it found dates to PHP 5.4.0, released in 2012 - Calif research. The researchers published the history as a comic, and its second page shows the 2020 stop on that trail: PHP bug #78876, where long variables caused out-of-memory errors and temporary files were not cleaned up.
The more uncomfortable chapter is 2024. During PHP's first external security audit in years, carried out by Quarkslab through OSTIF and funded by Germany's Sovereign Tech Agency, a PHP core developer found the first signs of the out-of-memory problem behind CVE-2026-109618. Every fix the team designed carried a cost (compatibility breaks, new denial-of-service risk, or changes to third-party extensions), the security team worked by consensus, and that process caused work on a fix to "stall and restart repeatedly over months". The PHP Foundation's own words are blunt: "the delay was a structural failure" - PHP Foundation. That is worth understanding, because it is the reason the fix, when it came, was large: Alexandre Daubois of the PHP security team says the patch removes the jump from the allocator entirely and touches 390 files - Alexandre Daubois.
AI changed the speed of everything after that. Earlier in 2026 the Foundation and Alpha-Omega scanned PHP with Anthropic's Claude Mythos, and the team later added OpenAI's Daybreak Red for validating reports and exploits. On July 30, Calif began an audit under OpenAI's Patch the Planet program; on August 11 Calif's Ryan Austin reported an unconditional remote code execution with the same root cause as the 2024 problem, OpenAI independently reported that root cause, and further attack vectors followed from Wiz on August 24 and OpenAI on September 18 - PHP Foundation. OpenAI says Calif found its flaw using GPT-5.6 Sol - OpenAI. Google says Gemini 4 Argon made "an invaluable contribution" to Wiz's discovery, exploit development, attack detection and patch validation - Google Cloud. (We covered that model's launch and pricing in our Gemini 4 Argon guide.)
The final 48 hours were chaotic. Around 15:00 UTC on October 20, the two CVE IDs began circulating with a wrong release time and a wrong CVSS score of 10.0; PHP responded by publishing its advance notice at 19:30 UTC and pulling the release forward from October 22 to October 21 at 14:00 UTC. Around 20:00 UTC, Calif mistakenly published a video of its proof of concept; it was deleted, but copies circulated, and the Foundation says it contributed to accelerated proof-of-concept development. At 07:41 UTC on October 21, Chainguard published patched images more than six hours early, which the Foundation describes as unilaterally breaking the embargo - PHP Foundation. Chainguard's CEO said he made the call to ship early "when exploits hit in the wild" - Socket. By October 23 the advisory thread on Hacker News held 1,553 points and 658 comments - Hacker News.
Two dates in that sequence matter more to you than the drama. First, Amazon's MadPot honeypots recorded an exploitation attempt on October 17, four days before disclosure, from an anonymizing network, and Amazon says it is unclear how the attacker got the information - Amazon Threat Intelligence. Second, Cloudflare had protections live for every plan, including Free, from 03:02 UTC on October 21, nearly eleven hours before PHP's release - Cloudflare. Why this matters: your exposure window did not start at 14:00 UTC on October 21, it started at least on October 17, and anything you investigate later must go back that far. How to apply this: keep every web, PHP and firewall log you have from October 17 onward until you have finished the checks in section 10.
The broader point is structural rather than dramatic. OpenAI argues that frontier AI found these bugs before attackers did, giving defenders a head start it calls a "Defender's Window". The PHP Foundation's numbers show the other side of the same coin: the team has published 15 CVEs since July 1 and worked through more than 100 reports that were invalid, duplicates or not reproducible, because AI-assisted research multiplies the incoming volume. The defender's window is real, but it is only as long as the slowest step between a maintainer's patch and your server, which is the step this guide is about.
3. How Exposed WordPress Is: The Numbers
PHP2Shell's headline reach comes from PHP's footprint. W3Techs reports that PHP runs 69.3% of all websites whose server-side language it can detect - W3Techs. That number alone says little about risk, though, because a site on PHP 8.4 can patch in minutes while a site on PHP 7.4 cannot patch at all from php.net. The question that decides how hard this week is for you is which PHP branch your WordPress install runs on, and the WordPress project publishes that distribution live.
The chart below uses WordPress.org's own statistics, read on October 23, 2026. PHP 8.3 (27.0%) and PHP 8.2 (23.5%) lead, but PHP 7.4 still holds 15.5% and PHP 8.1 holds 10.0%, and every version from 7.3 down to 4.4 adds up to another 5.6% - WordPress.org statistics. The newest branches are the smallest slices: 8.4 at 9.8% and 8.5 at 4.9%, with release candidates of the coming PHP 8.6 on a sliver of sites (0.02%).
Adding those up gives the number that defines this week: 65.3% of WordPress sites run a branch that received an official fix on October 21, and 34.7% run a branch that did not. The wider PHP web looks worse. Using W3Techs' October 23 breakdown (PHP 8 on 45.3% of all websites, which is about 65.4% of PHP sites, with 8.1 and 8.0 making up 20.9% of PHP 8), roughly 51.7% of PHP websites sit on a patched branch and 48.3% on 8.1 or older - W3Techs PHP 8 breakdown. WordPress, pushed by host defaults and core's own nudges, is actually ahead of the PHP web as a whole.
Internet scans tell a consistent story. Tenable's FAQ cites an estimate of over 1.3 million internet-exposed hosts potentially vulnerable and a Shodan count of over 1.9 million hosts exposing PHP - Tenable. TuxCare relays a count of about 884,000 hosts whose HTTP banner reported PHP 8.1 or older on October 20, about 59% of hosts that reported a version at all, while noting that banners are not proof, because some of those hosts run backported packages - TuxCare. Hunt.io's internet data points the same way: about 1.1 million IP addresses on PHP 7.x, 5.x and 4.x, with roughly 127,000 more on 8.0 and 8.1 - Hunt.io. That banner caveat is the thread that runs through this whole guide: the version a server advertises and the code it actually runs are not always the same thing.
Three WordPress-specific details make the exposure easier to miss than it should be:
- Site Health may not warn you: WordPress.org's PHP check API, queried October 23, still returned
"is_secure": truefor PHP 8.1 - WordPress.org API - Docker's official image:
wordpress:latestis WordPress 7.1.3 on PHP 8.3 with Apache - Docker Hub - Its Apache base runs mod_php: the
php:<version>-apachebase uses mod_php with mpm_prefork - Docker Hub
Each of those is a trap for a careful owner. The first means the absence of a red "update PHP" notice in your dashboard says nothing about PHP2Shell: that API answers "is this branch insecure in general", and two days after the release its data still had not caught up with an event that left 8.1 without an upstream fix. The second and third mean that self-hosted and agency sites built from the official image run Apache with mod_php under prefork, the configuration PHP calls highest-risk and the one Wiz used for its own tests against wordpress:latest - Wiz Research. Why this matters: the sites most likely to be overlooked are not ancient ones, they are fast-moving container deployments that nobody rebuilt. How to apply this: if you run WordPress in Docker, treat "rebuild and redeploy from a fresh image" as part of the patch, as section 7 explains.
4. How Dangerous It Is in Practice: The Honest Range
Coverage this week has swung between "the internet is on fire" and "this is overhyped", and both camps have evidence. A site owner does not need to pick a side, but does need to understand why the two sides disagree, because that disagreement tells you exactly what to prioritize. The short version: PHP2Shell is a severe, real, remotely reachable memory-corruption bug, and it is also much harder to weaponize at internet scale than the famous one-line bugs it gets compared to. Two days after the release, the evidence points to heavy attack traffic and, so far, no confirmed compromise from these two bugs.
The attack traffic is real and large. Amazon says PHP2Shell is now seeing broad exploitation attempts in the wild, with multiple actors hitting its honeypots within hours of the release, while adding that it is unclear whether any of those attempts would have succeeded against real systems - Amazon Threat Intelligence. Cloudflare's CEO reported tens of thousands of attempts within 45 minutes of the announcement, hundreds of thousands within two hours and millions by early October 22 - The Hacker News. Cloudflare's CTO called the company's firewall rules a "STOPGAP only" - The Repository. Pantheon's network mitigations blocked over 60,000 suspicious requests probing the bug in a single day - Pantheon status. The Verge reported problems at 4chan, DeviantArt and Wikipedia as sites went down or into maintenance - The Verge.
The clearest view of what hits WordPress specifically comes from Wordfence, the WordPress security firewall. It blocked more than 74,000 exploit or probing attempts tied to these bugs between October 20 and the evening of October 22, with the peak on release day, as the chart below shows - Wordfence attack data. The largest cluster of requests tried to run commands that read .env and wp-config.php, and others tried to write a PHP shell named wp2s.php into upload, plugin or theme folders, which is exactly what section 10 tells you to look for.
The capability evidence is the part that should keep you patching. CrowdStrike says its own private exploit operated consistently across x86-64 and ARM and stayed reliable across worker restarts - CrowdStrike. VulnCheck built a weaponized exploit for CVE-2026-109618 that works on x86-64 with ASLR enabled and display_errors off, PHP's recommended production setting, and by the evening of October 22 it supported PHP 8.4.10 to 8.4.26 without changes - VulnCheck. SOCRadar found an exposed attacker server holding a "one-click" exploit for PHP 8.4.26 under Apache mod_php, plus a script aimed at five WordPress sites - SOCRadar. Private, working exploits exist; they are simply not yet in the hands of the people spraying the internet.
The sceptical evidence is just as concrete. Sansec updated its analysis on October 22 to say mass exploitation is "less likely than we expected", because an attack needs specific knowledge of the target server, and the public exploits do not work against a default PHP installation; most of them need the target to already have a local file inclusion bug - Sansec research. A widely shared post argued there are "literally thousands" of public proofs of concept that fail against a vanilla PHP server, some of which quietly add a vulnerability to the test server first - Elias Taylor. Red Hat notes that its standard deployments use PHP-FPM, where workers exit on early failures, which makes remote code execution "difficult in practice" - Red Hat. Wordfence achieved code execution only in a specially configured lab build; the same test merely crashed a normal build - Wordfence lab test.
The outcome data so far backs the sceptics. Sophos withdrew its own October 22 report of a successful exploitation: on closer analysis its telemetry showed scanning and a small number of failed attempts, including one that tried to install a Mirai botnet variant called Potassium - Sophos. Hunt.io watched the one mass-exploitation operator it could see run ten passes against up to 123 servers and log zero shells - Hunt.io. Cisco Talos describes most activity as scanning plus "uncoordinated attempts to deliver various payloads" - Cisco Talos. Wordfence adds the caveat that matters: the absence of a confirmed compromise in its data is not evidence that an unpatched server is safe, because the relevant failures happen outside WordPress and its logs.
Why both camps are right becomes clear from first principles. The bugs that produced instant botnets, such as Log4Shell or Shellshock, were logic bugs: one string, sent to any vulnerable server, made it run code, regardless of hardware or memory layout. PHP2Shell is a memory-corruption bug: turning it into code execution depends on the exact layout of a worker's heap, which depends on the exact PHP binary, its build options, the extensions loaded and the state the worker is in. That makes each working exploit expensive to build and specific to a target, which is why public PoCs fail on default installs.
The same reasoning shows where the danger concentrates. Memory-corruption exploits do not need to be universal to scale; they need a monoculture of identical binaries. Ubuntu 24.04's php8.3 package, the official wordpress:latest image and a big host's custom build are each byte-for-byte identical across thousands or millions of servers. An exploit tuned once for one of those binaries works on every server running it, and VulnCheck's exploit is a textbook case: it covers a specific band of builds (8.4.10 to 8.4.26) rather than "PHP" in general. AI lowers the cost of that tuning: Amazon's own conclusion is that AI-assisted analysis makes it cheaper to turn a patch diff into a working exploit, so more actors can act within hours - Amazon Threat Intelligence. And even a failed exploit is not harmless: the realistic floor is worker crashes (downtime) and the memory-disclosure path from section 1.
Why this matters for your plan: "the public exploits don't work" is a statement about today's public code against default builds, not about a determined attacker against the popular binary your site happens to run. How to apply this: patch on an hours timescale, but do not panic-delete or wipe anything; Sansec's October 22 update told store owners they do not need to take their stores offline, while still urging them to update PHP. Better Stack's explainer, watched more than 180,000 times by October 23, walks through why governments took sites offline anyway and what the outages looked like.
The useful takeaway from the outages is about asymmetry of costs. A government portal or a large forum that cannot confirm its PHP build in an hour faces a choice between a few hours offline and an unknown risk of compromise; for them, offline is rational. A small WordPress site on a managed host that patched within the day faces no such trade-off. The rest of this guide is about getting into the second group quickly, starting with the step most owners skip: proving which PHP actually serves the site.
5. Step 1: Find the PHP That Serves Your Site, and Prove It Is Patched
The most common mistake this week is checking the wrong PHP. A server can have several PHP versions installed side by side: the one you get when you type php -v in a terminal (the command-line interpreter), the one PHP-FPM runs for your site, and the one Apache loads as a module. Wordfence warns explicitly not to rely on php -v alone, because the version serving websites can differ - Wordfence. The only PHP that matters for PHP2Shell is the one that answers web requests for your site.
The second mistake is trusting the version number. Linux distributions and hosting vendors usually backport security fixes into the version they already ship rather than jumping to the new upstream release, so a patched server can report a version that looks older than 8.3.36, and an unpatched one can report a number that looks fine if workers were never restarted. AlmaLinux tells its users plainly not to rely on php -v, because "our fixes are backported, so the upstream PHP version number doesn't change" - AlmaLinux. The flowchart below shows how the rest of the patch path depends on who runs your PHP.
Read the version WordPress actually uses
Inside WordPress, go to Tools > Site Health > Info > Server, which shows the PHP version used to render your site - WordPress.org. That tells you the branch, which is the first and most important fact: if it says 8.1, 8.0, 7.4 or anything older, there is no php.net release for you, and the question becomes whether your host or vendor has backported the fix, or whether you need to move branches. If it says 8.2 to 8.5, you then need to know whether the build includes the October 21 fix.
Site Health shows the version string, not the build, so the next check is the build date or the package revision. Hostinger gives its customers a simple rule that works for many hosts: open PHP Info, and a Build Date of October 21, 2026 or later means the fix is in, because Hostinger built the fix into the PHP version each site already runs - Hostinger. If your host offers no PHP Info screen, you can create a temporary phpinfo() page, read the "Build Date" line, and delete the file immediately afterwards, because a public phpinfo page leaks useful detail to attackers.
- Site Health: tells you the branch WordPress runs on
- PHP Info build date: October 21, 2026 or later usually means patched
- Package revision: the authoritative answer on Linux servers
- Host confirmation: the answer on shared and managed hosting
Those four checks form a ladder from quick to definitive. On shared hosting you can usually only climb the first two rungs yourself, and the last rung is a ticket to your host, which is fine: their package revision is the real answer. On your own server, skip straight to the package revision. Either way, the question you are answering is not "what version is this" but "does the code running right now contain the fix", which is why the next subsection matters.
Read the package revision on a server you control
On a Linux server, the package manager knows exactly which fix each build contains. Ubuntu's notice shows why version numbers mislead: on Ubuntu 24.04 the fixed package is 8.3.6-0ubuntu0.24.04.13, and on Ubuntu 22.04 the fixed PHP 8.1 package is 8.1.2-1ubuntu2.28, both of which "look older" than 8.3.36 - Ubuntu USN-8948-1. Liquid Web's cPanel builds carry backports under suffixes like 8.1.34-5 and 7.4.33-19 - Liquid Web status. The changelog, not the number, is the proof.
The commands below show the checks Sansec and AlmaLinux recommend, adapted for the common cases. Replace 8.3 with your branch:
# Debian / Ubuntu: which PHP packages are installed, and does the changelog mention the CVE?
dpkg -l | grep -E '^ii\s+(php|libapache2-mod-php)'
zgrep CVE-2026-109618 /usr/share/doc/php8.3-common/changelog.Debian.gz
# RHEL / AlmaLinux / Rocky: package revision and changelog
rpm -q php-common
rpm -q --changelog php-common | grep CVE-2026-109618
# Is Apache loading PHP as a module (mod_php, the highest-risk setup)?
apache2ctl -M 2>/dev/null | grep -i php # Debian / Ubuntu
httpd -M 2>/dev/null | grep -E 'mpm_|php' # RHEL family
If zgrep or the rpm changelog returns a line, the fix is in the package; if it returns nothing, it is not, whatever the version says - Sansec research. The Apache module check matters too: AlmaLinux notes that output containing mpm_prefork_module plus a php_module line means mod_php, and that AlmaLinux 9 and 10 do not ship mod_php at all.
External checkers, and their limits
Two free tools appeared this week. OpenAI published php2shell-checker, a script (also packaged as Codex skills) that reads the PHP version a site advertises in its X-Powered-By or Server headers, or from the local installation when you have server access, and compares it with the fixed releases - OpenAI php2shell-checker. Its own README is candid: HTTP headers cannot tell whether a version contains a backported fix, missing or hidden headers return UNKNOWN, and "Even OK only describes the advertised version - it doesn't verify the installation!" Most WordPress hosts hide the header, so expect UNKNOWN often.
Assetnote's php2shell-scanner takes the opposite approach: it actively probes the site with multipart requests nested 62 levels deep, growing from 1 MiB to 20 MiB, and reports a site vulnerable if PHP throws a memory-exhaustion error or a worker fails - Assetnote php2shell-scanner. That is closer to ground truth, but it deliberately pushes PHP to exhaust memory and sends about 29 MiB per tested path, so run it only against sites you own, preferably staging first. Why this matters: no outside tool can prove a fix, only disprove one. How to apply this: use the package revision or your host's written confirmation as the proof, and treat scanners as a second opinion.
Restart, or the fix is not live
Installing a patched package does not change the code inside PHP workers that are already running. Every source in this incident repeats the same instruction: after updating, restart PHP-FPM or Apache so every worker loads the new code, and rebuild containers from an updated image - Wordfence. On managed hosting the host should have done this, but "has PHP been restarted?" is a fair question to include in your ticket, because a long-running worker can keep old code until it is recycled. Kinsta even told customers that switching to a lower PHP version and back would apply the patched build immediately, which is a restart in disguise - Kinsta status.
With the branch, the build and the restart confirmed, you know which of the next sections applies. If you are on 8.2 to 8.5 and patched, skip to section 8 to plan your version for 2027. If you are on 8.2 to 8.5 and not patched, section 6 or 7 gets you there in minutes. If you are on 8.1 or older, you need either a backport or a version change, and both paths are covered below.
6. Step 2: Update PHP on Your WordPress Host, Panel by Panel
Most WordPress sites do not run on a server their owner manages. They run on shared or managed hosting, where the host installs PHP, applies patches and decides which versions you may choose. On those plans the PHP2Shell fix is mostly the host's job, and the large hosts did that job within hours. What remains your job is twofold: confirm the host has actually patched the build serving your plan, and, if your site is on 8.1 or older, switch it to a supported branch using the host's panel.
The speed differences between hosts were real but not dramatic. Measured from PHP's release at 14:00 UTC, Pantheon reported every live site on supported PHP patched at 19:49 UTC - Pantheon status. 20i finished, older versions included, by 20:01 UTC, and WP Engine finished deploying patches at 01:19 UTC the next day - The Repository. Hostinger completed its rollout at 06:44 UTC - Hostinger. Kinsta marked its incident resolved at 08:26 UTC with all versions from 7.4 to 8.5 patched - Kinsta status. Nearly all of them kept firewall rules in place during the rollout, so these numbers measure the real fix, not the first line of defence.
Read the chart as a measure of operational capacity, not of safety on the day: every host in it had mitigations in front of customers before or at release. The interesting comparison is with a self-managed site, where the same work (validate a build, roll it out, restart workers, confirm) falls on one person, often at night. Even the slowest host in the chart finished in under a day across its whole fleet; that is the service you are buying, and the next subsections show how to check that you are getting it.
Managed WordPress hosts: confirm, do not assume
On fully managed WordPress platforms, you usually cannot and need not touch PHP during an incident like this. WordPress.com told customers there was nothing to do, and that Automattic's platforms (WordPress.com, WordPress VIP, WP Cloud, Pressable and Tumblr) were protected - WordPress.com. Kinsta rolled patched builds for 7.4 through 8.5 while keeping Cloudflare rules and local configuration changes in place "for all sites regardless of patch status" - Kinsta status. Pantheon patched supported versions first, then its long-term-support builds for 5.6 to 8.1 once its upstream vendor shipped backports - Pantheon status.
Managed hosts also differ in how much version control they give you afterwards. WP Engine's PHP Selector lives in the User Portal: open Sites, select the environment, find PHP in the Updates section, click the version number, pick a new one and confirm. WP Engine also offers a "PHP Test Driver" to preview a site on a new version, and it automatically upgrades environments when it retires a version - WP Engine. Kinsta puts the setting under Sites, then the site, then Tools, then PHP settings, with a bulk option for many sites at once and automatic upgrades enabled by default when a version reaches end of life - Kinsta docs.
- WordPress.com, VIP, Pressable: nothing to do; the platform owns PHP
- Kinsta: Sites > site > Tools > PHP settings; auto-upgrades on end of life
- WP Engine: Sites > environment > Updates > PHP version > Confirm
- Pantheon: supported and LTS builds patched by the platform
The pattern across all four is the same, and it is the point of paying for managed hosting: the platform owns the runtime, including the unglamorous work of backporting fixes to old branches you should have left years ago. What these hosts cannot do is make an old plugin run on a new branch, which is why WP Engine and Kinsta both tie version changes to staging tests. If your managed host has patched, your remaining task is the version plan in section 8.
cPanel: the MultiPHP Manager
cPanel is the control panel on many shared hosting plans, GoDaddy's Web Hosting (cPanel) plans among them - GoDaddy help. Patching here happens at two levels. The host (or the server's administrator) updates the PHP packages through EasyApache 4, cPanel's package system; CloudLinux reports that fixed ea-php packages for ea-php52 through ea-php85 were released on October 21 - CloudLinux. You, as the account holder, choose which installed version each domain uses.
That choice lives in cPanel > Software > MultiPHP Manager. Select the checkbox next to the domain, choose a version from the PHP Version menu, and click Apply - cPanel documentation. The menu only lists versions the server has installed; if 8.3 or 8.4 is missing, your host must install it in WHM's EasyApache 4 interface. cPanel's documentation notes that EasyApache 4 supports PHP 7.4 and 8.1 through 8.5, and that the default profile includes 8.1 and 8.2, which is exactly why so many cPanel sites sit on those two branches.
Two cPanel-specific details matter for PHP2Shell. First, a domain marked "Inherited" takes the server's system PHP version, which only the administrator sets, so it changes whenever they change it; pick an explicit version you control instead. Second, because PHP2Shell is worst under mod_php, check whether your domain runs under PHP-FPM: the MultiPHP Manager shows an FPM enabled or disabled indicator per domain, and cPanel describes FPM as maintaining pools of workers that respond to PHP requests - cPanel documentation. GoDaddy said its Managed WordPress hosting was fully patched while its Web Hosting servers were still on mitigations at its last update, and Bluehost warned customers they might see upload-size limits during patching - The Repository.
Plesk
Plesk handles versions per domain. Go to Websites & Domains, find the domain, click PHP, make sure "PHP support" is ticked, choose the version from the drop-down, choose a handler type, and click OK; if the version you need is missing, your host has to add it - Plesk documentation. The handler choice is the PHP2Shell-relevant part: Plesk lets each domain combine a PHP version with a handler type, including a dedicated PHP-FPM handler for stability and isolation, and moving a domain to an FPM handler takes it out of the Apache-module setup PHP rates as highest-risk, without changing your PHP version at all.
Patched Plesk builds were available on release day. Liquid Web's status page lists Plesk 18.0.81.4 builds of 8.5.12, 8.4.27, 8.3.36 and 8.2.35, plus rebuilt end-of-life builds dated October 21, 2026 - Liquid Web status. If you run your own Plesk server, the PHP updates arrive through Plesk's normal update mechanism; if your host runs Plesk for you, the build date in your phpinfo is the quickest proof that it has been applied.
hPanel, Site Tools and other custom panels
Several large hosts run their own panels. In Hostinger's hPanel, open Websites, then Dashboard for the site, then PHP Configuration, pick a version and click Update; the change applies within a couple of minutes to every site on the plan, and versions below 8.2 are no longer selectable - Hostinger support. Hostinger says its patched builds now cover every version from 5.2 to 8.5 on Web, Cloud and cPanel hosting, while VPS plans are managed by their owners, who must update PHP themselves - Hostinger.
In SiteGround's Site Tools, the setting is under Devs > PHP Manager, where you can choose "Managed PHP" (SiteGround picks and updates the version) or set a version manually - SiteGround knowledge base. SiteGround told customers it had rolled out server-level mitigations across all of its servers hours before the release. At DreamHost, go to Manage Websites, click Manage, open the Settings tab, click Manage next to PHP, pick a version and click Change PHP Version; the drop-down still includes unsupported, end-of-life versions, and if the site's code turns out to be incompatible, the panel offers to revert to the previous PHP version - DreamHost. Why this matters: a panel that still offers PHP 7.4 or 8.0 is not endorsing them, it is leaving the choice to you, and this week showed what that choice costs. How to apply this: whichever panel you use, pick an explicit supported version (8.3 or 8.4) rather than "inherit", "default" or whatever the site was created with years ago.
What to ask your host, word for word
When the panel cannot answer your question, a precise ticket gets a precise answer. Wordfence suggests one sentence that covers the essentials, and it is worth sending exactly as written - Wordfence:
Is the PHP service serving my site patched for CVE-2026-109618 and CVE-2026-109619, and has PHP been restarted?
Status pages are useful but general, and many exclude VPS, dedicated and legacy plans, so a ticket about your specific plan is worth sending even if the status page says "resolved". Support agents on the front line often answer "you are protected" by default, because that is what the incident macro says. Asking about the build and the restart separately forces an answer that someone had to look up, and it surfaces the cases where a plan was excluded from the rollout. If your site is business-critical, ask for the answer in writing in the ticket so you have a record of what you were told and when.
- Which build serves my site, and does it contain both fixes?
- Worker restarts: were PHP workers restarted after the update?
- Plan coverage: is my plan included, or is it one I manage?
- mod_php or PHP-FPM: which does my site run?
- Available versions: can I move to 8.3 or 8.4?
The answers sort you into one of three outcomes. If the host confirms a patched build and a restart, you are done with the emergency and can plan your version upgrade calmly. If the host says your plan is self-managed, section 7 is yours. If the host gives a vague "you are protected" without naming a build, treat that as mitigation rather than a fix: the activeset tracker notes that "protected" can mean either a deployed fix or a firewall rule blocking the known attack while the build is still rolling out - activeset. Ask again, politely, for the build.
7. Step 3: Self-Managed Servers, VPS and Containers
If you rent a VPS, a cloud instance or a dedicated server, or you deployed WordPress from a "1-click" image, nobody patches PHP for you. DigitalOcean put it in one line for its 1-Click customers: "Instances you already deployed don't update themselves" - DigitalOcean. AWS says it cannot update running Lightsail instances in place, and that customers must create new instances from updated blueprints and migrate - AWS security bulletin. The good news is that on a supported distribution, the fix is a package update and a restart.
The work divides by what you run. A supported Ubuntu, Debian, RHEL or AlmaLinux release with its own PHP packages got fixes on release day. A server using a third-party PHP repository depends on that repository's maintainer. A server on PHP 8.1 or older needs a backport or a branch change. And anything built from a container image needs a rebuild, because updating a running container changes nothing once it restarts. The subsections below take these in turn.
Ubuntu and Debian
Ubuntu shipped fixes on October 21 for php8.5 on 26.10 and 26.04 LTS, php8.3 on 24.04 LTS and php8.1 on 22.04 LTS - Ubuntu USN-8948-1. That last one is significant: Ubuntu 22.04 users on the distribution's own PHP 8.1 got a free fix, even though php.net ships none for 8.1. Ondřej Surý's widely used repository (deb.sury.org and the ppa:ondrej/php PPA) also published fixed builds for 5.6, 7.4, 8.0 and 8.1, according to Sansec's vendor table - Sansec research.
# Update every installed PHP package (DigitalOcean's recommended command)
sudo apt update
sudo apt install --only-upgrade 'php*'
# Restart whatever serves PHP (use your branch number)
sudo systemctl restart php8.3-fpm # PHP-FPM sites
sudo systemctl restart apache2 # mod_php sites, and Apache in general
# Confirm the web-facing build, not only the CLI
zgrep CVE-2026-109618 /usr/share/doc/php8.3-common/changelog.Debian.gz
Older Ubuntu and Debian releases need paid extended support. Sansec lists Ubuntu Pro for 14.04 through 20.04 (PHP 7.4 on 20.04, 7.2 on 18.04) and Freexian ELTS for Debian 9 to 11 - Freexian ELA-1853-1. Why this matters: a server whose operating system is itself end-of-life usually also runs an end-of-life PHP, so it has two expired layers, not one. How to apply this: if the fix for your box requires a paid add-on, price it against the cost of moving to a current OS release, which also moves you to a current PHP.
RHEL, AlmaLinux and Rocky Linux
AlmaLinux began rolling patched packages out to its production repositories for every PHP stream on AlmaLinux 8, 9 and 10 at 21:15 UTC on October 21, including retired streams such as php:7.2, 7.3 and 8.0 on AlmaLinux 8 and php:8.1 on AlmaLinux 9, with the warning that "future fixes for retired streams are not guaranteed" - AlmaLinux. Red Hat lists RHEL 7 through 10 as affected (RHEL 7 only by CVE-2026-109619), and its bulletin, last updated October 22, said some fixes were available with more in progress - Red Hat. Patchstack's tracker sums up the gap: fixes exist for php:7.4 on RHEL 8 and PHP 8.0 on RHEL 9, but retired streams such as 8.1 on RHEL 9 get none - Patchstack.
# AlmaLinux / RHEL family: refresh metadata, update PHP, restart
sudo dnf clean metadata && sudo dnf upgrade 'php*'
sudo systemctl restart php-fpm # or: sudo systemctl restart httpd (mod_php on EL8)
# Move off a retired stream (AlmaLinux's example uses php:8.2; choose php:8.3 where your release offers it)
sudo dnf module reset php
sudo dnf module enable php:8.3
sudo dnf distro-sync 'php*'
One warning for this family: many servers use Remi's repository for newer PHP versions, and PHP's announcement noted that the volunteer-maintained rpms.remirepo.net would not have updates on release day, advising users to switch to distribution packages or build from source - Patchstack. Sansec's table later listed Remi's repository with fixes for 7.4, 8.0 and 8.1, with 7.2 and 7.3 to follow. If your server pulls PHP from Remi, check the package changelog for the CVE before assuming the update landed.
Branches with no upstream fix: who backported what
For PHP 8.1 and older, the PHP security team published minimal source backports for 7.4, 8.0 and 8.1 as branches in the php-src repository, aimed at people building from source or without an LTS vendor - php-src backport branch. Most site owners will not compile PHP, so the practical question is which vendor ships a ready-made package for your platform. The table below summarizes Sansec's vendor list, with costs as each vendor describes them.
| Platform | Vendor | Branches with a fix | Cost |
|---|---|---|---|
| Debian, Ubuntu | deb.sury.org / ppa:ondrej/php | 5.6, 7.4, 8.0, 8.1 | Free |
| Ubuntu 22.04 | Ubuntu | 8.1 | Free |
| Ubuntu 14.04 to 20.04 | Ubuntu Pro | 7.4, 7.2, 7.0, 5.5 by release | Subscription |
| Debian 9 to 11 | Freexian ELTS | 7.4, 7.3, 7.0 by release | Paid |
| RHEL family | Remi's repository | 7.4, 8.0, 8.1 (7.2 and 7.3 to follow) | Free |
| AlmaLinux 8 to 10 | AlmaLinux | Every PHP stream it ships | Free |
| RHEL 8, 9 | Red Hat | Some streams, others in progress | Subscription |
| cPanel on CloudLinux | CloudLinux ea-php | ea-php52 to ea-php85 | With CloudLinux |
| Any | TuxCare ELS, ZendPHP LTS | 5.x to 8.1 (TuxCare); 7.2 to 7.4 and 8.1 (Zend) | Paid |
Read that table as a bridge, not a destination. A backport fixes this pair of CVEs on an old branch, but it does not fix the dozens of other issues that were patched upstream after that branch's end of life, and there is no guarantee the next critical bug gets the same treatment. AWS, for its part, told customers on end-of-life runtimes 5.4 to 8.1 to "begin migration to a supported, fixed PHP branch immediately" - AWS security bulletin. Use a backport to close today's hole, and put the branch upgrade in section 8 on this month's calendar.
Docker, containers and snapshots
Containers fail in a specific way during an incident like this: updating the base image on Docker Hub does nothing to containers already running, and redeploying an old image tag brings the vulnerable PHP straight back. The official php images received fixed tags such as 8.3.36-apache and 8.3.36-fpm late on October 21 (UTC), and the official wordpress images built on PHP 8.3 were rebuilt early on October 22 - Docker Hub. The php:8.1-apache tag, by contrast, was last updated on December 31, 2025; Sansec notes that the official Docker php image gets no fix for 8.1 or older.
# Pull the rebuilt image and recreate the container (docker compose example)
docker compose pull wordpress
docker compose up -d --force-recreate wordpress
# Confirm the PHP version inside the running container
docker compose exec wordpress php -v
# For your own images: pin to a fixed release, rebuild without cache, redeploy
docker build --pull --no-cache -t mysite:php8.3.36 .
The same logic covers snapshots and backups. DigitalOcean warns that restoring a snapshot, backup or custom image taken before you patched "brings back the vulnerable PHP", and that images sitting in a container registry stay vulnerable until rebuilt and pushed - DigitalOcean. Why this matters: container users often believe they are immune to "server patching", when in fact they have moved the patch into the build pipeline. How to apply this: rebuild every image that contains PHP, redeploy every service, and add a note to your restore runbook to patch PHP immediately after any restore from before October 21.
Cloud platforms and serverless
Platform services split along one line: whether the provider manages the PHP base image for you. Google App Engine standard applications on PHP 8.2 and newer were updated automatically, while App Engine flexible, Cloud Run services without automatic base-image updates, and anything on PHP 8.1 or earlier must be rebuilt or upgraded - Google Cloud. AWS Elastic Beanstalk released updated PHP platform versions for every supported branch (8.2 to 8.5), which arrive automatically only for environments with Managed Platform Updates enabled; everyone else must update the platform version themselves - AWS security bulletin.
Vercel deployed WAF rules for all projects at no cost, covering PHP functions on Vercel and requests it proxies to external PHP backends, but it states that the runtime is still yours to upgrade and that direct requests to an external backend bypass those rules - Vercel. That last point applies to every edge firewall, including Cloudflare: if attackers can reach your origin server's IP directly, the WAF never sees them. Locking the origin to accept traffic only from your CDN is the step that turns a WAF from decoration into a real (if partial) control.
8. Step 4: Pick a PHP Version That Lasts, Without Breaking the Site
Patching gets you through this week. Choosing the right branch gets you through the next incident. The calendar is the first constraint: on php.net's support schedule, PHP 8.2 receives security fixes until December 31, 2026, 8.3 until December 31, 2027, 8.4 until December 31, 2028 and 8.5 until December 31, 2029 - PHP supported versions. A site that patched 8.2 to 8.2.35 this week will be back in the unsupported column in ten weeks, which is why Hostinger, for one, told customers to pick 8.3 or newer when they upgrade, and why CloudLinux flagged 8.2's end of life in the first lines of its advisory.
The official chart makes the point visually. The red rows are branches that will never get another fix, and the dashed "today" line sits about two months from the end of PHP 8.2.
The second constraint is WordPress itself. WordPress recommends hosts run PHP 8.3 or greater, still runs on PHP 7.4 for legacy environments, and warns that those older versions "may expose your site to security vulnerabilities" - WordPress requirements. The core team's compatibility table lists WordPress 7.1 and 7.0 as compatible with PHP 7.4 through 8.5 - WordPress core handbook. WordPress core is therefore never the blocker for moving to 8.3 or 8.4; the blockers are themes, plugins and custom code.
Choosing between 8.3, 8.4 and 8.5
For most sites the decision comes down to how well maintained your plugins are. PHP 8.3 is WordPress's recommendation and the most widely run branch among WordPress sites (27.0% in the chart in section 3), which means most plugin authors test against it; it is the safe default for a site jumping from 7.4, 8.0 or 8.1.
PHP 8.4 buys an extra year of support and suits sites whose plugins are actively maintained. PHP 8.5 is the newest supported branch, with the longest runway, but it also carried an extra confirmed attack path for CVE-2026-109619 through its new fatal-error backtraces feature (php.net's stopgap for it, fatal_error_backtraces=0, applies to 8.5 only). It is fixed in 8.5.12, but it is a reminder that newer is not automatically safer in the first months. Whatever you pick, pick a branch with at least a year of support left, so the next emergency finds you on a version that gets a fix rather than one that does not.
PHP 8.6 is the one to watch, not to install. Its fourth release candidate, published the same day as the fixes, already contains them, but php.net is explicit that release candidates are test builds not meant for production - PHP 8.6.0 RC4. The PHP project's timetable targets general availability on November 19, 2026 - PHP 8.6 timetable. For a site moving off 8.1 this week, that is a reason to land on 8.3 or 8.4 now and consider 8.6 once hosts and plugin authors have caught up, not a reason to wait.
- PHP 8.3: the safe default; supported to end 2027
- PHP 8.4: an extra year; best with maintained plugins
- PHP 8.5: longest runway; newest code, smallest install base
- PHP 8.2: only as a stepping stone; dies December 31, 2026
There is a reasonable case for a two-step move from very old versions. Going from 7.4 straight to 8.4 crosses five releases of breaking changes at once, so a failure is harder to diagnose; going to 8.3 first, fixing what breaks, and moving to 8.4 next year splits the risk and keeps each change small enough to roll back. Hostinger already defaults new sites to 8.3 while offering 8.2 to 8.5, which is a sensible signal of where the large hosts see the safe middle - Hostinger support.
What actually breaks, and why
Knowing what changed between branches tells you where to look when something fails. PHP 8.0 turned several notices into warnings and made passing an invalid value to count() throw a TypeError, a classic cause of white screens in old plugins - PHP 8.0 migration notes. PHP 8.2 deprecated dynamic properties, utf8_encode() and "${var}" string interpolation - PHP 8.2 deprecations. PHP 8.4 deprecated implicitly nullable parameters and the E_STRICT constant - PHP 8.4 deprecations.
The distinction between a deprecation and an incompatibility matters for planning. A deprecation produces a warning in the log and keeps working, so a plugin full of 8.2 deprecations will usually still run on 8.3; an incompatibility (a removed function, a new TypeError) stops the page. Most of the white screens owners fear come from the 7.x to 8.0 boundary, not from 8.2 to 8.4. So if your site already runs 8.0 or 8.1 cleanly, moving to 8.3 is usually a small step; if it runs 7.4, the 8.0 boundary is the real test.
Test before you switch: tools that still work
The plugin many guides still recommend, WP Engine's PHP Compatibility Checker, now carries a warning on WordPress.org that it is no longer actively maintained, and it was last tested with WordPress 6.4 - WordPress.org plugin directory. Its engine lives on as the open-source PHPCompatibilityWP ruleset for PHP_CodeSniffer, which knows which functions WordPress itself polyfills and so avoids false alarms - PHPCompatibilityWP. Running it takes a developer machine with Composer and a copy of your wp-content folder:
# In a copy of your site's wp-content (or one plugin's folder)
composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
composer require --dev phpcompatibility/phpcompatibility-wp:"^3.0@dev"
# Check everything against PHP 8.3 and newer
vendor/bin/phpcs -p . --standard=PHPCompatibilityWP --runtime-set testVersion 8.3- --extensions=php
Static analysis catches the obvious problems, but it cannot run your checkout or your contact form, so pair it with a staging test. Kinsta, WP Engine and most managed hosts offer one-click staging copies; on cPanel or Plesk you can clone the site to a subdomain and set that subdomain's PHP version separately. Sucuri's checklist of what to click through after a version change is a good minimum: logins, checkout, forms, uploads, scheduled tasks, APIs and admin pages - Sucuri. Turn on WordPress debug logging on staging so deprecation warnings land in wp-content/debug.log instead of on the page.
If the scan turns up problems in custom code, this is a good job for an AI coding agent working on a branch of your theme or plugin: the fixes are mechanical (declare properties, add nullable types, replace utf8_encode()), and the scan output is a ready-made task list. Our guide to running Claude Code unattended covers how to let an agent work through a list like this safely, and a shared AGENTS.md file is a simple way to tell any coding agent which PHP version the code must support. Why this matters: the fear of breaking the site is the single biggest reason 34.7% of WordPress sites are still on unpatched branches. How to apply this: make the version change a tested, reversible step, then do it.
Switch, watch, and know how to roll back
Every panel covered in section 6 can switch back as easily as it switched forward; DreamHost goes furthest and offers a "revert to previous PHP version" option when its check finds the site incompatible with the new version - DreamHost. Switch during a quiet hour, load the pages that make you money, watch the error log for fifteen minutes, and keep the old version as a known escape route for the day.
The one thing not to do is roll back to an unpatched build. If 8.3 breaks a plugin and you return to 8.1, make sure the 8.1 you return to is your host's backported build, then fix or replace the plugin before trying again. A rollback is meant to be a pause, and the next section covers what to do if even that pause has to be bridged with stopgaps.
9. If You Cannot Patch Today: Stopgaps and Their Limits
Sometimes the fix is not available yet: your host is still rolling out builds, your vendor's backport is a day away, or the box runs software that cannot move off PHP 7.4 this week. For those hours, PHP's advisory and the security vendors offer a set of stopgaps. Every one of them comes with the same warning from PHP itself: these measures "reduce exposure to specific attack paths but do not make an installation safe" - PHP security advisory. They are a bridge to the patch, not a substitute for it.
It helps to know what each stopgap actually blocks, because the advice circulating online has been muddled. Alexandre Daubois of the PHP security team points out that the most repeated tip, turning off session.upload_progress, belongs to only one of the two bugs (CVE-2026-109619), while the stopgap the advisory puts first for the other bug, enforcing memory_limit=-1, appeared in none of the write-ups he read - Alexandre Daubois. The subsections below match each measure to the bug it addresses.
Edge firewalls: Cloudflare and others
If your site already sits behind Cloudflare, you have had protection since 03:02 UTC on October 21 without doing anything: seven rules named "PHP Remote Code Execution Attack" shipped in the Cloudflare Managed Free Ruleset for Free plans and seven more in the Cloudflare Managed Ruleset for paid plans, alongside a new full transaction detection capability whose response rules can block a matching origin response before it reaches the requester, deployed on every plan including Free - Cloudflare. Paid-plan customers should confirm the Managed Ruleset is deployed in block mode, and anyone can review matches under Security > Security Analytics > Events. Hosts running Imunify360 with its WAF enabled receive rulesets 8.83 to 8.88 automatically, which block the known exploit requests and the public exploit tool - CloudLinux.
Firewall rules have two structural limits. First, they match known payloads, and Cloudflare itself says it watched attackers mutating payloads to slip past the rules, which is why its CTO called them a stopgap. Second, they only see traffic that flows through them: anyone who learns your server's real IP address can skip the CDN entirely. Vercel spells this out for proxied PHP backends, recommending that origins accept only traffic from its network - Vercel. How to apply this: keep the WAF on, and restrict your origin's firewall to your CDN's IP ranges so the WAF cannot be bypassed.
PHP configuration mitigations
If you control the server's PHP configuration, the advisory lists settings that close the confirmed attack paths. They must be set where they apply before request parsing (php.ini or the FPM pool), never through ini_set() or a .user.ini file, and every worker must be restarted afterwards:
; php.ini (mod_php) or, as php_admin_value / php_admin_flag, in every FPM pool
; CVE-2026-109619: block the confirmed session upload-progress path
session.upload_progress.enabled = 0
; PHP 8.5 only: block the confirmed fatal-error backtrace path
fatal_error_backtraces = 0
; CVE-2026-109618: remove the controllable memory-limit failure
memory_limit = -1
; Keep request bodies small: 8M without uploads, 32M with them
post_max_size = 8M
upload_max_filesize = 6M
Each line has a cost. With memory_limit = -1, a single runaway request can consume all of the server's memory, so the advisory pairs it with a container or cgroup limit that kills only the affected worker, and it warns specifically against RLIMIT_AS or RLIMIT_DATA, which can trigger the vulnerable failure instead of preventing it - GitHub advisory GHSA-w8hw-w9fx-j79w. PHP core developer Gina Peter Banyard warned that disabling session upload progress alone is not enough - Sansec research. And post_max_size limits only help if the web server also enforces them for chunked or length-less requests.
One of these lines should outlive the patch. On October 23 PHP published an informational advisory for a third issue, CVE-2026-115560, and suggested keeping session.upload_progress.enabled=0 until the next regular bugfix release, which will carry the fix and a full advisory - PHP informational advisory. The risk is narrow: it needs an application that sets session.serialize_handler to php_binary or php_serialize in its configuration, then switches it to php at runtime with ini_set() before calling session_start(), with upload progress still enabled. PHP calls that setup unlikely in practice, which is why the fix waits for the regular release rather than another emergency. The setting only turns off PHP's built-in, session-based upload progress tracking, so if you applied it on October 21, leave it in place; if you patched and then removed it, put it back. Hostinger, for one, told its VPS customers to set it and restart every PHP worker, while saying its managed plans were already covered by the changes it made on October 21 - Hostinger.
There is also a WordPress-specific clean-up hidden in the advisory. On mod_php, php_value and php_flag lines in .htaccess are applied before PHP initializes the request, and a setting that is invalid or deprecated emits a diagnostic at exactly the point where CVE-2026-109619's stale state can be consumed; hiding errors does not help - GitHub advisory GHSA-6wmf-4rv5-p3rp. Years of copy-pasted WordPress tutorials have left many sites with old php_value lines in .htaccess. Removing any that reference settings your PHP version no longer supports is cheap hygiene that also closes one consumer path.
Blocking multipart requests, and its side effects
Every known trigger arrives as a multipart/form-data request, the format browsers use for file uploads, which PHP parses before WordPress runs. Sansec's most direct stopgap is to reject such requests at the web server - Sansec research:
# nginx: add to each server block that passes requests to PHP, then: nginx -t && systemctl reload nginx
if ($http_content_type ~* "multipart/form-data") {
return 403;
}
# Apache: add to each VirtualHost that serves PHP, then reload Apache
RewriteEngine On
RewriteCond %{HTTP:Content-Type} multipart/form-data [NC]
RewriteRule ^ - [F]
On a WordPress site this rule has obvious side effects: it breaks media uploads in the admin, contact forms with attachments, and some checkout and account forms that post as multipart. Sansec tells store owners to place a test order after adding it and to remove it once PHP is updated. Why this matters: it is the bluntest stopgap and the most effective one, because it removes the attack's delivery format rather than chasing payload signatures. How to apply this: use it for hours, not days, on sites that can live without uploads, and pair it with a note to yourself to remove it.
Taking the site offline, and why maintenance mode is not enough
The most conservative advice came from governments. CISA told organizations that cannot update, or that run PHP 8.1 or earlier, to take PHP-based systems offline until patched, and the UK NCSC said organizations "should consider taking the system offline until it can be fully mitigated", advice the WordPress project relayed to owners who manage their own hosting - WordPress.org. Several Dutch government and media sites, DigiD and Tweakers among them, did exactly that on October 21 - Patchstack.
There is a trap here for WordPress owners: a maintenance-mode plugin does not take the site offline in the sense that matters. The maintenance page is generated by WordPress, which means every request still reaches PHP, and PHP parses the multipart body before WordPress decides to show the maintenance page. The attack surface is untouched. To actually go offline, stop serving PHP at a layer in front of it: a static holding page at the web server, a CDN rule that blocks requests to the origin, or stopping PHP-FPM entirely. If your host has confirmed a patched build and a restart, none of this is necessary; Sansec's October 22 update went further and told store owners they do not need to take their stores offline at all, while still urging them to update PHP - Sansec research.
10. After the Patch: Check Whether You Were Hit
Patching stops new attacks; it does not undo old ones. Because exploitation attempts were recorded from October 17, Amazon recommends starting investigations from the earliest known exploitation rather than the disclosure date, and it frames the lesson bluntly: for critical internet-facing bugs, "measure patch and mitigation timelines in hours" - Amazon Threat Intelligence. Wordfence asks site owners to preserve web, PHP, firewall and system logs covering at least the 48 hours before the release through the moment PHP was patched and restarted - Wordfence.
The difficulty is that PHP2Shell happens below WordPress, so WordPress's own logs and most security plugins see little. Wiz suggests looking for unusual multipart requests followed by memory-limit errors or repeated HTTP 500 responses, unexpectedly large or malformed PHPSESSID cookies, and unexplained PHP worker churn - Wiz Research. CISA adds a host-level signal: PHP worker processes launching shells or downloading tools - CISA alert. None of these proves compromise, and their absence does not prove safety, but they tell you where to look first.
A practical check, in order
Sucuri's list of things to review after the patch is a sensible order of work: unfamiliar PHP files, unexpected scheduled tasks, unknown administrator accounts, suspicious outbound connections, and unusual upload-related requests - Sucuri. On a WordPress site with shell access and WP-CLI, most of that takes a few commands:
# 1. Memory-limit fatals around the window (log paths vary by host)
grep -h "Allowed memory size" /var/log/php*-fpm.log /var/log/apache2/error.log 2>/dev/null | tail -50
# 2. PHP files created or changed since October 17, especially in uploads
find wp-content -name "*.php" -newermt "2026-10-17" -print
find wp-content/uploads -name "*.php" -print
# 3. Administrators and scheduled events you do not recognise
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered
wp cron event list
# 4. Core files that differ from the official release
wp core verify-checksums
A PHP file inside wp-content/uploads deserves immediate attention: WordPress stores media there, not code, and a web shell dropped there is the classic sign of a successful upload-path attack. A new administrator account created between October 17 and your patch time, or a cron event pointing at an unfamiliar function, is a strong sign of compromise. If you find either, follow WordPress's own guide for hacked sites, and assume the attacker had the same access as the website itself - WordPress.org hacked-site FAQ.
Search for the markers attackers actually used
General checks find general problems, and this week also produced specific fingerprints worth a few minutes. Wordfence published the non-IP indicators it saw in attack traffic against WordPress sites, including the command-execution markers P2S_OK, NxSP followed by digits and SHL_OK, and the shell file names wp2s.php, php2shell.php and axiom_*.php - Wordfence attack data. VulnCheck recorded a different technique: an attempt to plant a web shell inside a server-side session file through the upload-progress field, then read it back through a file-disclosure bug, which failed because the path did not exist - VulnCheck.
Both leave traces you can search for. The file names are the most useful check because they survive on disk, while the markers live in request bodies, which ordinary access logs do not record:
# 1. Shell file names seen in PHP2Shell attack traffic (run from the site root)
find . \( -name "wp2s.php" -o -name "php2shell.php" -o -name "axiom_*.php" \) -print
# 2. PHP code inside session files (use session.save_path from your phpinfo page)
SESSION_DIR=/path/from/session.save_path
grep -l "<?php" "$SESSION_DIR"/sess_* 2>/dev/null
# 3. Markers in request-body logs, if a WAF or ModSecurity audit log keeps them
grep -E "P2S_OK|NxSP [0-9]+|SHL_OK" /path/to/your/waf-or-audit.log
Read the results with the same care Wordfence applies to its own data. A marker in a log shows an attempt, not a success, and the overwhelming majority of this week's attempts failed. A shell file on disk, or PHP code inside a session file, is different: it means something got written, and that site should be handled as compromised using the hacked-site steps above. Why this matters: specific indicators turn a vague worry into a yes-or-no check. How to apply this: run the first two commands on every server that was unpatched at any point after October 17, and keep the output with your incident notes.
Rotate the secrets the bug could have leaked
The memory-disclosure path changes the calculation even if you find no web shell. Sansec says leaked worker memory can contain passwords, session tokens, API keys and even TLS private keys from other requests - Sansec research. An attacker who read memory rather than ran code leaves almost no trace on disk, so if your server sat unpatched and exposed during the window, treat the secrets PHP handled as possibly read.
That does not mean every site must rotate everything. If your host patched within hours and had firewall rules in front of you from before the release (which describes most managed hosts in section 6), the window was short and filtered, and rotating the highest-value keys is a reasonable precaution rather than an emergency. If you ran your own server on mod_php, sat on PHP 8.1 or older without a backport, or saw the warning signs above in your logs, rotate the full list below. The order matters: change the credentials that can move money or send email in your name first, because those are what attackers monetize fastest. This week's traffic also shows what attackers reach for first: the largest cluster Wordfence saw tried to read .env and wp-config.php, the files that hold a site's database credentials and keys.
- Database password in wp-config.php, changed at the host and in the file
- WordPress salts, regenerated with
wp config shuffle-salts, which logs everyone out - Plugin API keys: payments, email delivery, CRMs, cloud storage
- Admin passwords, and any application passwords
- TLS certificate key, reissued if PHP ran inside Apache (mod_php)
That last item follows from how the setups differ. Under mod_php, PHP runs inside the same Apache processes that terminate TLS, so a memory leak from those processes is the case where a private key is most plausibly exposed; under PHP-FPM, PHP runs in separate worker processes. Regenerating salts is documented in WP-CLI's own reference - WP-CLI. Why this matters: rotating secrets is cheap compared with discovering a month from now that a payment key leaked. How to apply this: prioritize the keys that can move money or send email in your name, and record what you rotated. Our security checklist for AI-built apps covers secret storage and rotation habits that make this step faster next time.
11. The Bigger Lesson: Who Owns the Runtime Under Your Site
Step back from the commands and PHP2Shell tells a simple structural story. A website is a stack of layers: your content, the CMS, its plugins and theme, the language runtime, the operating system and the network edge. Each layer has an owner who must patch it. WordPress owners have been trained for a decade to own the top three layers (keep core and plugins updated), and most never think about the runtime, because on shared hosting someone else owns it. PHP2Shell is a bug in exactly that forgotten layer, which is why a fully updated WordPress install could still be wide open.
The economics of that layer are changing. The PHP Foundation describes security work on PHP itself as resting on "a small group of foundation-funded developers and a little volunteer help", while AI-assisted research multiplies the reports they receive - PHP Foundation. OpenAI says these bugs are "among several significant vulnerabilities" it has already found and that it expects "similar findings to increase sharply in the coming months" - OpenAI. PHP is not special here: Node.js shipped its own security releases the same day - Node.js. Runtime emergencies are becoming a routine operational event, not a once-a-decade one.
If patch windows are now measured in hours, every site needs a clear answer to "who patches the runtime, and how fast?". There are three honest answers, and each has a price:
- Rent the ownership: a managed host patches PHP for you, as this week showed, in roughly 6 to 18 hours
- Automate the ownership: automatic security updates, scheduled image rebuilds and monitoring on your own servers
- Change the runtime: move to a stack with a smaller or better-managed surface, accepting that it has its own
Each of these is a legitimate choice, and the wrong answer is only the implicit one: owning the runtime by default without knowing it. That is the position of every site owner who installed WordPress on a VPS from a tutorial years ago, or deployed a 1-Click image and never looked again, and it is the position that turned this week into an emergency for them rather than a status-page update. The PHP Foundation's request is also worth hearing here: it asks the companies, hosts and public institutions that depend on PHP to fund the people who fix it, so that the next structural problem is resolved before it becomes an emergency - PHP Foundation sponsorship.
The first option is what most small sites should choose, and the chart in section 6 is the evidence: even the slowest host in it patched its whole fleet within a day, and the managed platforms had firewall rules in front of customers before the release. The second option suits teams with an operations habit; automatic security updates on Ubuntu or Debian can install a fixed package within a day of its release, but they may not restart every PHP worker and they never rebuild your containers, so automation has to cover the whole chain. The third option is a bigger decision that this week should inform but not rush, and it is the subject of our AI website builders market map and our guide to when to graduate from a vibe-coding tool.
Changing the runtime moves the duty rather than removing it. A static or headless front end shrinks the PHP surface but usually keeps a PHP or Node.js backend somewhere. Software built from scratch with AI coders runs on whatever stack they chose, and that stack has its own advisories. Founden, for example, builds its companies as Next.js apps with a Python backend, and when it checked the companies it runs after this advisory, none ran PHP; the Node.js security release that landed the same day was theirs to take instead, which is the same duty in a different runtime rather than no duty at all. If you are weighing that kind of move, our guides to where to deploy your app and the AI-native company tech stack compare who owns which layer on each platform, and building and deploying with Claude Code shows what the self-built route involves.
There is also a cost question that rarely gets asked until a week like this one. Running your own VPS looks cheaper than managed hosting on the invoice, but the difference is paying for exactly the work this guide describes: watching advisories, validating builds, restarting workers, rebuilding images and checking logs, at any hour. For a site that earns money, one night of that work can cost more than a year of the price difference. Why this matters: the cheapest-looking option silently assigns you the job of security operations. How to apply this: write down, for each site you run, who patches PHP and how they would tell you, and if the honest answer is "nobody", fix that before the next advisory rather than during it. And this was not the only forced platform change this week for small teams: OpenAI switching off GPT-4 and other older models on October 23 is another deadline that falls on whoever owns the integration, as our GPT-4 deprecation guide explains.
12. Conclusion: A Decision Framework for This Week
PHP2Shell is serious for a reason that has nothing to do with WordPress and everything to do with where it sits: in the PHP engine, below every plugin and update you control from the dashboard. The bugs are real, remotely reachable and capable of leaking secrets even when they do not yield code execution. They are also harder to weaponize at scale than the headlines suggested, which means calm, fast, verified patching beats panic. The order of work is the same for every site; only who does each step changes.
This hour: find the PHP that serves your site (Site Health, then the build date or package revision), and confirm with your host or your package manager that the build contains the October 21 fix and that PHP was restarted. If it does not and the fix is not available yet, keep Cloudflare or your host's WAF on, apply the advisory's configuration stopgaps, and, if you are on PHP 8.1 or older with no backport in sight, consider taking the site offline at the server or CDN layer rather than with a maintenance plugin. If you manage PHP yourself, leave session.upload_progress.enabled=0 in place even after patching, as PHP's October 23 advisory asks.
This week: if you patched in place on 8.2, or you are on 8.1 or older, move to PHP 8.3 or 8.4 after a staging test with PHPCompatibilityWP and a click-through of the pages that matter. Check your logs back to October 17, look for PHP files in uploads, the shell names attackers used (wp2s.php among them) and unknown administrators, and rotate the secrets your PHP workers handled if your server was exposed during the window.
This quarter: decide who owns your runtime. If the answer is "me, when I remember", either automate it properly or rent it from a host that proved this week it can patch within a day. PHP 8.2's end of life on December 31, 2026 is the next deadline on that calendar, and, judging by how quickly AI is now finding bugs in old code, it will not be the last emergency before it.
This guide reflects the PHP2Shell situation as of October 23, 2026. Host patch status, vendor backports, exploitation activity and PHP support dates change quickly, so verify current details with your host, your distribution's security notices and the official PHP advisory before acting.