In short: The official PHP image on Docker Hub comes in many tags that combine version, variant (such as cli, fpm, apache) and base system. A tag is a pointer that moves to new states after rebuilds; only the digest identifies exactly one state. Against PHP2Shell you need an image with PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35, verified with php -v, and a rebuild of your own images on top of it.
Why the tag alone reveals little
Many teams write a line like FROM php:8.3-fpm in their Dockerfile and then forget it. That works as long as nobody asks which PHP version is actually running. Since PHP2Shell, everybody asks.
The answer is not in the Dockerfile. A tag such as 8.3-fpm points, at the time of pulling, to the latest state of that branch. If the image was pulled before 21 October and not renewed since, it contains a vulnerable version, even though the tag now points to a fixed state. According to the guidelines of the Docker Official Images, the images are actively rebuilt, including for security fixes. Your local copy is not affected by that.
The complete procedure from rebuild to newly created container is described in Patching PHP in Docker containers. This article is about the prior question: which image is the right one, and how do you prove its status?
PHP Docker image tags: how they are built
Tags consist of up to three parts. Docker Hub shows examples such as php:8.2-cli, php:8.2-fpm-alpine or php:8.4.27-zts-alpine3.24.
| Component | Example | Meaning |
|---|---|---|
| Version | 8.4.27 or 8.4 |
the full version stays at this state, the branch specification moves with new patches |
| Variant | cli, fpm, apache, zts |
operating mode of PHP in the container |
| Base system | alpine3.24, bookworm, trixie |
underlying Alpine or Debian version |
The variants are not equivalent for PHP2Shell. fpm runs PHP-FPM, apache runs PHP as an Apache module, that is, mod_php. The PHP team rates this operating mode highest because an exploit was demonstrated there before any application code even runs. More at Why mod_php is especially at risk. The cli variant is intended for command-line tasks.
The base system also has consequences. Alpine images are small but use a different C library than Debian images; some extensions or precompiled libraries behave differently there. So do not change the base system on the side with the security update, but stay with what your application knows for now.
Important for understanding: in the official image, PHP does not come from the packages of the base distribution. Docker Hub describes that the PHP sources are in the image and extensions are built in via helper scripts such as docker-php-ext-install. A package update of the base system in your own Dockerfile therefore changes nothing about the PHP version. All that matters is which state of the official image you build on.
Tag or digest: what changes when
A digest is a checksum over the contents of an image, written as sha256: followed by a long string. It changes with every rebuild. That makes it suitable as proof: whoever knows the digest knows exactly which state is running.
A division of labor has proven itself in practice:
- In the Dockerfile, use the branch tag, such as
php:8.4-fpm, so that a rebuild automatically gets the latest patch of that branch. - In the rollout, use the digest of your finished, built image so that it is unambiguous what is delivered.
- In the documentation, record both: tag, digest and the version verified with
php -v.
Whoever pins a full version tag such as 8.3.35-fpm instead never automatically gets a patch. That may be intended, but it requires a process that raises the tag with every security announcement.
PHP2Shell: checking an image before use
The following commands pull an image and read information. They do not change running containers.
# Pull the current state of the branch tag
docker pull php:8.4-fpm
# Which PHP version is in the image?
docker run --rm php:8.4-fpm php -v
# Show the digest of the pulled image
docker image inspect php:8.4-fpm --format '{{index .RepoDigests 0}}'
# Query digest and platforms directly in the registry
docker buildx imagetools inspect php:8.4-fpm
For multi-platform images, the imagetools query shows a top-level digest and below it one digest per platform, such as linux/amd64 or linux/arm64. Document the digest that your servers actually pull, and note that developer machines with a different architecture get a different platform digest.
If php -v does not show at least the fixed version of the branch, the image is outdated or comes from a cache. Then check whether your environment uses a registry mirror that serves old states.
For PHP 8.1 and older, the PHP project provides no official releases. An official image of these branches can therefore not contain a regular fixed version. According to the Docker guidelines, tags that are no longer maintained remain retrievable but are no longer updated.
According to Docker Hub, the image ships with php.ini-development and php.ini-production and strongly recommends the production version for production operation. Put your own settings, including the interim values from php.net, as a file in $PHP_INI_DIR/conf.d/.
Typical mistakes when choosing an image
- Variant out of habit: The apache variant is chosen because it starts easily, although fpm with a separate web server would be possible.
- No rebuild of your own images: The base image is current, the application image built from it is not.
- Multi-stage builds overlooked: In a Dockerfile with several
FROMlines, only one is adjusted. - Side images forgotten: Workers, cron containers and admin tools use their own tags.
- Relying only on
latest: The tag says nothing about branch and variant and can jump unexpectedly with a new main branch.
How to automate rebuilds so that an outdated image does not run for long in the first place is described in CI/CD: automatically rebuilding PHP images. Basics on PHP in containers are explained in PHP in containers.
How does PHP SOS help?
- Check the inventory: We read out which tags, digests and PHP versions are in your images and running containers.
- Assess the variant: We check whether mod_php is necessary or whether fpm with a separate web server reduces the risk.
- Rebuild and prove: With the PHP patch service, we rebuild images on a fixed base and document tag, digest and version.
- Anchor the process: We set up Dockerfiles and rollouts so that the next patch arrives in a plannable way.
Frequently asked questions
What is the difference between a tag and a digest?
A tag such as php:8.4-fpm is a name that can point to a new state after every rebuild. A digest is a checksum that identifies exactly one immutable state. For proof and reproducible rollouts, the digest is the more reliable specification.
Which tag contains the PHP2Shell fix?
The fixed versions are PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. A tag with the full version number shows the state directly. With shorter tags such as 8.4-fpm, check with php -v after pulling which version is actually contained.
Is there a patched official image for PHP 8.1?
The PHP project no longer provides official releases for 8.1 and older. An official image of these branches can therefore not contain a regular fixed version. Plan the move to a supported branch.
Is the apache variant riskier than fpm?
The apache variant runs PHP as an Apache module, that is, mod_php. The PHP team rates this operating mode highest for PHP2Shell. Both variants need the update; with mod_php, speed is especially important.