How I patched 35 servers in one night after PHP2Shell
On 21 October 2026 PHP and Node.js shipped emergency security fixes on the same day. Here's how I got 35 client servers, no two alike, patched by morning.
On 21 October 2026, PHP and Node.js both shipped emergency security fixes on the same day. By the next morning, all 35 client servers I look after were patched.
That sounds like one job done 35 times. It wasn't. Every server had its own mix of operating system, PHP version and Node version, and a few were on versions so old the fix didn't exist for them. It took about 8 to 12 hours, overnight.
What happened that day?
Two separate problems landed at once.
PHP2Shell is two flaws in PHP itself. They let anyone on the internet run their own code on your server, without logging in. PHP runs most of the web, including WordPress and Laravel sites, and the flaws reached back to PHP 5.4 from 2012. Details leaked a day early, so attackers had a head start. The fixes came out at 14:00 UTC on 21 October in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35.
So many sites went offline that it felt like a blackout. Wikimedia shut down supporting services. The Internet Archive went down for a while. Finland took all its government web services offline, and France did the same with its Interior Ministry sites. Most of them weren't hacked. They switched off on purpose, because being offline was safer than being open.
Node2Shell is a bug in the code Node.js uses to unpack Brotli-compressed data. If your app unpacks compressed data that someone else sends you, a bad file can crash the app or, worse, give an attacker a way in. It's fixed in Node.js 22.23.4, 24.22.1 and 26.12.1. A week later, the US government added it to its list of flaws attackers are already using.
Why can't one script fix 35 servers?
Because "update PHP" means something different on each kind of server. These are the cases I had in front of me that night:
| Question | What's different | What the fix looks like |
|---|---|---|
| Ubuntu or Debian | PHP comes from a package, often more than one version side by side | Update the packages, then restart PHP and the web server |
| Docker container | PHP or Node is baked into the image, not installed on the host | Rebuild the image from a patched base, redeploy, check the running container |
| PHP 8.2 and newer | Still supported, official fix available | Straight update to the fixed release |
| PHP 7.4 to 8.1 | No longer supported, no official builds | Backported packages, or an upgrade to a supported version |
| PHP 7.3 and older | No fix exists at all | Upgrade, or close it off from the internet until it's upgraded |
| Node 20 and older | End of life, no Node2Shell fix coming | Move to Node 22 or 24, then test the app |
The Docker row catches people out. You can update every package on the host and still be running the old, broken PHP, because the container carries its own copy. The fix only counts once the image is rebuilt and the new container is the one serving traffic.
What order do I work in?
When there are a lot of servers and not much time, the order matters more than speed.
- 01List every serverWhat OS, PHP, Node and containers each one runs
- 02Patch the easy onesSupported versions get the official fix first
- 03Deal with the old onesUpgrade, or close them off until they can be
- 04Restart and checkLook at what's running, not what's installed
- 05Tell the clientWhat was done, and anything still waiting
The supported servers go first because they're quick. Once they're safe, what's left is the hard part, and it gets your full attention.
What do you do when the version is too old to patch?
This is the part that needs a real decision, not just a command.
PHP 7.4 to 8.1. The PHP team stopped shipping builds for these, but fixed packages exist through backports. Where those were available, that was the quick route. Where they weren't, the server moved up to a supported version.
PHP 7.3 and older. There's no fix. Nobody wrote one. The only real answers are to upgrade, or to keep the server away from the open internet until it can be upgraded. An old site that's been running untouched for years can't always be upgraded in one night, so closing it off buys time to do the upgrade properly.
Node 20 and older. Node 20 is past end of life, so the Brotli fix isn't coming. The app has to move to Node 22 or 24. That's usually fine, but it's a real upgrade, so the app gets tested after the move, not just restarted.
How do you know a server is really fixed?
You check what's running, not what's installed. The two are often different.
- Restart PHP. PHP-FPM and Apache keep the old code in memory until they restart. An updated package does nothing until then.
- Check the right PHP. The
php -vyou type in the terminal can be a different version from the one serving your website. Check the one the website uses. - Check inside the container. Look at the version in the running container, not the image you think you deployed.
- Load the site. After all that, open it and make sure it still works.
A server I've only updated is a server I hope is fixed. A server I've checked is one I know is fixed.
Are your servers ready for the next one?
There will be another night like this. These are the questions worth answering before it comes.
- Would you struggle to say which PHP and Node version each server runs?Yes →Start there. Write it down for every server.No ↓
- Is anything on PHP 8.1 or older, or Node 20 or older?Yes →Plan the upgrade now, before the next fix skips you.No ↓
- Does nobody get told when PHP or Node ships a security fix?Yes →Someone needs to watch for them. It doesn't have to be you.No ↓
- Would patching every server take you more than a day?Yes →Get a process in place, or get help.
- Still no →You're in good shape. Keep the list up to date and keep watching.
PHP2Shell won't be the last emergency patch. If you'd like someone who already knows your servers when the next one lands, that's what my security patching service is for. Or just send me a note with what you're running.