Update, October 21, 14:00 UTC: PHP has released the fix. Update to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or install your distribution's patched packages.
Two critical vulnerabilities in PHP, CVE-2026-109618 and CVE-2026-109619, allow remote code execution: a crafted request reaches your server, and someone else's code runs on it. The PHP security team rated both 9.2 on the CVSS v4.0 scale. PHP released the fix on October 21 in versions 8.5.12, 8.4.27, 8.3.36 and 8.2.35.
Sansec expected mass attacks within 15 minutes of the release. A published fix usually shows attackers where to look. If your VPS runs WordPress, Laravel, a forum, a control panel or any other PHP code, update now.
What PHP has confirmed
According to the PHP security advisory, both bugs allow remote code execution without authentication. CVE-2026-109618 hits when PHP runs out of memory while request data is only partly set up, and cleanup then treats that data as valid. CVE-2026-109619 lets internal state from one request outlive its memory, so a later request on the same worker can pick it up.
Every PHP installation that serves web requests is at risk: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. PHP rates mod_php as the riskiest, with a demonstrated exploit that fires before any application code runs.
The CVE numbers first surfaced through CERT-EU and watchTowr, and the leak that preceded the release pointed at session upload progress. The advisory confirms upload progress as one attack path for CVE-2026-109619, but not the only one.
On exploitation, Huntress reported on October 20 that it had seen no attacks in its partners' environments and knew of no public exploit code. By the morning of October 21, that changed. Cloudflare CTO Dane Knecht wrote on X that the company was seeing early attacks and had deployed WAF rules for the known exploit vectors, available on every plan, free included. He called the rules a stopgap and urged everyone to patch at 14:00 UTC. If your site sits behind Cloudflare, that buys time, but only for traffic that goes through Cloudflare: requests sent straight to your server's IP skip the WAF.
Which PHP your server runs
Start by finding every PHP version on the server. The trap here is that the php -v command reports the version PHP uses on the command line. Your site is served by PHP-FPM or an Apache module, which can run a different version entirely.
So check three things: which PHP versions are installed (on Debian and Ubuntu, each one gets its own folder in /etc/php), which PHP services are running, and which version each FPM binary reports on its own. If PHP runs in Docker, check every container too: each one carries its own PHP, and the image name doesn't always say so.
Write the list down.
Update and verify
Before you start, take a fresh backup or snapshot: last week's copy is a poor rollback point for a same-day PHP upgrade. Also copy current nginx or Apache access logs and PHP-FPM logs somewhere logrotate won't reach. If something got in before the patch, those files are the only record of how.
The fixed versions are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Linux distributions received the patches in advance, so updated packages may already be in your repository. If PHP came from the remi repository, it won't get the update today: PHP recommends switching to distribution packages or building from source.
Update PHP the same way it was installed: through apt on Debian and Ubuntu (distribution packages or the ondrej/sury repository), through dnf on AlmaLinux, Rocky and CentOS Stream. Before you run the upgrade, confirm that your repository already offers the fixed build and that no PHP package is on hold: a held package is skipped without any warning. After the update, restart PHP-FPM, or Apache if PHP runs as its module.
Control panels deliver PHP on their own schedule. cPanel, for example, ships PHP as EasyApache 4 packages, so the fix arrives when cPanel publishes updated ea-php builds. Run the panel's update and check its changelog.
Docker: rebuild the container on a fresh base image instead of updating PHP inside the running one.
Long-running PHP processes (queue workers, schedulers, Laravel Octane) keep the old binary in memory until they restart, so restart those too. Then go through your list from earlier and check the version of every binary, FPM included.
One detail that confuses people every time: distributions backport security fixes and keep the old upstream number. A patched Ubuntu or Debian package can still read 8.1.2 with a distribution suffix after it. For those, look the CVE numbers up in the Ubuntu or Debian security tracker instead of comparing version strings with php.net.
If you blocked multipart requests while waiting (see below), remove the block once the fixed version serves your site and test a file upload. Upload progress can stay off: most sites never used it.
If you can't update right away
PHP lists several measures that reduce exposure and says plainly that none of them makes an installation safe. Use them only to bridge the gap until you update.
1. Turn off session upload progress
The feature is on by default and tracks how far a file upload has got. According to PHP, turning it off blocks the confirmed session paths of CVE-2026-109619, but not every variant. Uploads keep working; only progress bars built on this feature stop. Set session.upload_progress.enabled to Off for every installed PHP version in php.ini, the FPM pool or the server configuration. Don't rely on .user.ini: a file in a site folder can switch the feature back on, so on PHP-FPM lock the setting in every pool with php_admin_flag. Then restart PHP-FPM, or Apache if PHP runs as its module, so every worker picks up the change.
2. Apply PHP's measures for CVE-2026-109618
Set memory_limit to -1 with php_admin_value in the FPM pool or Apache config, so applications can't override it. Without a memory cap, one request can eat the whole server's RAM, so PHP recommends a container or cgroup limit as a safety net. Lower post_max_size to 8M if the site takes no uploads, or 32M if it does, and keep upload_max_filesize below it. On PHP 8.5, also turn off fatal_error_backtraces.
3. Verify the settings in the PHP that serves your site
The command line won't tell you. The simplest check is a throwaway PHP file with a random name in the site's root folder that prints the values of these settings. Open it in a browser, confirm the changes took effect, then delete the file. If you get your site's 404 page instead, the server routes everything through index.php and this check won't work there.
4. Limit multipart requests at the web server
PHP recommends limiting request size and the number of multipart parts before requests reach PHP. If the site doesn't need uploads, blocking multipart requests outright is the simpler version of the same idea. Setting file_uploads to Off won't help, since PHP still parses the multipart body and only skips the files. In nginx, add a rule that answers 403 to any request whose Content-Type header contains "multipart", and put it in every server block that passes requests to PHP, including the default one that answers requests sent to the bare IP. In Apache, the same rule goes into the virtual host or .htaccess. Make sure it checks the request's Content-Type header: Apache's CONTENT_TYPE variable describes the response and lets these requests through. Then test from another machine by sending a file upload to the site. The answer should be 403.
The price: every file upload stops until you remove the block. That includes WordPress media uploads and some contact forms that send multipart even without a file field (Contact Form 7 is one). Pages, logins, JSON APIs and most ordinary forms keep working. WordPress security plugins and application login checks don't replace this block, because PHP parses the request body before any of them run.
5. If you take a site offline until the update, stop the web server or return 503
Don't just remove the PHP handler from nginx or Apache: the server will then serve .php files as plain text, database passwords from config files included.
If you're on PHP 8.1 or older
PHP 8.0 and 8.1 are out of upstream support, and 7.x has been for years. PHP no longer ships releases for these branches. Some distributions and LTS vendors backport the fix, and for those who build from source, PHP published minimal patches for 7.4, 8.0 and 8.1. Ubuntu 22.04 is still in standard support, so Canonical may backport the patch into its own 8.1 packages; watch the Ubuntu security notices for these CVEs. If none of these options fits, move to a supported branch, and keep the measures above in place until then.
Pick 8.3 or 8.4 over 8.2. PHP 8.2 loses security support on December 31, 2026, so upgrading to it today means repeating the exercise in ten weeks. Test the site on a copy first: jumps from 7.4 tend to break old plugins in creative ways.
Who runs the update
On an unmanaged VPS, PHP is part of your stack, so the update runs on your side. Everything you need is in the steps above, and on most servers the update itself comes down to one command.
On a managed VPS, you can hand the update to is*hosting support: open a ticket and ask them to update PHP. Support doesn't log into servers on its own initiative, because only you know what runs there, so the request has to come from you.
Either way, check the PHP version one last time today. Then check FPM too, because that's what serves your site.