← Back to the counter

Update to PHP 8.5.12

PHP 8.5.12 is the newest release not affected by PHP2Shell (CVE-2026-109618, CVE-2026-109619). If you'd rather stay on your current branch, these releases are also fixed:

Ubuntu

Uses Ondřej Surý's PPA. Pick the block that matches how PHP is wired into your web server.

sudo add-apt-repository -y ppa:ondrej/php
sudo apt update
sudo apt install -y php8.5 php8.5-cli php8.5-common php8.5-curl php8.5-gd \
  php8.5-mbstring php8.5-mysql php8.5-xml php8.5-zip

Apache with mod_php (highest risk): switch the module over.

sudo apt install -y libapache2-mod-php8.5
sudo a2dismod php8.1 php8.2 php8.3 php8.4 2>/dev/null
sudo a2enmod php8.5
sudo systemctl restart apache2

PHP-FPM (nginx or Apache with proxy_fcgi): point your pool and web server at the 8.5 socket, /run/php/php8.5-fpm.sock.

sudo apt install -y php8.5-fpm
sudo systemctl enable --now php8.5-fpm
# then update fastcgi_pass / SetHandler to /run/php/php8.5-fpm.sock and reload the web server

Make the command line use 8.5 too:

sudo update-alternatives --set php /usr/bin/php8.5

Can't change branch today? Staying on 8.2, 8.3 or 8.4 from the same PPA also fixes it: sudo apt update && sudo apt install --only-upgrade 'php8.3*' libapache2-mod-php8.3 (swap in your version), then restart Apache or PHP-FPM.

Restart and reboot

The patch takes effect only once every running PHP process has been replaced. Restart whatever runs PHP for you:

# Apache with mod_php: a full restart is required to load the new module
sudo apachectl configtest && sudo systemctl restart apache2

# PHP-FPM: restart the new pool, stop and disable the old one, reload the web server
sudo systemctl restart php8.5-fpm
sudo systemctl disable --now php8.3-fpm     # swap in your old version
sudo nginx -t && sudo systemctl reload nginx   # or: sudo systemctl reload apache2

# List anything else still running old code (needrestart ships with Ubuntu)
sudo needrestart -r l

A full reboot is only needed if the system says so, usually because the same upgrade brought in a new kernel or libc:

[ -f /var/run/reboot-required ] && cat /var/run/reboot-required.pkgs || echo "No reboot needed"
sudo systemctl reboot

Warning: restart apache2 drops requests in flight, and every site on the server is unavailable for a few seconds. Always run apachectl configtest first, because a config error stops Apache from coming back. Don't use reload or graceful after a mod_php upgrade, because they can leave old workers serving requests.

Warning: before rebooting a remote server, make sure you have console access (your provider's web console) in case SSH doesn't come back. Take a snapshot first, and check that the database and other services are enabled to start at boot: systemctl is-enabled apache2 php8.5-fpm mariadb.

Debian

Uses packages.sury.org. After adding the repository, install the packages and switch the module exactly as in the Ubuntu section.

sudo apt update && sudo apt install -y lsb-release ca-certificates curl
curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb
sudo dpkg -i /tmp/debsuryorg-archive-keyring.deb
echo "deb [signed-by=/usr/share/keyrings/debsuryorg-archive-keyring.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" \
  | sudo tee /etc/apt/sources.list.d/php.list
sudo apt update
sudo apt install -y php8.5 php8.5-cli php8.5-fpm

Restart and reboot

# Apache with mod_php
sudo apachectl configtest && sudo systemctl restart apache2

# PHP-FPM
sudo systemctl restart php8.5-fpm
sudo systemctl disable --now php8.2-fpm     # swap in your old version
sudo nginx -t && sudo systemctl reload nginx

# Find services still running old code (install it with: sudo apt install needrestart)
sudo needrestart -r l

# Reboot only if a kernel or libc update asks for it
[ -f /var/run/reboot-required ] && cat /var/run/reboot-required.pkgs || echo "No reboot needed"
sudo systemctl reboot

Warning: if you use Debian's own packages (apt install --only-upgrade php8.2 on bookworm, or php8.4 on trixie) instead of sury.org, the fixed versions are 8.2.35-1~deb12u1 and 8.4.27-1~deb13u1. You still need to restart Apache or PHP-FPM afterwards. Installing the package doesn't replace running workers.

Warning: restarting Apache or PHP-FPM drops requests in flight. On a remote server, make sure you have console access before you reboot.

RHEL / AlmaLinux / Rocky Linux

Uses the Remi repository and needs EPEL. Swap 9 for 8 or 10 to match your release.

On 21 October, Remi did not yet have the fixed build. Run dnf info php after updating and confirm it shows 8.5.12 before you rely on it.

sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm
sudo dnf module reset -y php
sudo dnf module enable -y php:remi-8.5
sudo dnf update -y 'php*'

Restart and reboot

RHEL-family systems run PHP through PHP-FPM by default. Restart it, then the web server:

sudo systemctl restart php-fpm
sudo apachectl configtest && sudo systemctl restart httpd
# or, for nginx:
sudo nginx -t && sudo systemctl reload nginx

# Which services still run old code? (needs dnf-utils / yum-utils)
sudo needs-restarting -s

# Does the whole system need a reboot? Exit code 1 means yes
sudo needs-restarting -r; echo "exit=$?"
sudo systemctl reboot

Warning: Red Hat's own php packages keep their old version numbers when they backport fixes, so php -v can't tell you whether you're patched. Check the errata with dnf updateinfo list --cve CVE-2026-109618. As of 21 October, Red Hat had not published one.

Warning: dnf module reset php changes which PHP stream the server follows. Test your applications on 8.5 before doing this in production. Restarting httpd drops requests in flight, and on a remote server you need console access before rebooting.

SUSE Linux Enterprise / openSUSE

SLES 15 SP7 has a fixed php8 package (8.3.36). Install just the PHP2Shell patches, or everything pending:

sudo zypper refresh
sudo zypper list-patches --cve=CVE-2026-109618,CVE-2026-109619
sudo zypper patch --cve=CVE-2026-109618,CVE-2026-109619
# or update the PHP packages directly
sudo zypper update 'php8*' 'apache2-mod_php8'
php -v

Restart and reboot

# PHP-FPM and/or Apache with mod_php8
sudo systemctl restart php-fpm
sudo apachectl configtest && sudo systemctl restart apache2

# Processes still using deleted (old) files, grouped by service
sudo zypper ps -s

# Does the system need a reboot? Exit code 102 means yes
sudo zypper needs-rebooting; echo "exit=$?"
sudo systemctl reboot

Warning: as of 21 October, php7, the SLES 15 SP4–SP6 LTSS channels, openSUSE Leap and the BCI container images were still listed as affected. If zypper list-patches shows nothing for you, use the temporary mitigations below and a WAF until a patch lands.

Warning: restarting Apache drops requests in flight. On a remote server, make sure you have console access before rebooting.

Fedora

Fedora ships recent PHP branches in its own repositories. Pull the updates once they reach updates; updates-testing often has them earlier.

sudo dnf upgrade --refresh 'php*'
# not there yet? try the testing repo
sudo dnf upgrade --refresh --enablerepo=updates-testing 'php*'
php -v

Restart and reboot

sudo systemctl restart php-fpm
sudo apachectl configtest && sudo systemctl restart httpd
# or: sudo nginx -t && sudo systemctl reload nginx

# Reboot check (exit code 1 means a reboot is needed)
sudo dnf needs-restarting -r; echo "exit=$?"
sudo systemctl reboot

Warning: packages from updates-testing haven't finished Fedora's QA. Disable the repo again after the PHP update. Restarting httpd drops requests in flight.

Amazon Linux 2023

AL2023 pins packages to a repository release. Moving to the newest release picks up fixed PHP once Amazon publishes it:

sudo dnf check-release-update
sudo dnf upgrade --releasever=latest 'php*'
php -v

Restart and reboot

sudo systemctl restart php-fpm
sudo apachectl configtest && sudo systemctl restart httpd

sudo needs-restarting -r; echo "exit=$?"
sudo systemctl reboot

Warning: as of 21 October, Amazon had not published a fixed PHP package (check the ALAS bulletins). Until php -v shows a fixed version, apply the temporary mitigations below and put the instance behind AWS WAF or another WAF with PHP2Shell rules.

Warning: --releasever=latest upgrades every package you name to the newest release, not just PHP. On EC2, check that the instance is behind a load balancer or has a recent AMI snapshot before rebooting. Instances in an Auto Scaling group should be replaced by rolling the launch template, not patched in place.

Alpine Linux

Package names include the branch (php84, php83, …). Upgrade the branch you use and check the version it reports:

apk update
apk policy php84            # shows available versions; swap in your branch
apk upgrade 'php84*'
php84 -v

Restart and reboot (OpenRC)

rc-service php-fpm84 restart
rc-service nginx reload      # or: rc-service apache2 restart
reboot                       # only after a kernel (linux-lts / linux-virt) upgrade

Warning: Alpine hadn't published an advisory as of 21 October. If apk policy doesn't show a fixed version for your branch, use the temporary mitigations below. Most Alpine PHP runs in containers. There, rebuild the image and recreate the container (see Docker) instead of patching a running container.

Arch Linux

Arch tracks the latest PHP. Always upgrade the whole system:

sudo pacman -Syu
php -v

Restart and reboot

sudo systemctl restart php-fpm
sudo apachectl configtest && sudo systemctl restart httpd
# or: sudo nginx -t && sudo systemctl reload nginx

# Reboot if the kernel was upgraded (running kernel no longer installed)
pacman -Q linux; uname -r
sudo systemctl reboot

Warning: never run pacman -Sy php on its own. Partial upgrades are unsupported on Arch and can break the system. A full -Syu may also bring in a new kernel, which needs a reboot before kernel modules load correctly.

cPanel / WHM and CloudLinux

EasyApache 4 builds ea-php74 through ea-php85 carry the fix in the 21 October release. CloudLinux alt-php builds are rolling out.

# EasyApache 4 PHP versions
sudo yum update 'ea-php*'
# CloudLinux alt-php versions
sudo yum update 'alt-php*'

# Which PHP versions are installed and assigned
/usr/local/cpanel/bin/rebuild_phpconf --current

Restart and reboot

/scripts/restartsrv_apache_php_fpm
/scripts/restartsrv_httpd

# Reboot check (CloudLinux/AlmaLinux base)
sudo needs-restarting -r; echo "exit=$?"
sudo reboot

Warning: on shared servers, each account can be set to a different PHP version. Update every installed ea-php and alt-php version, not just the default. Restarting httpd briefly interrupts every hosted site. Do it outside peak hours, and announce it if you have SLAs.

Docker

Pin the patched tag in your Dockerfile, then rebuild with --pull so you don't reuse a cached vulnerable layer.

# Dockerfile: pick the variant you use
FROM php:8.5.12-apache
# FROM php:8.5.12-fpm
# FROM php:8.5.12-cli
docker build --pull --no-cache -t myapp .

Restart and redeploy

Containers have to be recreated from the new image. Restarting an existing container keeps the old, vulnerable PHP.

# Docker Compose
docker compose up -d --force-recreate

# Plain Docker
docker stop myapp && docker rm myapp
docker run -d --name myapp [your previous flags] myapp

# Kubernetes: roll out the new image and wait for it to finish
kubectl set image deployment/myapp app=registry.example.com/myapp:php-8.5.12
kubectl rollout status deployment/myapp

# Confirm what is actually running
docker exec myapp php -v

Warning: docker restart and docker compose restart do NOT pick up a new image. Recreating a container drops anything written inside it that isn't on a volume, so check your volumes first. Rebuild every image that uses FROM php:…, including base images other teams maintain.

Warning: the host only needs a reboot if its own kernel was updated. The PHP fix lives entirely inside the image.

macOS (Homebrew)

brew update
brew upgrade php
php -v

Restart

# PHP-FPM managed by Homebrew
brew services restart php

# Homebrew Apache or nginx, if they serve PHP
brew services restart httpd
brew services restart nginx

# Local dev servers (php -S, Laravel Valet/Herd, etc.): stop and start them again
valet restart

Warning: a versioned formula such as php@8.3 doesn't move to 8.5 on its own. Run brew upgrade php@8.3 to get 8.3.36, or switch with brew unlink php@8.3 && brew link php. No reboot is needed.

Windows

Download the 8.5.12 zip from windows.php.net/download. Choose Thread Safe for Apache's mod_php and Non Thread Safe for IIS or FastCGI. Stop the web server before extracting, otherwise running php-cgi.exe processes keep the old DLLs locked. Then extract over your existing PHP folder, keeping your php.ini.

Stop, replace, restart (PowerShell as Administrator)

# IIS
iisreset /stop
# ...extract PHP 8.5.12 over C:\PHP, keep php.ini...
iisreset /start

# Or recycle a single site's app pool instead of all of IIS
Import-Module WebAdministration
Restart-WebAppPool -Name "MySitePool"

# Apache service
Stop-Service Apache2.4
# ...extract PHP 8.5.12...
Start-Service Apache2.4

# Make sure no old PHP is still running
Get-Process php-cgi, php -ErrorAction SilentlyContinue

# Reboot only if Windows itself asks for it
Restart-Computer -Confirm

Warning: iisreset takes every site on the server offline until IIS starts again. Recycling a single app pool doesn't. If files are still locked after IIS stops, check for leftover php-cgi.exe processes before replacing anything.

Warning: before restarting a remote Windows server, make sure you can reach it over RDP or an out-of-band console, and that IIS and Apache are set to start automatically.

From source

Rebuild with the same ./configure flags you used before. php -i | grep "Configure Command" prints them.

curl -LO https://www.php.net/distributions/php-8.5.12.tar.xz
tar xf php-8.5.12.tar.xz && cd php-8.5.12
./configure --prefix=/usr/local/php85 [your previous flags]
make -j"$(nproc)" && sudo make install

Restart

# Restart the services that load your custom build (names vary by setup)
sudo systemctl restart php-fpm
sudo apachectl configtest && sudo systemctl restart apache2

# Confirm the running FPM master was started from the new binary
ps -o pid,lstart,cmd -C php-fpm | head -3
/usr/local/php85/sbin/php-fpm -v

Warning: make install overwrites the binary, but processes that were already running keep the old code in memory until they're restarted. If you installed under a new prefix, update your service unit, Apache LoadModule line or PATH to point at it. Otherwise you'll restart into the old version.

Verify

Check the version the web server runs, not just the command line. They are often different binaries.

php -v
php-fpm8.5 -v 2>/dev/null
# what the web server actually runs (delete the file afterwards):
echo '<?php echo PHP_VERSION;' | sudo tee /var/www/html/v.php
curl -s http://localhost/v.php; echo; sudo rm /var/www/html/v.php

Then stop advertising your version. Set this in the php.ini your web server loads, and restart:

expose_php = Off

PHP 8.1 and older

These branches are end-of-life, and there are no official fixed binaries for them. Upgrade to 8.5.12 using the steps above. If you can't upgrade right away, PHP published source-only backports for 7.4, 8.0 and 8.1, and your distro or LTS vendor may ship them. Until then, take the site offline or put it behind a WAF that has PHP2Shell rules.

php.net's temporary mitigations reduce exposure but are not a fix. Put them in php.ini or your FPM pool, not in .user.ini or ini_set(), then restart every worker:

memory_limit = -1                    ; enforce with php_admin_value so apps can't override it
post_max_size = 8M                   ; 32M if you need uploads; keep upload_max_filesize below it
session.upload_progress.enabled = 0
fatal_error_backtraces = 0           ; PHP 8.5 only

Remove memory_limit = -1 once you're patched. It lets a single request use all of the server's memory.

↑