On 21 October 2026, the PHP project shipped an emergency release fixing two critical bugs in PHP itself. Both can let an attacker who hasn't logged in run code on your server. The security community is calling them PHP2Shell.
PHP's own advisory doesn't hedge: "There are no complete mitigations, updating is the only safe choice."
This guide covers what we'd tell any client running PHP in production: how to work out what you're actually running, how to patch it properly, and what to do if you can't patch today.
The short version
- What: CVE-2026-109618 and CVE-2026-109619. Both are rated CVSS v4.0 9.2 (Critical), and both can lead to unauthenticated remote code execution.
- Where: PHP itself, not your framework, CMS or plugins.
- Who: any PHP installation serving web requests: PHP-FPM, Apache mod_php, php-cgi, FrankenPHP and LiteSpeed. Researchers traced the vulnerable code back as far as PHP 5.4.
- Fixed in: PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35.
- Older versions: the PHP security team published source-only patches for 7.4, 8.0 and 8.1. Versions 5.4 to 7.3 get no fix at all.
- Exploitation: Amazon's threat intelligence team saw an exploit attempt on 17 October, four days before the patch. Cloudflare reported early attacks on release day, and Amazon now says the bugs are being broadly exploited. Proof-of-concept code is circulating publicly. Treat this as an emergency.
What actually went wrong
You don't need this section to patch, but it explains why the usual "we keep everything updated" answer doesn't hold up.
CVE-2026-109618 (out-of-memory corruption). If PHP runs out of memory while it's still setting up internal data for a request, the cleanup code can treat that half-built data as valid. An attacker can trigger this with a crafted multipart request, even one that looks like it's inside your configured size limits.
CVE-2026-109619 (stale state between requests). PHP workers handle many requests one after another. Some internal state from one request can survive after its memory has been freed, and the next request on the same worker can use it. One confirmed path goes through session upload progress, a feature that's switched on by default. It works even if your code never calls session_start().
The risk is highest on Apache with mod_php. PHP says there's a demonstrated exploit there that fires before any of your application code runs. PHP-FPM is harder to exploit, but it's still affected, so don't treat it as safe.
Why updating WordPress or Laravel doesn't help
These bugs sit underneath your application. WordPress.org has confirmed that no WordPress update is needed or planned, because the fix is the PHP update. The same goes for Laravel, Symfony, Drupal, Magento and your own custom code. Up-to-date plugins and Composer packages are good practice, but they don't fix this.
Step 1: Find out which PHP version you're really running
This is where most teams slip, and there are two catches.
Catch 1: php -v might be lying to you
On the command line, php is the CLI binary. Your website is probably served by PHP-FPM or mod_php, which can be a different version on the same machine. Check the one that actually serves traffic:
# CLI version (useful, but not the whole story)
php -v
# FPM binaries, one per installed version on Debian/Ubuntu
ls /usr/sbin/php-fpm*
php-fpm8.3 -v
# Which FPM services are running
systemctl list-units --type=service | grep -i fpm
If you're on Apache with mod_php, check which module is loaded:
apache2ctl -M | grep -i php
Catch 2: an old version number doesn't always mean you're unpatched
Linux distributions received the patches in advance, and Debian, Ubuntu, RHEL and others often backport fixes without changing the upstream version string. So a server showing 8.1.2 might be patched, and one showing 8.3.6 might not be. Check the installed package's changelog for the CVE IDs:
# Debian / Ubuntu
zgrep -E "CVE-2026-10961(8|9)" /usr/share/doc/php8.3-*/changelog.Debian.gz
# RHEL / Rocky / Alma
rpm -q --changelog php-fpm | grep -E "CVE-2026-10961(8|9)"
If both IDs appear, that package has the fix. If nothing comes back, assume you're exposed.
Don't forget the servers nobody thinks about: staging boxes, old marketing sites, an internal admin panel, a cron server running PHP scripts, a Docker image built two years ago and never touched since. Attackers scan the whole internet, not just your main product.
Step 2: Patch PHP
How you patch depends on where PHP runs.
Managed hosting (Kinsta, WP Engine, Pressable, Laravel Cloud and similar)
Your host patches PHP for you. Look for their status page or email to confirm it's done. If you run PHP 8.1 or older on a managed host, use their control panel to move to 8.2 or newer.
Shared hosting (cPanel, Plesk)
cPanel and Plesk have released updated packages, and most hosts will roll them out across all accounts. If your control panel lets you choose a PHP version and you're on 8.1 or older, switch now. If you can't tell whether the patch is in, open a support ticket and ask directly.
Your own VPS or cloud server
Update the packages, then restart PHP. That restart is the step people forget. Old workers keep running the vulnerable code in memory until they're replaced.
# Debian / Ubuntu
sudo apt update
apt list --upgradable 2>/dev/null | grep php # confirm the fix is on offer
sudo apt upgrade
sudo systemctl restart php8.3-fpm
# If you use mod_php
sudo systemctl restart apache2
# RHEL / Rocky / Alma
sudo dnf upgrade 'php*'
sudo systemctl restart php-fpm
Change 8.3 to whichever version you run, and restart every FPM service, not just the first one you find.
Check where your PHP packages come from. A lot of servers get PHP from third-party repositories rather than the distribution. PHP warned that the Remi repository (rpms.remirepo.net) wouldn't have updates on release day. Ondřej Surý's packages (the ondrej/php PPA and sury.org) are another common source. If apt or dnf says there's nothing to upgrade, find out which repository your PHP came from before you assume you're up to date.
DigitalOcean
DigitalOcean has emailed every customer about this, and a few points in it are worth knowing if you host there:
- Marketplace 1-Click images (WordPress, LAMP, LEMP, Laravel and others) are being suspended until they're patched. That only stops new deployments. If you created a Droplet from one of those images in the past, PHP on it is yours to update, using the VPS steps above.
- DigitalOcean may temporarily restrict traffic to resources that show signs of compromise. An unpatched Droplet that gets hit could end up cut off as well as compromised.
- App Platform and Cloudways handle PHP updates at platform level. If you deploy a custom container on App Platform, though, rebuilding that image is still your job.
- DOKS (Kubernetes) workloads need the same image rebuild as any other container.
Docker and Kubernetes
Patching the host does nothing for PHP inside your containers. You need to rebuild the images and redeploy:
docker build --pull --no-cache -t myapp:php-patched .
--pull fetches the latest base image instead of reusing the cached one, and --no-cache stops Docker from reusing old layers. Before you deploy, check the PHP version inside the new image:
docker run --rm myapp:php-patched php -v
Then redeploy, and check that old pods or containers have actually been replaced. A rolling deploy that stalls halfway can leave vulnerable replicas serving traffic.
PHP 8.1 or older
This is the hard case. The PHP project has published source-only patches for 7.4, 8.0 and 8.1. They help if you compile PHP yourself or have a long-term-support vendor, but most teams don't. For 7.3 and earlier there's nothing at all.
The real fix is to upgrade to a supported version. If you're going to do that work anyway, go straight to 8.3 or 8.4 rather than 8.2: PHP 8.2 reaches end of life on 31 December 2026.
Test the upgrade on staging first. Old themes, plugins and libraries can break between major versions. Take a full backup, upgrade, then check your login, checkout, forms and any file uploads.
If you can't upgrade quickly and the server faces the internet, WordPress.org, the UK's NCSC and CERT-EU have all suggested taking it offline or isolating it until it's fixed. It's an unpopular option, but it's the right one for a server that can't be patched.
Step 3: If you can't patch today
PHP has published interim settings. They block the attack paths known so far, but the advisory is clear that they do not make an installation safe. Use them to buy hours or days, not weeks.
For CVE-2026-109619, turn off session upload progress. Most applications don't use it, and WordPress doesn't.
session.upload_progress.enabled = 0
On PHP 8.5, also add:
fatal_error_backtraces = 0
For CVE-2026-109618, PHP recommends removing PHP's own memory limit and keeping request sizes small:
memory_limit = -1
post_max_size = 8M ; or 32M if you genuinely need uploads
upload_max_filesize = 6M ; keep this below post_max_size
memory_limit = -1 looks backwards, but it's deliberate: the bug lives in PHP's out-of-memory handling, so you stop PHP from reaching it. Something else then has to stop a runaway worker, ideally a container or cgroup limit that kills only that worker. PHP specifically warns not to use RLIMIT_AS or RLIMIT_DATA, because they can trigger the same vulnerable path.
Three details matter here:
- Set these at server level: in
php.ini, an FPM pool withphp_admin_value, or the Apache config.ini_set()inside your code and.user.inifiles apply too late to help. - Check nothing overrides them. A pool config or an application setting that brings back a finite
memory_limitundoes the mitigation. - Restart every worker after the change.
To confirm what the running FPM pool actually sees, don't trust php -i (that's the CLI again). Use a temporary phpinfo() page behind authentication, then delete it.
Add an edge layer if you have one. Cloudflare has deployed WAF rules for the known attack vectors on every plan, including free. On paid plans, check the managed rules are set to block, not just log. Google Cloud Armor users can enable the File Upload rule category and block request bodies that exceed inspection limits. These rules only see traffic that goes through them, and attackers who find your origin IP can go around them. They're one layer, not the fix.
Step 4: Check whether someone got in first
Amazon recommends starting any investigation from 17 October, not from the disclosure date. If a server was exposed during that window, patching closes the hole but doesn't tell you whether someone already used it.
Things worth checking on any internet-facing PHP server:
- Worker crashes since 17 October. Memory-corruption exploits tend to leave segfaults behind while attackers fine-tune them. Look for
SIGSEGVin PHP-FPM logs orSegmentation faultin Apache error logs, especially in clusters. - New or changed PHP files in web-writable folders such as
uploads/,cache/andtmp/. Your own deploys will show up too; you're looking for files nobody on the team recognises. - Unexpected processes or outbound connections from the web server user (
www-data,apache,nginx). - New cron jobs, SSH keys or admin accounts you didn't create.
find /var/www -name "*.php" -newermt "2026-10-17" -ls
A quiet log doesn't prove you're clean. PHP points out that hiding errors with display_errors or @ doesn't stop the bug, and an exploit that works first time may leave nothing behind. If anything looks off, treat the server as compromised: rebuild it from a known-good image and rotate any secrets it held.
Be careful with "proof of concept" code from GitHub. JFrog found fake PHP2Shell exploit repositories, including one hiding an encoded payload. Don't run exploit code you can't fully read on a machine you care about.
A checklist you can hand to your team
- List every server, container image and hosting account running PHP, including staging and legacy sites.
- Confirm the version that serves web traffic (FPM or mod_php), not just the CLI.
- Check distribution changelogs for both CVE IDs where the version string looks old.
- Upgrade to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or a vendor package with both fixes.
- Restart every PHP-FPM service and Apache.
- Rebuild Docker images with
--pull --no-cache, checkphp -vinside, then redeploy. - Plan the upgrade for anything on 8.1 or older, aiming for 8.3 or newer.
- Apply the interim settings anywhere patching has to wait, and verify them with
phpinfo(). - Turn on WAF rules at your edge if you have one.
- Review logs and file changes from 17 October onwards.
Need a second pair of eyes?
At NotJuzApp we've spent 13 years building and running production backends, web apps and cloud infrastructure for clients on AWS, Docker and Cloudflare, and keeping them patched is part of the job.
If you aren't sure which PHP version your site or app is really running, or you're stuck on an old version and nervous about upgrading, email milan@notjuzapp.com with your domain and hosting setup. We'll tell you whether you're exposed and what to do next. No charge for that first look.
Sources
- PHP security advisory, 21 October 2026
- GHSA-w8hw-w9fx-j79w (CVE-2026-109618) and GHSA-6wmf-4rv5-p3rp (CVE-2026-109619)
- Rapid7: Critical unauthenticated RCE in PHP
- Tenable: PHP2Shell FAQ
- JFrog Security Research: PHP2Shell
- Google Cloud: Responding to core PHP vulnerabilities
- Codeable: What WordPress site owners need to do