# PHP Still Showing the Old Version After an Update (cPanel & CloudLinux)

Source: https://srvscripts.com/guides/php-still-showing-old-version-after-update/
Updated: 2026-10-28
Publisher: srvScripts (https://srvscripts.com/)

You updated PHP on a cPanel server — `yum update` finished, `rpm -q` shows the new build, the command line agrees — but a website keeps reporting the old version in `phpinfo()`. First confirm the packages really did update (`rpm -q` and `php -v` should show the new build); if they do not, that is a different problem. Once they have, a common cause of the lingering old version is that a long-running PHP worker is still executing the **old binary from memory** and will keep doing so until it is recycled — but a version mismatch has other causes too, so this guide confirms the stale worker before you act on it.

**In short:** a PHP package update replaces the files on disk, but it does not, on its own, recycle the PHP workers (`lsphp` under LiteSpeed/LSAPI, or `php-fpm`) that a site already has running — so do not assume the update restarted them. Linux keeps the old executable mapped in memory even after the file is replaced, so the worker serves the old version and the kernel marks its `/proc/PID/exe` with `(deleted)`. Recycle the affected worker — or, when several are affected, its handler — and the new version is served. Before you do, rule out the ordinary causes of a version mismatch (the wrong version assigned in MultiPHP Manager, a separate PHP install, or a cached `phpinfo`), because not every mismatch is a stale worker.

## Quick check (one command)

Read-only and safe to run as root. List your PHP web workers — both `lsphp` (LSAPI) and `php-fpm` — then ask the kernel what binary one is really running. A path ending in `(deleted)` is a stale worker:

```
ps -eo pid,user,etime,comm | grep -E '[l]sphp|php-fpm'   # lsphp (LSAPI) and php-fpm workers
readlink /proc/PID/exe                                    # ...ends in " (deleted)" = stale worker
```

Use the symptoms below to point yourself at the likely cause before reading further. A `(deleted)` executable confirms the running file was replaced on disk; it does not on its own prove the version is older, so always confirm the version actually served.

| Symptom | Most likely cause | First check |
| --- | --- | --- |
| rpm -q / php -v show the new version, but one site shows the old one | a worker still running the replaced binary | readlink /proc/PID/exe ends in (deleted) |
| CLI and cron are correct; only the website is wrong | a persistent web worker not yet recycled | the worker started before you ran the update |
| Every site shows the wrong version | wrong version assigned, or a stale PHP-FPM master | MultiPHP Manager; the php-fpm master’s /proc/PID/exe |
| Version never changes, even after a restart | a different PHP stack, or a handler misassignment | which /opt path the domain’s handler uses (EA-PHP vs Alt-PHP) |

## The six places a PHP version can come from

“PHP is 8.3.36” is never a single fact on a cPanel/CloudLinux server. Before chasing a mismatch, be clear about which version you are reading, because the fix is different for each:

| Where you look | How you read it | What it is |
| --- | --- | --- |
| Installed package | rpm -q ea-php83-php-cli | the version written to disk by the package manager |
| PHP CLI | /opt/cpanel/ea-php83/root/usr/bin/php -v | what cron jobs and WP-CLI run — a separate binary from the web one |
| LSAPI binary | /opt/cpanel/ea-php83/root/usr/bin/lsphp -v | the on-disk LiteSpeed PHP binary the web server launches |
| PHP-FPM runtime | the running php-fpm workers | the version FPM pools are actually serving |
| Served to visitors | phpinfo() on the site, or a response header | what the live site runs right now — the only number visitors see |
| Assigned version | WHM › MultiPHP Manager | which PHP version each domain is set to use |

Two more distinctions matter on these servers. **EasyApache (EA-PHP)** under `/opt/cpanel/ea-phpNN/` is the stack cPanel serves most sites with; **CloudLinux Alt-PHP** under `/opt/alt/phpNN/` is a separate set of binaries exposed through the PHP Selector. A domain can be updated in one stack while you are checking the other. And the package version (on disk) is not the running version: a worker that started before the update keeps running until it is recycled. That gap is what this guide is about.

## Confirm it is a stale worker

Work from the package down to the running process. First confirm the update really landed on disk:

```
rpm -q ea-php83-php-cli ea-php83-php-common ea-php83-php-litespeed
/opt/cpanel/ea-php83/root/usr/bin/lsphp -v
```

If those show the new version (for example PHP 8.3.36) but a site still reports the old one, look at the running web workers. Under LiteSpeed/LSAPI that is `lsphp`:

```
ps -eo pid,ppid,user,lstart,etime,args | grep '[l]sphp'
```

Note the **start time**. A worker that started before you ran the update is the suspect. Now ask the kernel what binary that process is really executing — replace `PID` with the process ID from the line above:

```
readlink /proc/PID/exe
```

The tell-tale answer is a path with `(deleted)` after it:

```
/opt/cpanel/ea-php83/root/usr/bin/lsphp (deleted)
```

That is the whole diagnosis: the file on disk was replaced by the update, but this process is still running the old copy that the kernel is keeping alive in memory, which it flags as `(deleted)`. Reading `/proc/PID/exe` is the one check that distinguishes a stale worker from every other cause — the package, the CLI and the on-disk binary can all look correct while a worker serves the old code. The companion script below does this check across every worker for you.

| What readlink /proc/PID/exe shows | What it means | What to do |
| --- | --- | --- |
| a path ending in (deleted) | confirmed: the worker runs a binary that was replaced on disk | recycle that worker, or restart the web server (see below) |
| a normal path, but the on-disk binary is newer than the worker’s start time | unverified: the binary changed after the worker started (could be an in-place rebuild, not a version change) | confirm the version actually served, then recycle only if it is genuinely old |
| a normal, current path | this worker is already on the current binary | look elsewhere — see “Rule out the other causes” below |

## Why an update does not change the running version

A package update (`yum update`, `dnf update`, EasyApache, or a CloudLinux `alt-php` update) does one thing: it replaces files on disk. It does not, by itself, stop and restart the PHP workers a site already has running. LSAPI keeps `lsphp` workers alive between requests for speed; PHP-FPM keeps a pool of workers under a master process. Those long-lived processes were launched from the old binary, and Linux keeps that executable mapped in memory for as long as the process lives — even after the file itself is replaced or unlinked. So the worker goes on serving the old version, and `phpinfo()` reports it, until the worker is recycled and a fresh one launches from the new binary on disk.

This is why `rpm -q` and `php -v` (which start a brand-new short-lived process every time you run them) immediately show the new version, while the web worker — which is persistent — does not. It is normal operating-system behaviour, not corruption.

## Rule out the other causes first

A stale worker is the most common reason, but it is not the only one. If `/proc/PID/exe` does not show `(deleted)` and the binary is current, check these before recycling anything:

- **The domain is assigned a different version.** WHM › MultiPHP Manager (or the user’s PHP Selector) may have the site set to another version entirely. You could be updating 8.3 while the domain runs 8.2.

- **Two PHP stacks.** You updated EA-PHP under `/opt/cpanel/ea-phpNN/`, but the site runs CloudLinux Alt-PHP under `/opt/alt/phpNN/` (or the reverse). Check which stack the domain’s handler points at.

- **A PHP-FPM pool.** If the domain uses PHP-FPM rather than LSAPI, it is the `php-fpm` master and its pool workers that must be recycled, and the master is restarted as a service — a per-worker kill will not help it.

- **A cached `phpinfo` or page cache.** If you are reading the version from a cached page (LiteSpeed Cache, a CDN, or a saved `info.php`), you may be looking at an old snapshot. Load an uncached `phpinfo()` or send a no-cache request.

- **OPcache.** OPcache stores compiled bytecode, not the PHP version, so it does not change the reported version — but it can make code changes look like they did not apply, which is sometimes mistaken for a version problem.

Only once you have confirmed the right version is installed and assigned, and a worker is genuinely running a replaced binary, is recycling the answer.

## Fix it safely

The right fix depends on the handler, so match it to what you found. **If the stale process is a PHP-FPM master**, the fix is to restart that version’s FPM service — recycling an individual worker will not help, because the pool’s workers are forked from the master:

```
systemctl restart ea-php83-php-fpm    # the affected version's FPM service
```

For a stale `lsphp` (LSAPI) or an FPM pool worker, use one of the two options below. Both interrupt any request that worker is mid-way through, so pick the one that fits.

### Option A — recycle just the affected worker

The least disruptive option, once you have positively identified the stale process and its owner. Send it `SIGTERM` so it can finish its current request where the handler allows, and the web server (or the PHP-FPM master) launches a fresh worker from the new binary on the next request:

```
kill -TERM PID
```

`SIGTERM` is not guaranteed to be graceful for every handler configuration, so a request in flight may still be cut off. Never run `killall lsphp` on a shared server — that drops every account’s in-flight requests at once. For the detail of terminating one user’s workers safely, including processes stuck in uninterruptible state, see our guide on [stuck lsphp processes](https://srvscripts.com/guides/litespeed-stuck-lsphp-processes/).

### Option B — restart the web server

Restarting the web server recycles its PHP workers in one step. This is the simplest fix when several workers or several sites are affected, and it is what resolved the real case this guide is based on — a single Apache restart (`mod_lsapi` on CloudLinux), after which every site reported the new version:

```
systemctl restart httpd        # Apache / mod_lsapi - recycles its lsphp workers
```

A web-server restart briefly disrupts every site on the server, so run it in a maintenance window. It is not always required — a targeted recycle (Option A) is often enough. One important caveat: **restarting the web server does not reliably recycle every kind of PHP process**, so match the method to the handler:

- **An independently managed PHP-FPM master** is restarted through its own service, not by the web server: `systemctl restart ea-php83-php-fpm`.

- **LiteSpeed running PHP in detached (external) mode** — the usual configuration on LiteSpeed Enterprise and OpenLiteSpeed — keeps its `lsphp` processes alive across a server restart by design, so a plain `systemctl restart lsws` can leave the stale workers running. Recycle PHP the way LiteSpeed documents instead: use **Restart PHP** in the LiteSpeed WebAdmin console, or drop LiteSpeed’s per-account restart marker (it is picked up on the next request) — or simply recycle the specific PID with Option A:

```
touch /home/ACCOUNT/.lsphp_restart.txt   # LiteSpeed recycles that account's detached lsphp
```

Do **not** reboot the whole server just to refresh PHP workers; that is never necessary for this problem.

## Verify the fix

Do not assume the recycle worked — confirm it. The version visitors see is the one that matters:

- Check the version the site actually serves — that is the number that matters. A full, public `phpinfo()` exposes a lot of server detail, so prefer a **minimal version check that you restrict and then remove**: a one-line file that prints only the PHP version, protected by an IP allow-rule or simply deleted the moment you have read it. A response header such as `X-Powered-By` is convenient but can be disabled, stripped by the server, or cached, so treat it only as a hint:

```
# minimal and temporary - delete it as soon as you have read the version:
echo '
