Current situation: PHP2Shell. Two critical PHP vulnerabilities (CVSS 9.2) allow malicious code without login. Patches since 21 October 2026.What to do now →

Securing FrankenPHP

FrankenPHP ships PHP inside its own program. A distribution update is therefore not enough: the binary or image must be rebuilt and all workers restarted.

Last updated 5 min readServer & Hosting

In short: FrankenPHP is an application server based on Caddy that embeds PHP in its own program. For FrankenPHP security after PHP2Shell, that means: the distribution's PHP packages do not count, but the PHP version in the FrankenPHP binary or container image does. Build or pull a version with fixed PHP and restart all workers.

Why FrankenPHP is patched differently

In classic setups, the distribution supplies PHP as a package, and an update swaps the files. FrankenPHP, by contrast, contains PHP as an embedded library. Anyone who uses the official binary or a container image built on it gets PHP shipped along. An apt upgrade on the host changes nothing about that.

PHP2Shell covers CVE-2026-109618 and CVE-2026-109619, each with CVSS v4.0 9.2. The PHP project explicitly lists FrankenPHP among the affected operating modes. The fixed versions are PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. What matters, then, is which PHP version your FrankenPHP was built against.

Worker mode and PHP2Shell

FrankenPHP can run applications in worker mode. The application is loaded once and then handles many requests in a row in the same process. This brings speed, but it extends the lifetime of every process.

CVE-2026-109619 describes internal state of a request that can persist after memory has been freed; a later request in the same worker can use it before it is reset. In worker mode, many requests interacting within one process is the normal case. That is no reason to give up worker mode, but it is a clear reason to carry out updates and restarts particularly carefully. Technical details are under CVE-2026-109619.

FrankenPHP security: checking the version

Check the embedded PHP version directly in the running system. The following commands only read; names of services and containers are examples.

# Installed binary
frankenphp version

# In the running container (container name as an example)
docker exec app frankenphp version
docker exec app php -v

# Which image is actually running? (the digest shows the exact state)
docker inspect --format '{{.Image}} {{.Config.Image}}' app

Note the PHP version from the output. If it is older than the fixed releases, the system is vulnerable. Always check the running container, not just the most recently built image, because a new image only takes effect once the containers have been replaced.

Apply the update and restart all workers

What the update looks like depends on your installation:

  • Binary on the host: install a new FrankenPHP release with the fixed PHP version, restart the service.
  • Container: pull a base image with the fixed PHP version, rebuild your own image without cache for the base layer, replace the container.
  • Self-compiled: rebuild against the fixed PHP version, regenerate all extensions with the same version.
# Example host installation as a systemd service
systemctl restart frankenphp

# Example Docker Compose: rebuild without cache and replace containers
docker compose build --pull --no-cache
docker compose up -d --force-recreate

Afterwards, check the PHP version in the running container again, along with the start time of the processes. In worker mode, every worker must have been restarted. For Kubernetes, additional steps apply, described under Patching PHP in Kubernetes, and for containers in general under Patching PHP in Docker containers.

Important

A tag such as latest or a major version in the image name does not guarantee that you have the latest state. Actively pull the base image again and then check the PHP version in the running container.

Interim measures and hardening

If the update cannot be done immediately, php.net recommends interim measures that do not offer complete protection. With FrankenPHP, you set the values in the php.ini that FrankenPHP loads, or through the PHP settings in the Caddy configuration, not via ini_set() in the application.

; php.ini in the container or on the host
session.upload_progress.enabled = 0
post_max_size = 8M
upload_max_filesize = 6M

With an upload function, php.net gives post_max_size = 32M. On PHP 8.5, fatal_error_backtraces=0 is added. The memory limit belongs at container level, for example via the container's memory limit, not through RLIMIT_AS or RLIMIT_DATA. Restart all workers after every change.

For continuous operation, it is worthwhile to reference images with a fixed digest, rebuild them regularly and automatically, and add an image scanner to the build chain. A patch also does not remove malware that was planted earlier. Keep logs from 20 October 2026 onward; with containers, that means saving them before replacing the container.

If you use FrankenPHP as a statically built binary, you should know that all extensions are built in firmly alongside PHP. An update therefore always means replacing the entire binary. After the swap, check the version and the list of loaded extensions to confirm that everything is available as before, and test the application before you discard the old state. In container environments, it is advisable to label every image with the date of the build. That way you can trace at any time which state ran when and, if in doubt, go back in a targeted way without falling back onto a vulnerable state again. Record the mapping of image, PHP version and point in time in your documentation.

How does PHP SOS help?

  • We check which PHP version is actually inside your FrankenPHP binaries and running containers.
  • Through the PHP patch service, we rebuild images, replace containers and restart all workers, with evidence.
  • We preserve logs before the swap and check the application for traces.
  • For continuous operation, we set up automated rebuilds and image checks as part of server hardening.

Frequently asked questions

Does an apt or dnf update help with FrankenPHP?

Only if FrankenPHP itself comes from those package sources and is built against the patched PHP. Often PHP is built firmly into the FrankenPHP binary or the container image. In that case you have to update the binary or image.

Why is worker mode particularly important with PHP2Shell?

In worker mode, the application stays in memory and handles many requests one after another in the same process. CVE-2026-109619 concerns exactly the internal state that can persist between requests in the same worker. Restarting all workers after the update is therefore mandatory.

How do I check which PHP version is inside FrankenPHP?

The FrankenPHP binary prints the embedded PHP version when you call it with the version command. In a container, you can run the command via docker exec. Make sure you check the version in the container that is actually running, not in the newly built image.

Are there interim measures specific to FrankenPHP?

The general notes from php.net apply. You set the values in the php.ini that FrankenPHP loads, or in the PHP configuration of the Caddy file, not via ini_set() in the application. Then restart all workers.

Sources

Immediate contact

Every hour counts. Write to us now.

Briefly describe your system, PHP version and observation. We will get back to you, clarify the situation and work remotely until the incident is closed and documented.

Immediate help