Introduction
Three national agencies said the same thing on 21 October: patch PHP or take the system offline. That is unusual language for a PHP2Shell patch advisory. It tells you how little room this one leaves you.
TLDR
Update to PHP 8.2.35, 8.3.36, 8.4.27, or 8.5.12, then restart every PHP worker, because a running process keeps the old code until it restarts. PHP 8.1 and older get no official release, so those hosts need a vendor package, a migration, or isolation. Configuration tweaks and WAF rules buy time and do not close the hole. PHP says it plainly: there are no complete PHP2Shell mitigation steps, and updating is the only safe choice.
PHP2Shell Fixed versions at a glance
Four branches got a release on 21 October. Everything older is a different conversation.
Four branches got a fix. Everything older did not.
Released 21 October 2026. Find your branch, then check the support column, because that decides whether this is a patch or a project.
Two things the version column will not tell you. A release candidate such as 8.5.12RC1 predates the fix and is still vulnerable. And a distribution backport fixes the code while keeping the old version string, so on a packaged install the changelog is the answer and the banner is not.
Release candidates are not fixed builds. An 8.5.12RC1 tag predates the release and still carries the flaw.
PHP2Shell Patch by platform
Start with hosts running Apache and mod_php. PHP rates that setup as highest risk, because one unauthenticated request reaches the engine before any application code runs.
Update, then restart. Per platform.
Patch Apache with mod_php first. One unauthenticated request reaches the engine there before any application code runs.
apt update && apt upgradesystemctl restart php8.3-fpmdnf update php\*dnf module list php first to see which stream is enabled.systemctl restart php-fpm httpddocker compose build --pulldocker compose pull on generic tags. Edit pinned tags by hand.docker compose up -dthe panel's PHP updateper branch, every poolofficial Win32 buildthe web serverread the provider bulletinnew instance, then migratesudo grep -lE '(php|libphp).*\(deleted\)' /proc/[0-9]*/maps 2>/dev/null
This lists every process still mapped to a PHP binary that has already been replaced on disk. When the restart is complete, it prints nothing.
Then go find the PHP you forgot. Admin panels, monitoring tools, firewalls, phone systems, webmail, file sync, database admin tools, helpdesks, wikis and learning platforms all ship PHP, often on ports nobody scans and sub paths nobody remembers.
Ubuntu and Debian
Install the PHP security update from your distribution, then restart PHP-FPM or Apache.
Your version string may not move. Distribution maintainers often backport the fix and keep the existing number, so confirm the package changelog names both CVE IDs rather than reading the banner.
RHEL, Rocky, AlmaLinux and Amazon Linux
Update the PHP packages your repository ships, then restart the service.
On EL8 and EL9, check which module stream is enabled before you update, because a host can have a newer PHP installed and still serve an older stream.
Docker and containers
A running container keeps the image it started from, so a pull alone changes nothing.
For generic tags, run docker compose pull, and then docker compose up -d. For images you build yourself, run docker compose build –pull so the cached base layer is not reused. If your Dockerfile pins a tag such as FROM php:8.4.27-fpm, you have to edit that line by hand, because a pinned tag never moves.
Then recreate the containers and read the version again. Images that install PHP through apt or apk depend on distribution packages, so they need a rebuild once those packages land.
Windows
Official Win32 builds for 8.2 through 8.5 are published for x64 and x86, in thread-safe and non-thread-safe variants. Replace the build, then restart the web server.
Watch for standalone binaries. A bundled php.exe or php-cgi.exe inside a vendor application is easy to miss, and nobody patches it.
cPanel, Plesk and CloudLinux
On a panel server, the panel’s update is the fix. Control panels ship their own PHP builds, so distribution updates don’t touch them.
The panel change log covers supported branches and backports the fix to end-of-life ones, one of the few places those get a binary.
One trap: on at least one panel, the patched end-of-life builds carry the same version number as the vulnerable ones and differ only by build date. Read the build date, not the version.
On CloudLinux, a PHP2Shell security-only update may not list the alt-php packages. Run a regular update instead.
Managed platforms and appliances
Check your provider’s bulletin before you do anything, because the answer differs by service even inside one cloud. Some managed platforms apply the fix for you. Some cannot patch a running instance at all and expect you to launch a new one from an updated image and migrate. Custom runtimes and container images are always yours to update.
Then go looking for the PHP you forgot. Admin panels, monitoring tools, firewalls, phone systems, webmail, file sync, database admin tools, helpdesks, wikis,, and learning platforms all ship with PHP, and they often run for years without anyone updating them.
Restart every PHP worker
Updating the package writes new files to disk. It does not change the code inside a process that is already running.
Restart PHP-FPM pools, Apache, LiteSpeed and any queue or cron workers that load PHP. Recreate containers rather than restarting them.
One command tells you whether you missed anything. This lists any process still mapped to a PHP binary that has been replaced on disk, and it should print nothing when you are done:
sudo grep -lE '(php|libphp).*\(deleted\)' /proc/[0-9]*/maps 2>/dev/null
Proving the restart actually took across a fleet is a bigger job than running the command once.

If you are on end-of-life PHP
PHP 8.1, 8.0, and 7.4 will receive no further releases from the project. The security team published source-only backport branches for people who build from source, with no tarball, no binary, and no version bump.
Vendors provide binaries on their own schedules, and coverage is uneven. Extended-support vendors, some distributions, and the control panels ship patched builds for these branches. Others have published no fix at all, and at least one major cloud distribution lists its old PHP packages as no fix planned. One popular hosting platform says outright that no PHP2Shell patch exists for 8.0 or 8.1 on its service.
No official release. Three paths, pick one.
PHP published source only backport branches for these versions. No tarball, no binary, no version bump. So the decision moves to you.
- Extended support vendors, some distributions and the control panels shipped builds for these branches
- The version number may not change, so confirm by changelog or build date
- Buys you time. It does not put you back on a supported branch
- Check coverage first: some vendors published no fix at all for their old PHP packages
- The only option that ends the cycle instead of extending it
- Needs application testing, so it is a project with a date, not a patch window
- Target a branch with real support life left, not the next one up
- Say the date out loud. An undated migration is how a host reaches 7.4 in the first place
- This is what three national agencies recommended for hosts that cannot be patched
- Isolate the internet facing application rather than shutting the service down, where you can
- Remember the ways traffic skips your edge: direct access on the origin IP, and DNS only hostnames pointing at the same server
- Pair it with a dated plan, or it quietly becomes permanent
Pick before you promise. These paths cost different things and only one of them removes the problem. Decide which one each host is on, write it down with an owner and a date, and you can answer the question an auditor will actually ask.
So you have three paths. Take a vendor backport if your vendor has one, which is fastest and leaves you on a dead branch. Migrate to 8.3 or later, which is the durable answer and the slowest. Or pull the host off the public internet while you decide, as three agencies recommended for exactly this case.
Check which of the three you are on before you promise anyone a date.
If you cannot patch today
Every item here reduces exposure on a specific path. None of them makes a server safe.
- Turn session upload progress off in the FPM pool. It closes the demonstrated session route and not the other lifecycle routes.
- Cap request body size, part count, and headers per part. A size limit has a known gap because a multipart request without a usable declared length can slip past the first check.
- Block multipart form data on any virtual host that does not accept uploads.
- Set the memory limit to unlimited in the pool. This removes the out-of-memory trigger and still leaves an allocation failure path open.
- Apply the settings in php.ini or the FPM pool, not through ini_set or a .user.ini file, and restart the pool afterward or nothing changes.
WAF rules are the same deal. One major CDN pushed rules to every plan, including the free tier, and its own CTO called them a stopgap. One major cloud updated its managed PHP rule group to cover both CVEs, and its bulletin still says patching is the only complete fix. Rules match request shapes, and attackers were mutating payloads within hours.
There is also a bigger hole in the WAF story. The edge only sees what passes through it. Test whether your origin answers directly on its IP address, and check for DNS-only hostnames such as staging, admin, or api records that point at the same server. A 200, 301, or 302 from the origin IP means anyone can skip your edge entirely.

After the fix
Assume the secrets are gone. One of these flaws reads memory from the PHP worker, which can hold passwords, tokens, API keys, and certificates from other requests. If the host faced the internet before you patched, rotate database credentials, API keys, and session signing keys.
Then look for what a visitor may have left behind. Check web roots for files you did not put there, look for a PHP process that spawned a shell or a network tool, and review bursts of memory errors or repeated 500 and 502 responses from before the patch.
Keep in mind that a successful run looks nearly silent. The worker doesn’t crash, and the response looks normal, so a quiet log isn’t an all-clear.
When the patch window closes
A fleet patch ends with a list of hosts and a hope. The next question is which ones anyone actually checked.
- Secure.com Red Teaming retests your exposed PHP surface from outside, within a scope your team signs and can stop mid-run.
- One target runs $5,000, with a 30-day verification re-scan included, rather than billed as a second engagement.
- CSPM Teammate maps the attack path on your public hosts once your scanner reports the finding and the NVD record lands, without waiting for a KEV listing.
- Workflows pause for a named approver in Slack or Teams before a restart, an isolation, or a key rotation runs.
- Each host ends with a record naming what was checked, what was skipped, and what nobody knows yet.