Part of the guide: Website Security on Shared Hosting: Practical Guides

PHP Security Update October 2026: cPanel & XAMPP Fix

By Published 12 min read Security
PHP Security Update October 2026: cPanel & XAMPP Fix

On 21 October 2026 the PHP project shipped an emergency release for two critical bugs now called PHP2Shell. The PHP security update October 2026 fixes CVE-2026-109618 and CVE-2026-109619, both rated CVSS v4.0 9.2: remote code execution in PHP itself, before your application code runs. Updating WordPress, Laravel or any plugin does nothing here. The interpreter has to change.

Quick answer: you need PHP 8.2.35, 8.3.36, 8.4.27 or 8.5.12. On shared cPanel hosting the host installs it and you verify it with a three-line file. On Windows XAMPP you replace C:\xampp\php yourself, because the XAMPP installer still ships old PHP. The whole check takes 15 minutes; a Laravel app stuck on PHP 8.1 takes longer.

What PHP2Shell is and who is affected

Both bugs live in PHP's request handling. CVE-2026-109618 is an out-of-memory condition during request start-up that leaves internal structures half-built; the cleanup code later treats them as valid. CVE-2026-109619 is stale global state: a worker keeps a reference to memory freed in the previous request, and the next request can land on it. In both cases an attacker needs no account, no login and no bug in your own code.

The advisory lists every install that serves web requests: PHP-FPM, Apache mod_php, php-cgi, LiteSpeed's PHP handler and FrankenPHP. It calls the risk "highest for mod_php", because the demonstrated attack happens before any application middleware sees the request. FPM and LiteSpeed are not exempt; they are just one step further from the proof-of-concept. Source: PHP security advisory, 21 October 2026.

Fixed versions, released 21 October 2026 (php.net release archive):

BranchFixed versionWhat to do
PHP 8.58.5.12Update
PHP 8.48.4.27Update
PHP 8.38.3.36Update
PHP 8.28.2.35Update, and plan to leave 8.2: it reaches end of life on 31 December 2026
PHP 8.1 / 8.0 / 7.4No upstream release; minimal source backports onlyYour vendor must ship the backport; the version number may not change

That last row is where most confusion comes from, and it gets its own section below.

Find the PHP your website actually runs

cPanel's MultiPHP Manager shows a branch ("PHP 8.3"), not the build. The difference between 8.3.35 and 8.3.36 is the whole story this week, so you need the real number from the process that serves your pages.

Create a file in public_html called php-check.php:

<?php
header('Content-Type: text/plain; charset=UTF-8');
echo 'PHP version : ' . PHP_VERSION . "\n";
echo 'Handler     : ' . php_sapi_name() . "\n";
echo 'php.ini     : ' . (php_ini_loaded_file() ?: 'none') . "\n";

Open https://example.com/php-check.php. A typical result on Namecheap looks like:

PHP version : 8.3.36
Handler     : litespeed
php.ini     : /opt/alt/php83/etc/php.ini

PHP_VERSION is the build that executed the request. php_sapi_name() tells you the handler (litespeed, fpm-fcgi, apache2handler, cgi-fcgi), which decides where a mitigation has to be set. php_ini_loaded_file() tells you which php.ini actually applies, so you do not edit the wrong one.

Delete the file as soon as you have the answer; it leaks environment details. If you want to keep it for a day, restrict it to your own IP in .htaccess:

<Files "php-check.php">
    Require ip 203.0.113.10
</Files>

Two traps. First, if the site sits behind a page cache or a CDN, purge it before you read the result, or you may be looking at a response generated before the host patched. Second, the command-line PHP is not the web PHP:

php -v

can print PHP 8.2.35 (cli) while the browser shows 8.3.36 (litespeed). Shared hosts install several versions; CLI is what Composer and cron use, the handler is what attackers reach. For PHP2Shell the browser number is the one that matters.

I run HeadsNTail on Namecheap cPanel hosting and my Laravel HR apps sit on the same kind of shared hosting. On 22 October the first thing I did was upload exactly that three-line file. MultiPHP Manager said "8.3"; the file said 8.3.36 on LiteSpeed. That tiny file is the whole check. Everything else is trusting an email.

Shared cPanel hosting: the host patches, you verify

On shared hosting you cannot install PHP packages. The host does, and the patched builds were available quickly:

  • cPanel EasyApache 4, change log 25.91 (21 October 2026): ea-php82 8.2.34 → 8.2.35, ea-php83 8.3.35 → 8.3.36, ea-php84 8.4.26 → 8.4.27, ea-php85 8.5.11 → 8.5.12. The fix was backported to ea-php74, ea-php80 and ea-php81 without a new PHP version number. Source: EasyApache 4 change log.
  • CloudLinux (alt-php, used by many shared hosts including LiteSpeed set-ups): fixed packages for alt-php 5.2 through 8.5 reached the stable repositories for CloudLinux 7, 8 and 9 on 22 October at 00:07 UTC. Source: CloudLinux blog.

So on a 8.2-8.5 branch, the checker file must show the new number. On 7.4, 8.0 or 8.1 it will show the old number even when patched, because only the package release changed. An administrator verifies a backport from the package changelog, for example:

rpm -q --changelog ea-php81-php-cli | grep -E 'CVE-2026-10961[89]'

Shared-hosting customers cannot run that, so the ticket to your host has to ask for the right thing. Do not accept "PHP is updated". Ask this:

Subject: PHP2Shell (CVE-2026-109618 / CVE-2026-109619) - please confirm my account

Domain: example.com
PHP selected in MultiPHP Manager: 8.3
Handler reported by php_sapi_name(): litespeed

Please confirm:
1. The exact installed package and build (ea-php83 / alt-php83, release number).
2. That both CVE-2026-109618 and CVE-2026-109619 are included in that build.
3. That the PHP workers (LiteSpeed/FPM) were restarted after the update.
4. Whether your WAF (Imunify360 or other) has the PHP2Shell rules enabled.
5. If my branch is 7.4/8.0/8.1 and the version number did not change,
   the package release number that contains the backport.

Point 3 is the one everyone forgets. A patched package on disk does not help while the old workers keep running.

Before you touch anything else, take your own copy of the site. The host's backup is restored only by the host; a copy outside the account is yours. Our automatic cPanel backup to Google Drive guide covers the cron job.

Laravel on cPanel: patch first, change branch second

A Laravel app on PHP 8.2 or newer only needs the host's patched build and nothing changes for you. The problem is the app still on PHP 8.1: upstream gave it no release, only a backport your host may or may not have shipped, and Laravel 12 needs PHP 8.2 or newer, Laravel 13 needs 8.3 or newer. So treat it as two jobs:

  1. Today: confirm the 8.1 backport with your host (ticket above). That closes the hole.
  2. This week: move the app to 8.3 in a controlled way, because 8.1 will not get the next fix either.

Check what the app allows before switching:

cd /home/youraccount/laravel-app
composer check-platform-reqs
grep '"php"' composer.json

check-platform-reqs compares the installed PHP and extensions against every package's declared requirements; it catches the one package that still pins php: ^8.1. Then compare extensions, because a new branch in MultiPHP Manager starts with its own extension set:

php -m

Laravel itself wants mbstring, openssl, pdo_mysql, fileinfo, curl, xml; apps that make PDFs, Excel files or thumbnails also need gd or imagick and zip. Tick them in MultiPHP INI Editor or Select PHP Version for the new branch, then rebuild the caches with the PHP that will serve the site:

php artisan optimize:clear
php artisan optimize

Clear first, then build; a config cache written under 8.1 can point at paths that do not exist for 8.3. If the site returns 500 afterwards, read storage/logs/laravel.log before changing anything: "Call to undefined function", "Class not found" and "Failed opening required" each name the exact missing extension, package or path.

Two deprecations hit almost every older app. utf8_encode() is deprecated since PHP 8.2:

// before
$value = utf8_encode($value);

// after - only if the input really is ISO-8859-1
$value = mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1');

Check the source encoding first: converting text that is already UTF-8 as if it were ISO-8859-1 mangles Arabic and Urdu. The second is dynamic properties, deprecated since 8.2 on ordinary classes:

class Employee
{
    public string $name;
    public string $department;   // declare it instead of creating it on the fly
}

For the HR apps the real work was moving the one app still on PHP 8.1 to 8.3, because 8.1 only got a silent backport. Two old helpers using utf8_encode broke; mb_convert_encoding fixed them. After the switch, test what normal browsing never touches: login, file uploads, PDF and Excel export, the queue worker and the scheduler. The surrounding setup (document root, cron line, permissions) is in the Laravel on cPanel deployment checklist.

XAMPP on Windows: the installer is still unpatched

As of 23 October 2026 the Apache Friends download page offers XAMPP with PHP 8.2.12, 8.1.25 and 8.0.30, all older than the fix. Reinstalling XAMPP does not help. You replace the PHP folder with a patched build from windows.php.net.

  1. Identify the build you need. XAMPP runs PHP as an Apache module, so you need the Thread Safe (TS) x64 zip for your branch, built with VS16 (the 8.2 line on windows.php.net). The Non-Thread Safe build is for FastCGI and will not load as php8apache2_4.dll. The build needs the Microsoft Visual C++ 2015-2022 redistributable (x64); XAMPP usually installed it already.
  2. Stop Apache in the XAMPP Control Panel, then rename C:\xampp\php to C:\xampp\php-old. Do not delete it: it is your rollback, and it holds your php.ini and any extra extension DLLs.
  3. Extract the zip into a new C:\xampp\php.
  4. Bring your settings back. Copy C:\xampp\php-old\php.ini to C:\xampp\php\php.ini and check extension_dir = "ext", date.timezone, memory_limit, upload_max_filesize, post_max_size. Do not copy the whole ext folder over the new one: extension DLLs must match the exact PHP build (version, x64, TS). Copy only the non-bundled DLLs you added yourself, and only if a build for the new version exists.
  5. Check and start.
C:\xampp\php\php.exe -v
C:\xampp\apache\bin\httpd.exe -t

-v must print the patched version; -t must print Syntax OK. If it reports a module error, the usual causes are an NTS build, a 32-bit zip, a missing VC++ runtime, or an old DLL copied into ext. Fix the named error; do not copy more DLLs. Start Apache, open a one-line phpinfo(); file, confirm PHP Version and Server API: Apache 2.0 Handler, then delete the file.

Locally I use XAMPP on Windows, PHP 8.2.12, and the XAMPP site had nothing newer. I replaced C:\xampp\php with the 8.2.35 Thread Safe zip, copied my old php.ini back, restarted Apache, and phpinfo showed the new build in ten minutes. Local XAMPP only matters when you expose it with ngrok or on the office LAN, but I test file uploads there all day, so I patch it.

Temporary mitigations: what the advisory actually says

The advisory is blunt: the mitigations "do not make an installation safe". They narrow the known attack paths while you wait for the host. Set them in php.ini or the FPM pool (php_admin_value), restart the workers, and remove them once patched. ini_set() in your code and .user.ini files are too late: the vulnerable code runs before either is read.

; php.ini or FPM pool - not .user.ini, not ini_set()
session.upload_progress.enabled = 0
post_max_size = 8M        ; sites with no uploads; 32M where uploads are needed
memory_limit = -1         ; only where the advisory's reasoning applies and the server has other limits
fatal_error_backtraces = 0 ; PHP 8.5 only

Two of these need judgement. memory_limit = -1 removes PHP's per-script ceiling, so on a shared or small server it trades one risk for a resource-exhaustion risk; do not set it through cPanel on a whim. post_max_size below your real upload size silently breaks uploads: upload_max_filesize must stay smaller than post_max_size, and multipart forms add overhead on top of the file.

A WAF helps at the edge. Cloudflare enabled PHP2Shell rules on 21 October at 03:02 UTC, Free plan included, and noted that attackers were already mutating payloads (Cloudflare blog). CloudLinux's Imunify360 ruleset 8.85 blocks the public exploit tool, but as of 21 October 23:00 UTC no rule covered the out-of-memory path (CloudLinux blog). Treat a WAF as a seat belt, not the fix, and check that your origin IP is not reachable directly around Cloudflare. If you manage .htaccess rules yourself, the security headers guide for cPanel shows the safe block.

Common problems

  • The version did not change after the update. On 7.4, 8.0 and 8.1 that is expected: the backport keeps the version string. Ask for the package release number, or the rpm --changelog line, instead of re-checking PHP_VERSION.
  • Page cache or CDN shows the old number. Purge, then re-run the checker. I had to purge my page cache first because the cached response hid the new version.
  • Updated on disk, still old in the browser. Workers were not restarted. On cPanel ask the host; on your own server restart php-fpm/LiteSpeed; on XAMPP restart Apache.
  • php_value lines in .htaccess throw 500 after a handler change. php_value only works under mod_php; under FPM or LiteSpeed move those settings to php.ini/MultiPHP INI Editor. Keep Laravel's rewrite rules.
  • Moving 8.1 → 8.3 breaks old code. Dynamic properties and utf8_encode() are the usual two; the log names the file and line.
  • Uploads fail after the mitigation. post_max_size is now below the form size; raise it to 32M or remove the mitigation once patched.
  • Host never answers. Keep the mitigations in place, set the Cloudflare WAF, and escalate with the CVE numbers in the subject line; vague tickets go to the bottom of the queue.

FAQ

Is updating WordPress or Laravel enough to fix PHP2Shell?

No. The bugs are in the PHP interpreter, not in any framework. WordPress.org said no WordPress update is required or planned. The host (or you, on XAMPP/VPS) must install the patched PHP build.

My host still shows PHP 8.1. Am I safe?

Only if the host shipped the vendor backport (cPanel and CloudLinux both did) and restarted the workers. The version string stays 8.1.x either way, so ask for the package release number. Then plan the move to 8.3: 8.1 gets no upstream releases at all.

Can I set session.upload_progress.enabled=0 and relax?

No. It closes one demonstrated path; the advisory says the mitigations do not make an installation safe. It is a stop-gap until the patched build is running, and it only works from php.ini or the FPM pool, not from ini_set() or .user.ini.

Does XAMPP on my own PC need patching?

If anything other than your own browser can reach it (office LAN, port forward, ngrok), yes. Even if not, the XAMPP installer still ships PHP 8.2.12 and will for a while, so swapping C:\xampp\php for the 8.2.35 Thread Safe build is ten minutes well spent.

Summary checklist

  • [ ] Checker file shows a fixed version (8.2.35 / 8.3.36 / 8.4.27 / 8.5.12) in the browser, not just php -v
  • [ ] On 7.4/8.0/8.1: host confirmed the backport by package release number
  • [ ] PHP workers restarted after the update
  • [ ] Mitigations set in php.ini/FPM pool while waiting, removed after patching
  • [ ] Laravel apps: composer check-platform-reqs, extensions ticked for the new branch, optimize:clear then optimize, uploads and exports tested
  • [ ] XAMPP: C:\xampp\php replaced with the TS x64 build, httpd -t says Syntax OK, phpinfo shows the new version
  • [ ] Checker file deleted

Free tools for Security work

All free tools →

More Security articles