PHP Security Update Explained: Affected Versions and How to Fix Your Website
Published on November 5, 2026 by Muhammad Raza Bangi. Last verified November 5, 2026.
Quick Answer
On October 21, 2026, the PHP project released emergency security updates — PHP 8.2.35, 8.3.36, 8.4.27, and 8.5.12 — fixing two critical vulnerabilities that could let an attacker run code on your server without logging in, just by sending normal-looking web requests. Both are rated 9.2 out of 10 (Critical) under CVSS v4.0.
If your website runs on PHP, do these four things, in this order:
- Find out what PHP version is actually running your live website (not your laptop — see section 5 below).
- Install a patched release — 8.2.35, 8.3.36, 8.4.27, or 8.5.12, whichever branch you're already on (or newer, if one has shipped by the time you read this).
- Restart the PHP process that serves your site — PHP-FPM, Apache, your Docker container, or your hosting panel's PHP service. Installing a new PHP package does nothing until the running process is replaced.
- Confirm the fix by checking the version number the web server reports, not just your terminal.
In simple words: This is not a Laravel bug, a WordPress bug, or a plugin bug. It is a bug in the PHP language runtime itself, so it affects every PHP application equally, at the layer underneath all of them.
1. What Happened?
Six terms used throughout this guide:
- PHP runtime — the actual installed PHP program that executes your code on the server. Your code (Laravel, WordPress, or plain PHP) is just instructions; the runtime is the engine that reads and runs them.
- HTTP request— a message a browser, app, or script sends to your server, like "load this page" or "submit this form."
- Worker— a PHP process that handles requests. Many servers keep a pool of workers running continuously so each new request doesn't have to start PHP from scratch; a worker typically handles many requests, one after another, over its lifetime.
- Memory — the space a running program uses to store data while it works. Every request needs some memory to hold the data it is processing.
- Out-of-memory (OOM) error — what happens when a program tries to use more memory than it is allowed. PHP has a
memory_limitsetting; hitting it triggers an OOM error, which normally just stops that one request with a fatal error. - Use-after-free — a programming bug where code keeps a reference to a piece of memory after that memory has already been released for other use. If something else writes to that freed memory later, the old reference now points at the wrong data — and an attacker who can influence what gets written there can sometimes turn this into running their own code.
- Remote code execution (RCE)— when an attacker can make a server run commands or code of the attacker's choosing, not just read or crash the application. It is one of the most serious outcomes a vulnerability can have.
- Unauthenticated attacker — someone who does not have a username, password, or account on your site. They are exploiting the vulnerability from the outside, as any random visitor could.
Two separate critical vulnerabilities were fixed on October 21, 2026. They are different bugs with different root causes, but both live deep in PHP's own memory-management code — not in any specific framework or plugin.
CVE-2026-109618 — RCE via Out-of-Memory Corruption
Analogy: Imagine a chef who, partway through chopping ingredients for a dish, is suddenly called away by a fire alarm before putting the knife down or clearing the counter. The cleanup crew that comes in afterward, not realizing the dish was only half-prepared, plates it anyway — serving whatever was actually on the counter, including things that were never meant to be food.
Precise explanation: When PHP hits its memory limit, it uses a programming technique called longjmp() to immediately jump out of whatever it was doing and abort the current request. The problem is timing: during request setup, some memory is made reachable by PHP's internal bookkeeping before it is fully initialized. If the out-of-memory error strikes during that exact narrow window, PHP's early-exit code does not properly account for the half-finished state — it leaves the request's memory marked as valid when it isn't. The shutdown code that runs afterward then reads and acts on that corrupted, invalid data.
According to the official advisory, researchers successfully demonstrated full, unauthenticated remote code execution — running as the web server's own account — over plain HTTP against Apache with mod_php using a default configuration, specifically because mod_php always runs its shutdown procedure even when startup failed, which made the bug easier to reach reliably.
CVE-2026-109619 — RCE via Stale Global State
Analogy:Picture a hotel room that is supposed to be fully reset between guests. If a previous guest's key card is accidentally left active and a staff member's master directory still has a stale entry pointing at "this room, this guest, these privileges," a new guest who later gets handed that same room could, under the wrong circumstances, use the leftover entry to access something that was never meant to be theirs.
Precise explanation: Because workers handle many requests in sequence rather than starting fresh each time, PHP sometimes keeps request-specific data in a location that is accessible globally — not cleaned up the instant the request ends. The official advisory explains that this request-owned state can outlive the memory that backs it: a "producer" request stores something in that global spot, the request's memory is freed, and then a later, unrelated request handled by the same worker — the "consumer" — reads that now-stale pointer before normal startup has reset it. With a carefully arranged sequence of requests, an attacker can turn that leftover reference into full RCE.
Two concrete paths are named in the advisory: PHP 8.5's fatal_error_backtraces feature (backtrace data can persist after the memory behind it is freed), and the session upload-progress feature (its internal state can survive an OOM or fatal error and get read by a later upload-progress request).
No exploit details here: Both bugs were fixed the way the PHP core team always fixes memory-safety issues: by making cleanup and initialization code properly track and reset state, so stale or half-built data can never be read as if it were valid. This guide does not reproduce exploit payloads or attack steps — that is a separate, narrower audience than this article.
It is also worth being precise about scope: the PHP Foundation's own writeup says their security team "did not identify any functional exploits at the time of release," but by October 26, CISA added both CVEs to its Known Exploited Vulnerabilities catalog, which only happens with confirmed evidence of real-world exploitation. That does notmean every unpatched PHP site has already been compromised — it means this is a live, actively targeted bug class, not a theoretical one, and the gap between "patch released" and "you actually restart your server" is time worth closing quickly.
2. Who Is Affected?
Both CVEs live in PHP's core — the Zend Engine's memory and request-lifecycle handling — underneath every framework and CMS. A framework does not replace the PHP runtime it sits on top of. Laravel, WordPress, and a plain index.php file are all, ultimately, PHP code interpreted by the same underlying engine.
| Platform | How it is affected | Confirmed exploit vs. theoretical risk |
|---|---|---|
| Laravel & Filament | Runs on the PHP runtime underneath; vulnerable if unpatched, regardless of Laravel version | Advisory doesn't single out Laravel; risk is at the runtime layer it sits on |
| Symfony & CodeIgniter | Same — framework code never touches this layer | Same as above |
| WordPress & WooCommerce | Same — core and plugins run as PHP code on the same runtime | Same as above |
| Drupal & Joomla | Same | Same as above |
| Plain/custom PHP apps | Same, with no framework layer to even ask the question about | Same as above |
| PHP APIs | Any endpoint accepting an HTTP request can potentially trigger the memory conditions | Same as above |
| PHP-FPM | Named explicitly in the advisory as an at-risk SAPI | At risk; not the SAPI used in the advisory's confirmed demonstration |
| Apache mod_php | Named explicitly; this is the configuration used to confirm working RCE | Confirmed exploit demonstrated here specifically |
| php-cgi | Named explicitly as at-risk | At risk, not separately demonstrated in the public advisory text |
| FrankenPHP | Named explicitly as at-risk | At risk, not separately demonstrated in the public advisory text |
| LiteSpeed (LSAPI) | Named explicitly as at-risk | At risk, not separately demonstrated in the public advisory text |
A few things worth saying plainly, because they are common — and wrong — reasons people assume they are safe:
- "My site requires a login"doesn't help — the vulnerable code runs during PHP's own request setup and teardown, before your application's authentication logic ever gets a chance to check anything.
- "We validate all our inputs" and "we have CSRF protection" are real, good practices, but they operate at the application layer, above where these bugs live.
- "We don't have a file upload form"doesn't rule it out either — one of the two vectors named for CVE-2026-109619 involves session upload-progress machinery reachable by any POST request, not specifically a form your application built.
What this doesn't mean: None of this means every unpatched site is guaranteed to be breached — exploitability depends on exact configuration, SAPI, and request handling, and the advisory's confirmed demonstration is specific to one configuration (mod_php, default settings). But "we probably don't match that exact setup" is a guess, not a verification. The only way to know your risk is actually gone is to patch the runtime.
3. Affected and Fixed Version Table
| Branch | Affected range (these 2 CVEs) | First fixed version | Currently recommended (verified Nov 5, 2026) | Security support ends |
|---|---|---|---|---|
| PHP 8.2 | < 8.2.35 | 8.2.35 | 8.2.35 (Windows: 8.2.35-1) | 31 Dec 2026 |
| PHP 8.3 | < 8.3.36 | 8.3.36 | 8.3.36 (Windows: 8.3.36-1) | 31 Dec 2027 |
| PHP 8.4 | < 8.4.27 | 8.4.27 | 8.4.27 (Windows: 8.4.27-1) | 31 Dec 2028 |
| PHP 8.5 | < 8.5.12 | 8.5.12 | 8.5.12 (Windows: 8.5.12-1) | 31 Dec 2029 |
| PHP 8.1 and earlier | All versions | No official fix | N/A | Already ended (8.1 ended 31 Dec 2025) |
Source: GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp, and php.net/supported-versions.php.
As of this writing, "first fixed" and "currently recommended" are the same releases — no newer routine release has shipped since October 21. That will likely change around November 19, 2026, when PHP's next regular monthly release is expected, alongside PHP 8.6.0's planned GA — check php.net for anything newer before you patch.
PHP 8.2's clock is running out: PHP 8.2's security-support window ends December 31, 2026 — under two months from this article's research date. If you are on 8.2, this patch buys you time, not a long runway; start planning a move to 8.3 or newer.
About PHP 8.1 and older
PHP 8.1's active support ended November 2023 and its security support ended December 31, 2025 — it is fully end-of-life. The PHP project is not issuing a fix for these two CVEs on 8.1 or earlier, and we found no official PHP project backport for them at the time of research.
That doesn't automatically mean every 8.1 install is defenseless: some Linux distribution vendors and commercial long-term-support providers backport individual security fixes into their own package build while leaving the PHP version string looking old (a package revision bump rather than a jump to "8.2"). This is normal and doesn't mean the distro lied to you — but it does mean you cannot tell patch status from php -valone on an EOL branch. Check the full package revision against your OS vendor's own security advisory rather than assuming an old-looking version number means unpatched, or that it means patched. Applying one emergency backport also doesn't put you back on general-purpose support — you remain responsible for tracking every future CVE on an unsupported branch yourself.
4. The Three Things You May Need to Update
| Layer | What it is | Does it fix these runtime CVEs? |
|---|---|---|
| PHP runtime | The actual php executable / php-fpm binary installed on the server | Yes — this is the only layer that fixes CVE-2026-109618 and CVE-2026-109619 |
| Framework / CMS (Laravel, WordPress, Symfony, Drupal...) | Your application code, written in PHP, running on the runtime | No — these bugs are below the framework layer entirely |
| Composer packages / plugins / extensions | Third-party libraries your app depends on | No — same reason |
A few specific, common misunderstandings worth clearing up directly:
- composer update does not touch your server's PHP executable. Composer manages PHP libraries your application loads. It has no mechanism to install or replace the PHP interpreter itself; that is an operating-system or package-manager job.
- A clean composer audit does not prove your runtime is patched. It checks your installed Composer packages against a vulnerability database — it knows nothing about which PHP binary is actually executing your code.
- Changing composer.json installs nothing.It declares what your project is compatible with; it doesn't download or install a PHP interpreter.
- config.platform.php affects dependency resolution only. It tells Composer to pretend the server has PHP version X when deciding which package versions are installable. It has zero effect on which PHP binary is actually running on the server — it is a planning aid, not a patch.
- A same-branch patch update differs from changing PHP branches. 8.3.19 → 8.3.36 is a small, low-risk change: bug and security fixes only, same language features. 8.3 → 8.4 is bigger: new language features, some deprecations, and genuinely different edge-case behavior — and deserves its own compatibility check.
Concrete example: A Laravel application currently running PHP 8.3.19 needs its PHP runtime updated to 8.3.36 (or newer) to close these two CVEs. It does not need a Laravel major-version upgrade to do that — Laravel 12.x's own deployment docs only require PHP 8.2 or newer, and a patch-level PHP update is exactly the kind of change Laravel already expects you to make routinely.
5. Check Which PHP Your Website Actually Uses
Run these from the server that actually hosts your live site — not your laptop, unless your laptop is the only server (e.g., local development).
php -v # Shows the CLI PHP version and build date
php --ini # Shows which php.ini files are actually loaded
command -v php # Shows exactly which php binary the "php" command runs
php -m # Lists loaded extensions — useful after an update, to confirm nothing's missingWhy this isn't the whole story: php -v shows the command-line (CLI) PHP, which can be a completely different binary, version, and configuration from the PHP that actually serves web requests (via PHP-FPM, mod_php, or another SAPI). On a server with multiple websites, each can be pinned to its own PHP-FPM pool and version. Containers have their own isolated PHP, separate from the host machine. Queue workers and cron-scheduled tasks often invoke PHP directly via their own configured path, which may not match either the CLI default or the web SAPI.
To check the web-facing PHP version safely, create a temporary file:
<?php
echo phpversion() . ' — ' . php_sapi_name();Save it as something hard to guess (not info.php or phpinfo.php), place it in your site's public folder, restrict access immediately (IP allowlist, HTTP basic auth, or a firewall rule — whatever your server supports), load it once to confirm, and delete it immediately afterward.
Never leave a public diagnostic page online: Do not leave a phpinfo() page — public or "hidden" — on a live server; it leaks configuration details that help an attacker, and an unguessable URL is not real access control.
For Linux package-based installs, check the exact installed package revision, not just the short version:
# Debian/Ubuntu
dpkg -l | grep php
# RHEL/CentOS/Alma/Rocky
rpm -qa | grep phpCompare the full revision string against your distro's own published security advisory for these CVEs — that is the only reliable way to tell whether a vendor backport has actually landed.
6. Prepare an Existing Project Safely
Before touching anything in production:
- Back up application files, the database, and server configuration (php.ini, FPM pool configs, web server vhost files). A PHP version change is usually safe, but "usually" isn't "always."
- Verify the backup can actually be restored — an untested backup is a hope, not a plan.
- Record your current PHP extensions and active configuration (
php -m,php --ini, and yourconf.d/php-fpm.dcontents) so you can tell if anything is missing afterward. - Check framework, plugin, and extension compatibility with the target PHP version before switching.
- Prefer a patch within your existing branch (8.3.19 → 8.3.36) over jumping branches, when practical — it is the smallest change that still closes these CVEs.
- Test in staging first, using a copy of production data where feasible.
- Plan a brief maintenance window if your update process requires one (shared hosting especially).
- Preserve your existing composer.lock file— don't regenerate it as part of a PHP patch; you want the same dependency versions, just running on a newer PHP.
- Have a recovery plan that uses a patched, compatible environment — if something breaks, roll forward to a fixed configuration (restoring your tested backup while you debug on staging), not back to the exposed, unpatched runtime as a long-term fix.
7. Choose Your Environment: Practical Update Guides
Run only the section that matches your actual setup.
A. macOS with Homebrew and Laravel Valet
Who this is for: developers running local Laravel sites via Valet on macOS.
Inspect your current setup:
which php # Confirms whether Homebrew, Herd, or system PHP answers to "php"
brew list --versions | grep php # Lists installed Homebrew PHP formulae and their versions
valet which-php # Shows which PHP binary Valet is using for the current siteIf you installed PHP via Laravel Herdinstead of plain Homebrew, update through Herd's own updater, not Homebrew — mixing the two can conflict.
Updating a Homebrew PHP install:Homebrew's core php formula tracks only the latest stable branch. For a specific older branch like 8.3, most developers use the community-maintained shivammathur/php tap:
brew update # Refreshes Homebrew's own package index
brew tap shivammathur/php # Adds the versioned-PHP tap (if not already added)
brew upgrade shivammathur/php/php@8.3 # Upgrades your existing php@8.3 formula to its latest buildDon't assume a versioned formula exists for every branch in every tap — check the tap's formula list first. If you're on plain brew install php (the latest-only core formula) and it already tracks 8.5, brew upgrade php is enough.
Site isolation: if this site uses valet isolate, confirm which version it's pinned to and upgrade that specific one:
valet isolated # Lists every isolated site and its pinned PHP version
cd ~/Sites/your-project
valet isolate php@8.3 # Re-pins (and reinstalls if needed) this site to the upgraded php@8.3Restart:
valet restartVerify, in both places: run php -v in your terminal, then load the site in a browser and check your temporary web-facing version file from section 5.
Common mistakes: running brew unlink/brew link --force repeatedly "just in case" — only do this if which php or php -v actually show the wrong version; forgetting that Valet only serves one globally linked PHP version at a time unless a site is explicitly isolated.
B. Ubuntu/Debian with Nginx and PHP-FPM
Who this is for: self-managed Linux servers running Nginx + PHP-FPM.
Discover what you have first:
php -v
dpkg -l | grep php-fpm # Shows the exact installed php-fpm package and revision
ls /etc/php/ # Shows which PHP version directories exist
systemctl list-units --type=service | grep php # Shows the actual FPM service name, e.g. php8.3-fpmUbuntu's and Debian's own default repositories typically carry only one PHP version per OS release and are often slow to pick up point releases. Most servers running a specific, current PHP version use Ondřej Surý's PPA/apt repository (ppa:ondrej/php on Ubuntu; the equivalent packages.sury.org repo on Debian), which is the de facto standard way the PHP community distributes current builds for these distros — it is third-party to Ubuntu/Debian, but it is the same source most hosting documentation already points to.
Example — PHP 8.3 on Ubuntu:
sudo add-apt-repository ppa:ondrej/php # Adds the repository that carries current PHP point releases
sudo apt update # Refreshes package lists, including the new repo
sudo apt install php8.3-fpm # Installs/upgrades to the latest available 8.3.x buildCheck which extensions you currently have before upgrading, and install the matching ones for the new version so nothing silently goes missing:
dpkg -l | grep '^ii.*php8\.3-' | awk '{print $2}' # Lists currently installed php8.3-* extension packagesRestart the correct service — this is the step people skip:
sudo systemctl restart php8.3-fpm
sudo systemctl status php8.3-fpm # Confirm it's active and shows the new version/start timeNginx and PHP-FPM are separate: Restarting Nginx alone does not restart PHP-FPM — Nginx just forwards PHP requests over a socket to whatever FPM process is already running; it has no way to replace that process itself.
Verify no old workers remain:
ps aux | grep php-fpm # Every listed worker should have a recent start time, matching your restartCommon mistake: adding a third-party repo and installing the package, but never restarting the FPM service — the old worker pool keeps running the old, vulnerable binary indefinitely.
C. Apache
mod_php vs. proxying to FPM — know which one you have:
apache2ctl -M | grep php # If this shows a php module, you're running mod_php directly
apache2ctl -M | grep proxy_fcgi # If this shows proxy_fcgi, Apache is forwarding to PHP-FPM instead- mod_php: PHP runs inside the Apache process. Updating the PHP package requires restarting Apache itself (
sudo systemctl restart apache2on Debian/Ubuntu, orsudo systemctl restart httpdon RHEL-family distros) — there is no separate PHP process to restart. - Apache proxying to PHP-FPM: PHP runs as its own separate service. Restart PHP-FPM, the same as in section B — restarting Apache alone does nothing to the PHP process behind it.
Service names vary by distro (apache2 on Debian/Ubuntu, httpd on RHEL/CentOS/Alma/Rocky) — confirm yours with systemctl list-units --type=service | grep -E "apache|httpd".
D. Docker and Laravel Sail
Who this is for: Dockerized PHP apps, including Laravel Sail.
Identify every PHP-based service and its base image:
grep -i "FROM php\|image:.*php" Dockerfile docker-compose.yml # Finds every service built from a PHP base imageCheck each one — commonly your app container, plus separate queue-worker and scheduler containers that often share the same image.
Update to a verified patched image tag, then rebuild and recreate — pulling a new tag alone does nothing until the running container is replaced:
docker compose pull # Downloads newer images for tags already referenced in compose
docker compose build --no-cache # Rebuilds any custom Dockerfile-based image from scratch
docker compose up -d --force-recreate # Replaces running containers with the newly built/pulled images--force-recreate matters: Restarting an existing container (docker compose restart) keeps the same image and the same old PHP binary inside it; it does not pull or rebuild anything.
Verify every PHP-touching container, not just the main web one:
docker compose exec app php -v
docker compose exec queue php -v # If you run a separate queue-worker service
docker compose exec scheduler php -v # If you run a separate scheduler/cron serviceNever do this for a security update: Never run docker compose down -v to patch PHP — the -v flag deletes named volumes, which typically include your database and any persisted uploads. This is a patching task, not a reset.
Mutable tags, briefly: a tag like php:8.3-fpm can point to a different underlying image over time as new patch builds are published upstream — that is exactly how you get the fix, but it also means re-running the same pull command later can pull something different without you changing a single line of your own config. If you need a guaranteed, unchanging reference (for example, for a reproducible build), pin to a specific image digest instead of a floating tag — but for a routine security patch, pulling the current tagged image is the normal, correct move.
E. Shared Hosting / cPanel / Plesk
Who this is for: sites hosted on shared/managed hosting without SSH access.
Your hosting panel (cPanel's "MultiPHP Manager," Plesk's "PHP Settings," or similar) typically lets you pick a PHP branch per domain, like "8.3" — but that selector usually shows only the branch, not the exact security patch level installed underneath it. Whether your specific domain is actually patched for these two CVEs depends on whether your hosting provider has updated the PHP packages behind that selector on their servers — something only they control.
What to do:
- In your panel, confirm which PHP branch your domain is set to.
- Contact your host and ask them to confirm the exact patch level, since the panel's branch label won't tell you.
Copyable message to send your host:
Hi — I'd like to confirm the PHP security patch level for my domain [yourdomain.com].
PHP's October 21, 2026 security release fixed two critical vulnerabilities,
CVE-2026-109618 and CVE-2026-109619, in PHP 8.2.35, 8.3.36, 8.4.27, and 8.5.12.
Can you confirm which exact PHP version/package revision is currently running my
domain, and whether it includes the fixes for these two CVEs? If not yet applied,
when will it be? Thank you.F. Windows / XAMPP / WAMP / IIS
Who this is for: PHP running natively on Windows, including via XAMPP, WAMP, or IIS with FastCGI.
A PHP source version (like "8.3.36") and a Windows package build of that version are two different things — PHP's Windows builds bundle their own copies of certain support libraries, including Brotli (used for HTTP compression). That is exactly what the October 26 update addressed: a separate issue, CVE-2026-111497, an out-of-bounds write in the bundled Brotli decompression library, fixed by rebuilding the Windows packages as 8.2.35-1, 8.3.36-1, 8.4.27-1, and 8.5.12-1(the -1 denotes the rebuilt Windows package, not a new PHP version). It is reachable through PHP's cURL extension specifically when automatic HTTP response decompression is enabled — check your own cURL usage before assuming it applies to you.
Windows needs both fixes: If you are on Windows, you need both the October 21 core update and the October 26 Windows-specific rebuild. Get both by downloading the current Windows build directly from windows.php.net rather than an older cached installer.
Match your existing configuration exactly:
- Architecture — x64 vs x86, matching what you already have installed.
- Thread safety — Thread Safe (TS) if you are running mod_php-style inside Apache/IIS via a threaded SAPI; Non-Thread-Safe (NTS) if you are running PHP-FPM/FastCGI. Installing the wrong one breaks compatibility with extension DLLs.
- Extensions (DLLs)— each PHP build ships its own matching set of extension DLLs. Don't copy an extension DLL from a different PHP version or thread-safety build into a new install; download the matching extension build for your exact new version instead.
For XAMPP/WAMP specifically, follow that distribution's own documented upgrade process rather than manually swapping PHP folders inside it — these bundles pin specific PHP builds and expect their own updater or a full reinstall of the PHP component, not an ad-hoc file copy.
G. Managed Hosting, Forge, and Other Platforms
Who this is for: Laravel Forge, Laravel Cloud, or similar managed platforms.
On managed platforms, you typically control which PHP version your site/server is configured to use, and when you apply that change. The platform typically controls the actual PHP package builds available to select, and how quickly a new patched build becomes available after an upstream release. Check your platform's own PHP version selector and documentation for the exact current labeling — this guide won't guess at a specific dashboard's wording, since that changes between providers and over time.
If you run multiple servers or replicas (common on Forge and similar tools — load balanced servers, staging/production pairs), the patched version has to be applied to every one of them individually; updating one server doesn't update its siblings.
8. Laravel-Specific Checklist
Run these from your project root, after the PHP runtime itself is patched — they don't replace section 6, they're compatibility and process checks on top of it.
composer check-platform-reqs # Confirms your installed packages are actually compatible with the new PHP version
composer audit # Checks your Composer dependencies for known vulnerabilities (packages, not PHP itself)Then run your application's test suite, and manually smoke-test the flows that exercise real code paths: login, a key API endpoint, a file upload if you have one, checkout/database writes, outbound mail, and anything queued.
Restart long-running processesusing whatever process manager your project actually uses — Supervisor config, systemd units, or your hosting platform's process controls. A few Laravel-specific commands worth knowing (verify these exist and match your installed Laravel version before running them, since behavior can change between versions):
php artisan queue:restart— signals currently running queue workers to finish their current job and then exit gracefully, rather than killing them mid-task. It does not restart anything by itself; your process manager (Supervisor, systemd, etc.) has to be configured to automatically bring the worker back up after it exits — otherwise it just stops. After running it, confirm the old worker processes actually exited and that whatever replaces them is running from the newly patched PHP binary.- Horizon (if installed) has its own lifecycle commands (e.g.,
php artisan horizon:terminate) with similar graceful-exit-then-restart semantics — check Horizon's installed version's own documentation for the exact current command and behavior rather than assuming it matches an older version you may be used to. - Octane(if installed) reloads or restarts its application server process depending on which command you use — a "reload" can sometimes keep old application state cached in memory between requests, so after a PHP runtime change, prefer a full restart of the Octane process over a reload, and confirm via a version check that requests are actually being served by the new binary.
What this checklist is not: Clearing caches (optimize:clear), running migrations, regenerating keys, or running Composer as root are routine maintenance or deployment tasks — none of them patch the PHP runtime, and doing them doesn't substitute for actually updating and restarting PHP. In particular, don't run php artisan key:generate on an existing production application — it replaces your active encryption key, which will immediately invalidate existing encrypted data and currently logged-in sessions. It has nothing to do with this PHP update.
9. WordPress and Other PHP Platforms
The same runtime-first principle applies regardless of what's built on top of PHP:
- Patch the hosting PHP runtime first — through your host's PHP version selector (section E) or your own server (sections A–D), depending on your setup.
- Check compatibility of your CMS version, theme, and plugins against the target PHP version — most mainstream WordPress plugins and themes document a supported PHP range; check the handful you actually run before assuming everything just works.
- Update application components separately, as normal — WordPress core, plugin, and theme updates are routine maintenance, not a substitute for the runtime patch, and vice versa.
- Test the real flows: admin login, any public-facing forms, WooCommerce checkout if you run it, media/file uploads, and anything scheduled via WP-Cron.
The identical layering applies to Symfony, Drupal, Joomla, CodeIgniter, and any plain PHP application: the CMS or framework is code that runs on PHP, never a replacement for it, and none of them can patch a vulnerability that lives underneath them in the runtime itself.
10. What If I Can't Update Immediately?
Everything in this section reduces exposure temporarily — none of it is a complete fix. The only complete fix is the patched PHP runtime itself.
Per PHP's own October 21 guidance, the following reduce risk while you arrange the real update:
- session.upload_progress.enabled=0 — disables the session upload-progress feature that is one of the two named vectors for CVE-2026-109619 (and is also the specific setting PHP recommends for the separate CVE-2026-115560, below).
- A conservative post_max_size— PHP's guidance suggests roughly 8M if your application accepts no uploads at all, or around 32M if it does — reducing the size of request bodies PHP will even attempt to process.
- On PHP 8.5 specifically, fatal_error_backtraces=0 — disables the other named vector for CVE-2026-109619.
- Container or cgroup-level memory limits, as an operating-system enforced backstop in addition to PHP's own memory_limit.
About memory_limit=-1 (unlimited): PHP's guidance lists this as one option, but treat it carefully. Removing PHP's memory limit doesn't fix the underlying bug — it just changes how a memory-related failure can happen, and an unconstrained PHP process can now consume all available server memory under the right (or wrong) conditions, which is its own resource-exhaustion risk. This is a call for whoever actually manages your server's capacity planning, not a default setting to flip without thinking about what else runs on that machine.
Timing matters: these are php.ini / FPM pool-level settings, which PHP only reads when a worker starts — they must be in place before a request begins being parsed, and every existing worker needs to be restarted after you change them for the new setting to take effect. A config file edit with no restart protects nothing.
None of these are complete fixes: A web application firewall (WAF), disabling file uploads, hiding PHP errors, adjusting only memory_limit, or adding application-level middleware might incidentally reduce some exposure, but none of them touches the actual bug in the PHP runtime.
The separate CVE-2026-115560 issue: PHP's October 23 informational advisory covers a related but distinct problem that is not fixed by the October 21 releases — it is still pending a fix in PHP's next routine release, expected around November 19, 2026. It only applies under an unusual, specific configuration: your application sets session.serialize_handler to php_binary or php_serialize in configuration, then switches it to php at runtime via ini_set() before calling session_start(), with session upload-progress still enabled.
If that doesn't describe your application's session handling, this specific issue likely doesn't apply to you — but if you're unsure, the same mitigation (session.upload_progress.enabled=0) covers it too, and is worth applying regardless while you wait for the real fix.
11. Confirm the Update Really Worked
- CLI PHP version confirmed patched (php -v)
- Web-facing runtime confirmed separately (temporary, access-restricted check — then deleted)
- Correct FPM pool / domain confirmed, if you run more than one
- Every application server/replica checked, not just one
- Queue worker and scheduler processes/containers checked individually
- Full vendor package revision checked where a distro backport applies
- Old worker processes confirmed actually exited
- All previously installed extensions still present (php -m)
- Application tests pass; key user flows manually verified
- Logs checked for new errors introduced by the version change
- Any temporary diagnostic file removed
A version check confirms one thing, not two: A version check tells you the patch is in place now — it says nothing about whether the server was compromised before you patched. If you have specific reason to suspect prior compromise, that's a separate investigation (section 13), not something a clean version number can rule out. Don't try to "test" the fix by attempting to exploit your own live, patched site — that's unnecessary risk to production for a check a version number already answers.
12. Troubleshooting
| Problem | Likely cause | What to check |
|---|---|---|
| Terminal shows new version, browser doesn't | CLI and web SAPI are different binaries/pools | Re-run the web-facing check from section 5; confirm which FPM pool/socket actually serves that domain |
| Hosting panel still shows only “8.3” | Panel shows branch, not patch level | Ask your host for the exact package revision (section E) |
| Composer reports incompatible PHP requirement | A dependency declares a PHP range that excludes your new version | Run composer check-platform-reqs; update the specific package or adjust config.platform.php for planning, not as a runtime fix |
| An extension is missing after update | New PHP version package didn't pull in the same extension set | Compare your pre-update php -m output; install the matching extension packages for the new version |
| 502 Bad Gateway | Nginx can't reach the PHP-FPM socket, often because FPM failed to restart cleanly | systemctl status php8.3-fpm (or your version); check FPM's error log |
| FPM socket path mismatch | Nginx config points at a socket path for the old version | Compare the fastcgi_pass socket path in your Nginx vhost against the actual new FPM pool's listen path |
| Old workers still running | Service restarted, but some processes didn't exit | ps aux | grep php-fpm; if stale processes remain, investigate why the restart didn't fully cycle them rather than force-killing blindly |
| Docker build uses a cached image | docker compose up without a rebuild reuses the old image layer | docker compose build --no-cache then up -d --force-recreate |
| Valet site uses an isolated runtime | Global valet use doesn't affect isolated sites | valet isolated to check, then valet isolate php@X.Y on that specific site |
| Errors appear only after changing branches, not just patch version | Genuine behavior/deprecation differences between PHP branches | Check your framework's and key plugins' documented compatibility notes for the specific branch jump |
13. If Compromise Is Suspected
Patching closes the door — it does not undo anything that already happened through it. If you have specific signs of prior compromise (unexpected admin accounts, unfamiliar files, outbound traffic you can't explain):
- Preserve evidence and logsbefore cleaning anything up — don't overwrite logs or files you might need to investigate later.
- Involve your hosting provider or a security professional, especially on shared or managed hosting where you don't control the full stack.
- Isolate affected systems where you reasonably can, to limit further exposure while you investigate.
- Review for unauthorized changes — new admin users, modified core files, unfamiliar scheduled tasks, unexpected outbound connections.
- Rebuild from a known-clean, trusted state when warranted, rather than hand-cleaning a system you can no longer fully trust.
- Rotate potentially exposed secrets — database credentials, API keys, encryption keys — from a clean, already-patched environment, not from the potentially compromised one.
Patching alone does not repair a prior compromise: Patching the PHP runtime does not, by itself, remove an attacker who already gained access, nor does it repair any changes they made. Those require separate investigation and remediation.
14. Frequently Asked Questions
Is Laravel affected?
Laravel applications run on the PHP runtime, so yes — the vulnerability is in PHP itself, beneath Laravel, not in Laravel's own code.
Does Composer update PHP?
No. Composer manages PHP libraries your code depends on; it has no ability to install, update, or replace the PHP interpreter running on your server.
Is PHP-FPM safe?
PHP-FPM is named in the official advisory as an at-risk SAPI, the same as mod_php, php-cgi, FrankenPHP, and LiteSpeed. The confirmed, demonstrated exploit in the advisory specifically used Apache mod_php — that does not mean FPM is exempt, only that it was not the configuration used in that particular public demonstration.
Is my website safe because it requires login?
No. The vulnerable code runs during PHP's own request handling, before your application's login check ever executes.
What if my site has no upload page?
Not sufficient on its own — one of the two named attack paths for CVE-2026-109619 can be reached through session upload-progress machinery triggered by any POST request, not only a form your application built for file uploads.
Is changing composer.json enough?
No — it only affects which package versions Composer will install; it installs no PHP interpreter and changes nothing about what is actually running on your server.
Can I stay on the same PHP branch?
Yes — updating from, for example, 8.3.19 to 8.3.36 fully closes these two CVEs within the same branch, with the smallest realistic compatibility risk. You do not need to jump to a newer branch just to apply this fix.
Why does my provider show an older version after patching?
Hosting panels often display only the PHP branch, like “8.3”, not the specific patch level underneath. Ask your host directly to confirm the exact package revision.
Must I upgrade Laravel too?
No — Laravel 12.x's own requirement is PHP 8.2 or newer, so a patch-level PHP update within your current branch satisfies that requirement without any Laravel version change.
Does updating my laptop patch production?
No. Your local development environment (Homebrew, Valet, XAMPP, a local Docker setup) is entirely separate from whatever actually runs your live site. Each has to be updated and verified independently.
Does the fix mean my website was never compromised?
No — a current version number confirms your patch status going forward. It says nothing about whether the server was compromised before the patch was applied.
What should I ask my hosting provider?
Ask them to confirm the exact PHP package revision running your specific domain, and whether it includes the fixes for CVE-2026-109618 and CVE-2026-109619 — a copyable message is included in the shared-hosting section of this guide.
15. Final Action Checklist
- Identify the actual PHP version serving your live website — not your CLI, not your laptop.
- Install a patched release for your branch: 8.2.35, 8.3.36, 8.4.27, or 8.5.12 — check php.net first for anything newer.
- On Windows, also apply the October 26 Brotli rebuild if you use cURL with automatic decompression.
- Restart the actual process that serves PHP — FPM, Apache, your container, or your host's PHP service — not just the web server in front of it.
- Re-run the web-facing version check, then delete the temporary diagnostic file.
- Restart queue workers, Horizon, Octane, and scheduled processes separately, and confirm they're running the new binary.
- Run your test suite and smoke-test real user flows.
- If you can't update immediately, apply the official mitigations in section 10 as a stopgap, not a solution.
- If you suspect prior compromise, follow section 13 before assuming the patch alone resolved it.
References: GHSA-w8hw-w9fx-j79w (CVE-2026-109618), GHSA-6wmf-4rv5-p3rp (CVE-2026-109619), PHP News Archive 2026, PHP Supported Versions, The PHP Foundation's release writeup, CISA KEV catalog addition, oss-security advisory on CVE-2026-115560, Laravel 12.x Deployment docs, and Laravel 12.x Valet docs.
Need Help Patching or Maintaining Your Site?
The 92 Nodes team handles ongoing server maintenance so updates like this one never catch you off guard.
Talk to 92nodes