PHP packages with security fixes for Debian releases that no longer receive them.
The real fix is to upgrade to a supported Debian or Ubuntu release. These packages are a last resort: they keep PHP safer until you can upgrade. An archived release also misses security fixes in many other packages, not only in PHP.
Only for archived Debian releases: squeeze, wheezy, jessie, stretch and buster. On Debian 11 bullseye and newer, use deb.sury.org instead — it builds every PHP version for supported releases and gets fixes first. Nothing here is built for, or should be installed on, a supported release.
The Ubuntu targets, Ubuntu 14.04 trusty (PHP 5.6, 7.4), Ubuntu 16.04 xenial (PHP 5.6, 7.3, 7.4), Ubuntu 18.04 bionic (PHP 5.6, 7.2, 7.4) and Ubuntu 20.04 focal (PHP 5.6, 7.2, 7.4), exist for the same reason: the ondrej/php PPA dropped these releases when their standard support ended. On trusty that was in 2019, before PHP 7.4 existed, so there was no 7.4 build for it before this one. Trusty's ten years of support also ended in April 2024: nothing on it, its own PHP 5.5 included, gets fixes from Canonical any more. Xenial's own PHP 7.0, bionic's 7.2.24 and focal's 7.4.3 are still patched through Ubuntu Pro (ESM).
Debian 9 stretch and Debian 10 buster with their own PHP (7.0 and 7.3): use Freexian ELTS first. Its public repository already has the fix for amd64 and more (buster also arm64), with more security fixes than a rebuild here would have. The link explains how to add it to apt. ELTS is a service paid for by Freexian's customers; the public repository comes with no guarantee. This repository does not build these two.
deb.sury.org builds PHP — including old PHP versions — but only for Debian and Ubuntu releases that are still supported. Servers running an archived release get nothing from him. This repository covers what nobody else fixes: it rebuilds PHP for archived releases with the fixes applied, from sources you can check.
Current fix: CVE-2026-109618 and CVE-2026-109619 ("PHP2Shell", remote code execution; advisories GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp).
All targets carry the PHP group's mitigation for end-of-life PHP versions, the same patch Ondřej ships ("Avoid state corruption via bailout"). It is not the full fix from PHP 8.2+, but it closes both problems in a simpler way:
- On out of memory, the PHP process exits instead of continuing with broken state.
- Error and request state is reset at the end of each request.
- Session upload progress is turned off (
session.upload_progresshas no effect). This removes one of the confirmed attack paths. If your application shows upload progress bars, they stop working.
Each row is a target, named <release>/<php>-<component>, for example
debian7-wheezy/php5.4-debian. That is its directory under targets/, the name of its
tarball (debian7-wheezy_php5.4-debian.tar.gz) and the directory the tarball unpacks to.
A release is always a Debian or Ubuntu release in this README. The Release column shows
it the way target names spell it, with its apt codename after the dash.
The commands under "Installing" take four values from this table:
RELEASE is the Release column (debian8-jessie), CODENAME is its part after the dash
(jessie), COMPONENT is the Component column (sury) and TARGET is
php<PHP>-<COMPONENT> (php5.6-sury).
| Release | PHP | Component | Source package |
|---|---|---|---|
debian6-squeeze |
5.4 | debian7 |
php5 |
debian7-wheezy |
5.4 | debian |
php5 |
debian8-jessie |
5.6 | debian |
php5 |
debian8-jessie |
5.6 | sury |
php5.6 |
debian8-jessie |
7.1 | sury |
php7.1 |
debian9-stretch |
5.6 | sury |
php5.6 |
debian9-stretch |
7.0 | — not built here, use Freexian ELTS | php7.0 |
debian9-stretch |
7.3 | sury |
php7.3 |
debian9-stretch |
7.4 | sury |
php7.4 |
debian10-buster |
7.3 | — not built here, use Freexian ELTS | php7.3 |
debian10-buster |
5.6 | sury |
php5.6 |
debian10-buster |
7.4 | sury |
php7.4 |
ubuntu14.04-trusty |
5.6 | sury |
php5.6 |
ubuntu14.04-trusty |
7.4 | sury |
php7.4 |
ubuntu16.04-xenial |
5.6 | sury |
php5.6 |
ubuntu16.04-xenial |
7.3 | sury |
php7.3 |
ubuntu16.04-xenial |
7.4 | sury |
php7.4 |
ubuntu18.04-bionic |
5.6 | sury |
php5.6 |
ubuntu18.04-bionic |
7.2 | sury |
php7.2 |
ubuntu18.04-bionic |
7.4 | sury |
php7.4 |
ubuntu20.04-focal |
5.6 | sury |
php5.6 |
ubuntu20.04-focal |
7.2 | sury |
php7.2 |
ubuntu20.04-focal |
7.4 | sury |
php7.4 |
The exact package version of every target is in the notes of each
tag (GitHub
releases, tagged vYYYY.MM.DD.N, are called tags here) and in the BUILD-INFO.txt of its
tarball. Every version is the version it was built from (Debian's or deb.sury.org's) plus
+animaaN, except on squeeze: 5.4.45-1+animaaN~squeeze, built from wheezy's
5.4.45-0+deb7u14.
All targets are built for amd64. Five are also built for arm64: Debian 10 buster
PHP 5.6 and 7.4, and Ubuntu 20.04 focal PHP 5.6, 7.2 and 7.4. These are the first releases that
deb.sury.org no longer builds for. Older releases were rarely run on arm64, and buster's
own PHP 7.3 already has an arm64 fix from Freexian ELTS.
The apt component says where the source comes from, and therefore what adding it does:
-
debian— the PHP version the release itself shipped, rebuilt from the last official Debian source with the fix added. A drop-in replacement:apt-get upgradereplaces only packages you already have, with the same version plus the fix. This is what most servers want. It adds this fix and the CVE-2023-0662 fix (max_multipart_body_parts) to the last official source, but not other fixes released after it. On jessie, where the last Debian source is old, thesurycomponent has more fixes. Stretch and buster have nodebiancomponent: their own PHP is fixed in Freexian ELTS. -
sury— Ondřej Surý's PHP packages (which already contain the fix), rebuilt for a release he no longer builds for. Use it if you already run his PHP version on that release. A few dependencies newer than the release has ship in the same component: on stretchlibpcre2andlibargon2-1from buster, on xeniallibpcre2,libargon2-0andlibtidy5from bionic, on trusty those three pluslibzip4andlibsystemd0from xenial (and trusty's ownlibgcrypt20from universe; PHP 5.6 needs onlylibtidy5andlibzip4), and everywhere but buster aphp-commonrebuilt from Sury'sphp-defaultssource (next paragraph). The shippedlibpcre2replaces the older one of the release.libpcre2-devships too, becausephpX.Y-devneeds it; the other-devpackages are only used for the build. The rebuild keeps the same PHP extension API (phpapi-…), so extension packages you installed earlier (imagick, redis, apcu, …) keep working. Where his repository dropped the release, those can no longer be installed new.About
php-common: Sury's PHP packages need aphp-commonnewer than jessie, stretch, trusty, xenial and bionic have. His binaryphp-commonis made for PHP 8 and declaresBreakson older versions of 42 PECL extension packages (php-apcubelow 5.1.24,php-imagickbelow 3.7.0-12,php-redis,php-memcached,php-xdebug, …). Installed on an older release, it makesapt-getremove those extensions while upgrading PHP, even the release's own versions. Tags up tov2026.10.23.1shipped that binary (on focal too, although focal's own 2:75 would do). Now thesurycomponent shipsphp-commonrebuilt from the samephp-defaultssource with that field removed, as2:101+0~20261021.78+debian11~1.gbp53f25f+animaa1on every release: Sury's version plus the suffix, so it sorts above his binary and replaces it with a normalapt-get upgrade. Extensions that an earlier upgrade removed can then be installed again (from your own copy; the old repositories are gone).On trusty:
php7.4-fpmandphp5.6-fpmpull in trusty'ssystemd204 package. Their init scripts needsystemd-tmpfiles; trusty still boots with upstart. Installingphp7.4-devremoves trusty'slibpcre3-dev, because that package conflicts withlibpcre2-dev. This matters only if you build PECL extensions or other software against PCRE. PHP 5.6 uses trusty's own PCRE 8.31, sophp5.6-devneeds thatlibpcre3-devand cannot be installed together withphp7.4-dev.libapache2-mod-php5.6andlibapache2-mod-php7.4work with trusty's Apache 2.4.7. -
debian<N>— the PHP of a newer Debian release built for an older one (squeeze gets wheezy's PHP 5.4; squeeze itself shipped 5.3). Adding it upgrades PHP to that version. On squeeze, PECL extension packages built for PHP 5.3 (phpapi-20090626) are removed by the upgrade, andphp5-suhosinmust be purged first. Useapt-get dist-upgrade.
- CVE-2023-0662 (
max_multipart_body_partsDoS limit): Check that the INI directive is recognized by PHP:php -i | grep -i max_multipart_body_parts # Expected: max_multipart_body_parts => -1 => -1
- CVE-2026-109618 & CVE-2026-109619 ("PHP2Shell" bailout / memory corruption mitigation):
The fix adds
zend_mm_safe_error_ex()and routes allocation-overflow errors through it instead of a bailout. Check that the symbol is exported by the binary:nm -D $(which php) | grep zend_mm_safe_error_ex # Expected: ... T zend_mm_safe_error_ex
Jessie and newer: use the apt repository. Squeeze, wheezy and trusty: use a tarball. They cannot reach the repository, which is served over HTTPS only: squeeze's OpenSSL 0.9.8 does not speak TLS 1.2, and the GnuTLS 2.12 of wheezy and trusty fails the TLS handshake with GitHub's servers. A tarball also works on machines without internet access.
The repository is signed with a dedicated RSA 4096 key (repo/debian-php-backports.asc),
fingerprint 32E9 9830 8C44 8BF6 CC57 BD7B FEE3 4861 1F66 C3D6. RSA, because the gpgv of squeeze, wheezy and jessie cannot
verify Ed25519 signatures.
First set two values from the table under "Packages": the codename of your
release and the component (see also "Components"). Not sure which release
you run? lsb_release -sc prints the codename; cat /etc/os-release works too.
CODENAME=jessie # Release column after the dash: jessie stretch buster xenial bionic focal
COMPONENT=sury # Component column: sury or debianThen import the key and add the repository. On stretch, buster, xenial, bionic and focal:
curl -fsSL https://animaartificialis.github.io/debian-php-backports/debian-php-backports.asc \
| gpg --dearmor -o /usr/share/keyrings/debian-php-backports.gpg
echo "deb [signed-by=/usr/share/keyrings/debian-php-backports.gpg] https://animaartificialis.github.io/debian-php-backports $CODENAME $COMPONENT" \
> /etc/apt/sources.list.d/debian-php-backports.list
apt-get update && apt-get upgradeJessie has no signed-by (its apt predates it); use apt-key and a plain line:
wget -qO- https://animaartificialis.github.io/debian-php-backports/debian-php-backports.asc | apt-key add -
echo "deb https://animaartificialis.github.io/debian-php-backports $CODENAME $COMPONENT" \
> /etc/apt/sources.list.d/debian-php-backports.list
apt-get update && apt-get upgradeIf apt-get upgrade says the PHP packages "have been kept back", the new packages need
a package you do not have yet (for example, Sury's php-common needs psmisc and
recommends cron). apt-get upgrade never installs new packages. Upgrade PHP like this
instead:
apt-get install --only-upgrade 'php*'Jessie needs apt-transport-https and reasonably current CA certificates.
Every tag has one
tarball per target with its .deb files, a Packages index and BUILD-INFO.txt.
The arm64 tarballs end in _arm64.tar.gz; all others are amd64.
The tarball is ${RELEASE}_${TARGET}.tar.gz and unpacks to $RELEASE/$TARGET/, with
RELEASE and TARGET taken from the table under "Packages". For the
releases that cannot use the apt repository the values are:
RELEASE |
TARGET |
|---|---|
debian6-squeeze |
php5.4-debian7 |
debian7-wheezy |
php5.4-debian |
ubuntu14.04-trusty |
php5.6-sury |
ubuntu14.04-trusty |
php7.4-sury |
On wheezy and trusty, curl (built with OpenSSL) can download it, wget cannot:
RELEASE=debian7-wheezy # from the table above
TARGET=php5.4-debian # from the table above
URL=https://github.com/animaartificialis/debian-php-backports/releases/latest/download
mkdir -p /srv/debian-php-backports && cd /srv/debian-php-backports
curl -fsSLO "$URL/${RELEASE}_${TARGET}.tar.gz" -O "$URL/SHA256SUMS"
grep -F "${RELEASE}_${TARGET}.tar.gz" SHA256SUMS | sha256sum -c -
tar xzf "${RELEASE}_${TARGET}.tar.gz"
echo "deb [trusted=yes] file:/srv/debian-php-backports/$RELEASE/$TARGET ./" \
> "/etc/apt/sources.list.d/debian-php-backports-$TARGET.list"
apt-get update && apt-get upgradeFor a specific tag instead of the latest, replace latest/download with
download/<tag>, for example download/v2026.10.26.1.
Squeeze cannot download from GitHub at all: run the curl lines on another machine (or
download from the tag's page), check the sum there and copy the tarball over. Its apt has no
[trusted=yes], so tell it to accept the unsigned local repository. This upgrades PHP 5.3
to 5.4; purge php5-suhosin first (see "Components"):
mkdir -p /srv/debian-php-backports
tar -C /srv/debian-php-backports -xzf debian6-squeeze_php5.4-debian7.tar.gz
echo "deb file:/srv/debian-php-backports/debian6-squeeze/php5.4-debian7 ./" \
> /etc/apt/sources.list.d/debian-php-backports.list
apt-get update && apt-get --allow-unauthenticated dist-upgradeEach target builds in a podman or docker container of its own Debian or Ubuntu release, from archived Debian or deb.sury.org sources pinned by SHA256:
- A builder image is created from the release's own base image (
debian:<codename>-slim,debian/eol:squeeze, orubuntu:<version>), pointed at the release's archive (archive.debian.org for Debian, archive.ubuntu.com for Ubuntu), with the source package's build dependencies installed. For thesurytargets on every release but buster, the image also rebuildsphp-commonfrom Sury's pinnedphp-defaultssource without itsBreaksfield (build/container/build-php-common.sh, pins inbuild/build.conf), and the package ships with the target. - The source package is downloaded and its
.dscchecksum verified. - For
debiantargets, Sury's fixes are added todebian/patches/series(the patch files are committed undertargets/*/patches/, with their origin inSOURCE.md).surytargets use Sury's source package as it is. - The version gets an
+animaaNsuffix (+animaa1, raised on each rebuild of the same source) so it sorts above the version it was built from, and the packages are built withdpkg-buildpackage. - An install test, in a clean container of the same release, installs the built packages — over the stock ones where the release has them — and runs PHP.
The upstream test suite is skipped (nocheck); it takes hours on these releases.
Two kinds of changes go beyond adding the fix, each recorded in the target's SOURCE.md:
- Sury's packages carry a "secure DNS telemetry" patch. It is dropped from every
surytarget. - PHP 5.4 was never shipped by Sury, so there is no upstream patch for it. The 5.4
targets use a port of Sury's PHP 5.6 patch; the differences are listed in its
SOURCE.mdand the patch header.
Requires podman or docker.
make list # all targets
make build-debian8-jessie/php5.6-debian # packages land in dist/debian8-jessie/php5.6-debian/
make test # install test of everything builtLayout: targets/<release>/<php>-<component>/target.conf describes a target,
build/ holds the build scripts and the builder Containerfile, repo/ builds the apt
repository, tests/ holds the install test.
The sury targets pin deb.sury.org source packages (PHP, and php-defaults where
php-common is rebuilt) by exact version, and Sury removes old revisions from his pool.
A scheduled workflow (.github/workflows/sury-watch.yml, every eight hours) runs
repo/check-sury-updates.py, which compares the pinned revisions with Sury's index and
opens an issue labelled sury-update when a newer revision exists. Updates that an open
or closed issue already mentions are not reported again; a newer revision found while an
issue is open is added to it as a comment. A second kind of issue, labelled
sury-dropped, appears when a release this README sends to Sury is gone from his archive,
or its suite falls more than two weeks behind bookworm's (Sury refreshes every suite
daily) — bullseye is the last such release here. Those issues never receive update
comments. The script also runs locally: repo/check-sury-updates.py --dry-run.
The build scripts are MIT licensed (see LICENSE). PHP and the patches keep their own
licenses: the fixes come from the PHP project via deb.sury.org, packaging from Debian
and deb.sury.org.