How to Check the PHP Version Each Site Really Runs Before Patching PHP2Shell How to check the PHP version each site really runs: CLI vs web SAPI, WordPress Site Health, PHP-FPM, Docker tags, distro backports and one inventory table.
How to Check the PHP Version Each Site Really Runs Before Patching PHP2Shell
On October 21, 2026, PHP shipped 8.5.12, 8.4.27, 8.3.36 and 8.2.35 to fix the pair of flaws now called PHP2Shell (details of the flaws are here ). For most teams the first real question is how to check the PHP version each site actually runs.
Short answer: don't trust the shell alone. Confirm from the web side which PHP runtime serves the page (PHP-FPM, mod_php, LiteSpeed and so on), find the package that supplies it, record its exact version and repository, check both CVEs in that vendor's tracker, and keep every site in one table.
The care matters: php.net says every PHP installation serving web requests is affected (PHP-FPM, mod_php, php-cgi, FrankenPHP, LiteSpeed), and the UK NCSC warns against limiting the inventory to outdated versions.
Why doesn't php -v answer the question?
php -v asks the CLI binary on your PATH, not the one your web server calls. Per the PHP manual, php_sapi_name() returns cli on the command line but values such as fpm-fcgi, apache2handler or litespeed on the web, possibly from different binaries. FPM pools can use their own php.ini, the WP-CLI docs call the CLI php.ini "typically different than web", cPanel and Plesk set versions per domain, and containers carry their own PHP.
Checking from the web side: which method fits which site
Method What you learn Watch out for WordPress: Tools → Site Health → Info → Server Runtime PHP version and SAPI of the live request Not the package revision; wp cli info reports the CLI Trace fastcgi_pass to the FPM instance behind it Which FPM is configured for the site Confirm with a live request; binaries on disk can differ from running workers Protected diagnostic page or access-controlled route PHP_VERSION and PHP_SAPI through the real request pathNever a public phpinfo() docker exec <container> php -vThe container's CLI binary Not proof of what serves requests
WordPress is the easy case: Site Health's Server section shows "PHP version" and "PHP SAPI" read during that request, so it reflects the runtime serving the site (the screen can copy everything to your clipboard). That is the runtime version, not the package revision.
Laravel or in-house apps on nginx: fastcgi_pass names the configured FastCGI endpoint: a UNIX socket (Laravel's docs use unix:/var/run/php/php8.2-fpm.sock), a host and port, or a server group. Trace it to the FPM instance listening there, run that binary with -v, and confirm with a live request, since binaries on disk can differ from running workers.
For Apache with mod_php , LoadModule php_module is the PHP 8 name (PHP 7 used php7_module), so check the effective configuration including included files (httpd -M lists loaded modules) and confirm the SAPI from the web side. Put these sites first: php.net names mod_php the highest-risk case for PHP2Shell.
A minimal check file only works where the server runs it through the intended PHP handler; front-controller apps such as Laravel send requests through public/index.php, so a stray file may never run. Prefer an existing protected diagnostic page or an access-controlled route that prints only:
<?php echo PHP_VERSION, ' ', PHP_SAPI;
PHP_VERSION matches phpversion() and PHP_SAPI matches php_sapi_name(). Never publish a phpinfo() endpoint, even briefly: per the manual it prints environment variables and all EGPCS data, so a database password kept in an environment variable can land on the page. X-Powered-By is a hint at best: expose_php adds it by default, but it can be off, and Laravel's sample config hides it.
On shared hosting , the control panel shows the version (cPanel's "inherit" is the administrator's system default); patching the build is the provider's job, so ask them in writing for the package version and patch status.
For Docker , docker exec <container> php -v checks the container's CLI binary and ENV PHP_VERSION is image metadata; neither proves which worker answers requests, so confirm through the site's request path. Tags are mutable and a built image is a snapshot (Docker docs), and rebuilding with --pull doesn't change running containers: build from a verified fixed base, replace containers through your normal deployment and recheck every serving replica. A base pinned to an affected version stays affected.
The version looks old but is patched. How?
Distributions backport fixes into the version they shipped (Debian's Security FAQ), and Red Hat warns the version number alone can't tell you whether you're vulnerable. Ubuntu 24.04's php8.3 package carries upstream version 8.3.6 (package 8.3.6-0ubuntu0.24.04.x), far below 8.3.36, so the runtime number alone would flag it forever. Ubuntu 22.04's php8.1 (8.1.2-1ubuntu2.x) still gets updates (Launchpad shows one on October 21, 2026) though php.net ended 8.1 on December 31, 2025.
So check the package behind the web runtime (all read-only):
Find the package that supplies the binary: dpkg -S <path> on Debian/Ubuntu, rpm -qf <path> on RHEL and rebuilds such as AlmaLinux
Record its exact version and repository: apt-cache policy <package> shows the source; apt-get changelog <package> or rpm -q --changelog <package> shows its history
Check both CVEs on that vendor's tracker , such as Ubuntu CVE-2026-109618 or the Debian tracker for CVE-2026-109618 (and the CVE-2026-109619 pages): compare your package with the current fixed line for your release, per package.
A suffix in another format (the WP-CLI docs show +deb.sury.org+1) means another repository built the package; use that repository's evidence. Official Docker php images compile PHP from php.net source, so compare them with PHP's fixed releases, not a distro tracker (Sansec reports that image gets no fix for 8.1 or older, branches php.net no longer updates).
A PHP inventory table that works
All of this needs a complete site list; old microsites and vendor appliances with PHP inside slip through (Canada's Cyber Centre and the NCSC say to count them). If your hosts are scattered, zcrCTEM's EASM and CAASM can seed that list: EASM discovers internet-facing hosts starting from your root domains, CAASM merges hosts, devices and cloud resources from connected sources into one inventory, and Sync Health warns when a source goes quiet. The PHP version on each host still comes from the checks above.
Example (made-up data):
Site Host SAPI Runtime / package (repo) Source of version Patched? Owner www.example.com Shared hosting (cPanel) litespeed Unknown / ask provider MultiPHP Manager + protected check Unknown, verification pending Marketing shop.example.com VPS, Ubuntu 24.04 fpm-fcgi 8.3.6 / 8.3.6-0ubuntu0.24.04.x (Ubuntu archive) Site Health + dpkg -S, apt-cache policy Unknown, verification pending Web team api.example.com Container php:8.3-fpm Not yet checked CLI only so far docker execUnknown, verification pending Dev team intranet Old Apache server apache2handler 7.4.x / unknown Protected check Unknown, verification pending (7.4 is end of life upstream) IT Vendor system Appliance Unknown Unknown Ask vendor Unknown Procurement
Mark a row patched only with the exact installed build, tracker evidence for both CVEs, and confirmation that updated workers are serving requests. The steps:
Complete the list first: DNS, hosting accounts, cloud, containers, appliances
Check each site from the web side with the methods above
Record the runtime version and the exact package build with its repository , then check both CVEs
Order by urgency: internet-facing first, mod_php first within that
Restart or replace every PHP worker after patching and recheck from the web side. (Plan to leave 8.2 too: it reaches end of life on December 31, 2026.)
If the list outgrows a spreadsheet, see zcrCTEM packages and pricing or try the demo before you decide.
Can't patch today?
PHP says workarounds reduce exposure but don't make you safe. See how far a WAF gets you before you patch , then hunt for PHP2Shell in your logs on any unpatched host.
Limits
These methods give the version , not whether the host was already compromised
A web-side check reports only the pool that served that request; check every pool
Tracker status changes; look it up when you decide
Free tools do this job well
For a handful of sites, Site Health, apt or rpm, the tracker and a spreadsheet are enough. A platform earns its place when the site list itself is what you can't produce.
📞 Free Consultation
Related articles
Continue with these guides on inventory and external exposure:
Related product
See zcrCTEM for the scope of EASM and CAASM, and the pricing page for published prices.
Sources
PHP News Archive 2026 — fixed versions, affected SAPIs, mod_php as highest risk, PHP 8.1 and older, 8.2 end of life
PHP Manual — php_sapi_name() — cli, fpm-fcgi, apache2handler, litespeed and PHP_SAPI
PHP Manual — phpversion() — relation to PHP_VERSION
PHP Manual — phpinfo() — EGPCS and environment output
PHP Manual — FastCGI Process Manager (FPM) — pools with different php.ini
PHP Manual — Apache 2.x on Unix — LoadModule php_module; php-src UPGRADING 8.0 — renamed from php7_module; Apache httpd(8) — -M
PHP Manual — php.ini core directives (expose_php)
php-fpm man page (php-src) — the -v option; nginx — fastcgi_pass — socket, address or server group
PHP — Unsupported Branches — 8.1 end of life
WordPress — Site Health screen and WP_Debug_Data::get_wp_server()
WP-CLI — wp cli info
Laravel 12.x — Deployment — public/index.php front controller, fastcgi_pass and fastcgi_hide_header X-Powered-By
cPanel — MultiPHP Manager and Plesk — PHP Settings
Docker — Building best practices , docker container exec and the official image Dockerfile
Debian Security FAQ , Red Hat — Backporting Security Fixes and Ubuntu — About CVE statuses
Launchpad — php8.1 in Ubuntu and Launchpad — php8.3 in Ubuntu
dpkg(1) , apt-cache(8) , apt-get(8) and rpm(8)
Sansec — PHP2Shell , UK NCSC and Canadian Centre for Cyber Security AL26-026
Products | Pricing | Resources | Insights | Contact