Security guidance checked on 25 October 2026. The version numbers below are the first releases fixing the two October 21 vulnerabilities, not a promise that those releases remain the latest secure choice. Recheck the official advisories when applying this guide.
An up-to-date WordPress installation can still run on a vulnerable PHP runtime. In a Docker deployment, the PHP binary usually belongs to the application image, while WordPress files and database data may live in persistent volumes. Those layers have separate update paths.
This guide shows how to patch the runtime with a reviewed image, apply the remaining session upload-progress mitigation, and verify the result using a generic Docker Compose configuration. It assumes an existing installation whose application service is named wordpress.
What the PHP vulnerabilities affect
| CVE | Failure mode | Potential impact |
|---|---|---|
| CVE-2026-109618 | Invalid request state after an out-of-memory interruption | Unauthenticated remote code execution |
| CVE-2026-109619 | Request-owned memory referenced by stale worker state | Unauthenticated remote code execution |
The first PHP advisory describes memory corruption during request cleanup after an allocation failure. The second advisory covers state that can outlive the request memory it references. Apache with mod_php is particularly exposed, including a demonstrated path before application code runs. Changing to PHP-FPM does not replace the need to patch PHP.
This is why a WordPress core update, a plugin update or HTTPS alone cannot fix these runtime defects. The PHP executable serving requests must be replaced with a patched build. Neither setting a different WordPress version string nor restarting an unchanged vulnerable image achieves that.
Choose a patched PHP release, then check compatibility
| PHP branch | First release fixing both CVEs |
|---|---|
| 8.2 | 8.2.35 |
| 8.3 | 8.3.36 |
| 8.4 | 8.4.27 |
| 8.5 | 8.5.12 |
These thresholds are listed in the two PHP advisories. Select the latest available security release in a supported branch compatible with your installation. A patch update within the existing branch can reduce compatibility risk; moving to another PHP branch deserves separate theme and plugin testing.
The examples use wordpress:7.1.3-php8.5-apache. WordPress 7.1 supports PHP 8.5 according to the official compatibility matrix, but that does not certify every extension or custom theme. Test a restored copy of the site before changing production.
Resolve and verify the image before deployment
A tag containing php8.5 identifies a branch, not its embedded patch release. Tags can also be rebuilt. Resolve the candidate in the official registry, inspect its platforms, and pin the reviewed artifact using both its readable tag and its immutable digest:
image_tag=wordpress:7.1.3-php8.5-apache
docker buildx imagetools inspect "$image_tag"
image_digest="$(docker buildx imagetools inspect "$image_tag" --format '{{.Manifest.Digest}}')"
test -n "$image_digest"
export WORDPRESS_IMAGE="${image_tag}@${image_digest}"
docker pull "$WORDPRESS_IMAGE"
docker run --rm --network none --entrypoint php "$WORDPRESS_IMAGE" -v
The isolated command above runs the candidate PHP binary without mounting site data. Check the patch release on every architecture you deploy. For a mixed-architecture cluster, retain a suitable multi-platform index digest and verify its platform manifests. Docker documents the registry inspection command and digest output.
Record the verified WORDPRESS_IMAGE reference in the deployment configuration or version catalog. A pinned digest makes the reviewed artifact reproducible; future security updates require an intentional digest refresh. Resolving a digest establishes identity, while checking the embedded PHP version establishes the relevant patch baseline.
Apply the session upload-progress mitigation at startup
On October 23, PHP published an additional notice concerning CVE-2026-115560: an uncommon combination of session serializer changes and upload-progress handling can leave a residual RCE path. PHP recommends disabling session upload progress until a fixed release is available. The recommendation also applies after installing the October 21 fixes. See the official PHP notices.
Create config/php/security.ini:
; Reassess against the official PHP advisory before removing this mitigation.
session.upload_progress.enabled = Off
This disables PHP session upload-progress bookkeeping, not file uploads themselves. Features that explicitly depend on that bookkeeping need testing. Load the setting before request parsing, through startup INI configuration. A late ini_set() in WordPress or a plugin is not an equivalent mitigation. PHP reads server-module startup configuration when the web server starts, so recreate the container to restart its workers.
Disabling this feature alone does not fix the two critical CVEs. Keep the patched runtime and the residual mitigation together.
Add a security-aware healthcheck
Create config/wordpress/healthcheck.php. The check below verifies the reviewed PHP thresholds, the session setting and a local HTTP response:
<?php
function fail(string $message): never
{
fwrite(STDERR, $message . PHP_EOL);
exit(1);
}
// First fixes for CVE-2026-109618 and CVE-2026-109619.
$minimum = [
'8.2' => '8.2.35',
'8.3' => '8.3.36',
'8.4' => '8.4.27',
'8.5' => '8.5.12',
];
$branch = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION;
if (!isset($minimum[$branch]) || version_compare(PHP_VERSION, $minimum[$branch], '<')) {
fail('PHP runtime does not meet the reviewed security baseline');
}
if (filter_var(ini_get('session.upload_progress.enabled'), FILTER_VALIDATE_BOOLEAN)) {
fail('PHP session upload progress must be disabled');
}
$context = stream_context_create([
'http' => [
'timeout' => 5,
'ignore_errors' => true,
'follow_location' => 0,
'header' => "Connection: close\r\n",
],
]);
$headers = @get_headers('http://127.0.0.1/wp-login.php', false, $context);
$status = is_array($headers) ? ($headers[0] ?? '') : '';
if (!preg_match('/^HTTP\/\S+\s+[23]\d\d\b/', $status)) {
fail('Unexpected WordPress HTTP response: ' . $status);
}
The version map enforces a reviewed security baseline and deliberately rejects unreviewed PHP branches until it is updated. The HTTP test uses PHP’s get_headers() and accepts successful responses and redirects; it does not prove that login, database writes, uploads or every plugin work. Adapt the endpoint if your authentication setup changes the normal login route, and keep functional checks outside the probe.
Wire the files into Docker Compose
Save the following as compose.security.yaml beside your existing compose.yaml. It is an overlay for the existing wordpress service: the base file continues to define its database connection, credentials, storage, networks and routing. Review the merged configuration, especially if another config already uses one of these target paths. Compose merge rules keep file paths relative to the first Compose file.
services:
wordpress:
image: ${WORDPRESS_IMAGE:?Set a reviewed tag@sha256 image reference}
configs:
- source: php_security
target: /usr/local/etc/php/conf.d/zz-security.ini
mode: 0444
- source: wordpress_healthcheck
target: /usr/local/bin/wordpress-healthcheck.php
mode: 0444
healthcheck:
test: ["CMD", "php", "/usr/local/bin/wordpress-healthcheck.php"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
configs:
php_security:
file: ./config/php/security.ini
wordpress_healthcheck:
file: ./config/wordpress/healthcheck.php
The INI file is non-secret configuration, so a Compose config is appropriate. Keep passwords in your existing secret-management mechanism. This patch does not require changing the database engine version or replacing a database volume.
Recreate the WordPress container and verify the result
Before the change, take a logical database backup and a consistent copy or snapshot of the application volume, and test recovery on an isolated installation. Check free space for the candidate image and rollback artifacts. For a single WordPress instance, allow a short maintenance window while the container is replaced.
With WORDPRESS_IMAGE set to the reviewed reference, apply the overlay to the existing installation:
docker compose -f compose.yaml -f compose.security.yaml config --quiet
docker compose -f compose.yaml -f compose.security.yaml pull wordpress
docker compose -f compose.yaml -f compose.security.yaml up -d --no-deps --force-recreate --wait --wait-timeout 180 wordpress
docker compose -f compose.yaml -f compose.security.yaml exec -T wordpress php -v
docker compose -f compose.yaml -f compose.security.yaml exec -T wordpress php --ini
docker compose -f compose.yaml -f compose.security.yaml exec -T wordpress php -r 'var_export(filter_var(ini_get("session.upload_progress.enabled"), FILTER_VALIDATE_BOOLEAN)); echo PHP_EOL;'
docker compose -f compose.yaml -f compose.security.yaml exec -T wordpress php /usr/local/bin/wordpress-healthcheck.php
docker compose -f compose.yaml -f compose.security.yaml ps
docker compose -f compose.yaml -f compose.security.yaml logs --since 5m wordpress
--no-deps limits this operation to the application service, while --force-recreate ensures fresh workers load the startup INI settings even when only configuration changed. Compose documents these options and the health wait. Inspect failures before continuing: an HTTP 200 alone does not establish the PHP version or mitigation state.
Expect a patched PHP release, the security INI file in the scanned configuration, false for upload progress and a successful probe. CLI checks are useful, but confirm that the web SAPI loads the intended PHP build and startup configuration too, using an authenticated diagnostic or a staging-only probe. Remove temporary probes afterwards.
- Check the public HTTPS endpoint through the real reverse proxy, not just loopback HTTP.
- Exercise login, an existing post, media handling and any critical forms or integrations.
- Review PHP and application logs, health failures, restarts, CPU, memory and out-of-memory events during an observation window.
- Keep backup files, credentials and detailed diagnostic output out of public pages and source control.
Keep persistent WordPress updates separate
When /var/www/html is mounted from persistent storage, the official WordPress entrypoint initializes the application only when the expected installation files are absent. Replacing the image can therefore update PHP while the mounted WordPress core, plugins and themes remain unchanged. The image tag and the code actually served must be checked independently.
Manage core and extension updates through the supported WordPress update process, with their own compatibility checks and backups. A routine PHP patch does not require automatically replacing the application tree or running a database updater. If a later application update changes files or schema, returning to an older image cannot undo those persistent changes.
The Swarm and rollback details that matter
For Docker Swarm, apply equivalent settings through your stack deployment workflow. Swarm Configs are immutable: give each changed INI or healthcheck Config a new name or content-derived version, and update the service reference while keeping the in-container target path stable. Remove old Configs only after the replacement tasks are healthy. Docker documents Config rotation.
Health checks also have different operational consequences. A standalone Compose container can be marked unhealthy without its restart policy automatically restarting it for that reason alone; Swarm replaces tasks that fail health checks. Plan a maintenance procedure before a core or plugin updater temporarily makes HTTP requests fail. Do not let a health-triggered restart interrupt a file replacement or schema migration.
A runtime-only rollback is possible when the application files and database remain compatible and unchanged, but restoring an old vulnerable PHP image reopens the security exposure. If application data or schema changed too, recovery needs the matching files and database backup. Keep the runtime patch, the residual mitigation and the recovery plan aligned.
Official references
- PHP advisory: CVE-2026-109618 — RCE via OOM corruption
https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w - PHP advisory: CVE-2026-109619 — RCE via stale global state
https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp - PHP 2026 news archive: October 21 security release and October 23 session upload-progress advisory
https://www.php.net/archive/2026.php - WordPress core: PHP compatibility and WordPress versions
https://make.wordpress.org/core/handbook/references/php-compatibility-and-wordpress-versions/ - Docker: inspect registry images and digests with buildx imagetools
https://docs.docker.com/reference/cli/docker/buildx/imagetools/inspect/ - Docker Compose: merge configuration files
https://docs.docker.com/compose/how-tos/multiple-compose-files/merge/ - Docker Compose: configs
https://docs.docker.com/reference/compose-file/configs/ - PHP manual: startup configuration files
https://www.php.net/manual/en/configuration.file.php - Docker Compose: service healthchecks
https://docs.docker.com/reference/compose-file/services/#healthcheck - Docker Compose: up, force-recreate and wait
https://docs.docker.com/reference/cli/docker/compose/up/ - Official WordPress image: initialization and persistent files
https://github.com/docker-library/wordpress/blob/master/docker-entrypoint.sh - WordPress documentation: updating WordPress
https://wordpress.org/documentation/article/updating-wordpress/ - Docker Swarm: immutable configs and rotation
https://docs.docker.com/engine/swarm/configs/ - Docker Swarm: tasks, health checks and replacement
https://docs.docker.com/engine/swarm/how-swarm-mode-works/services/ - PHP manual: get_headers()
https://www.php.net/manual/en/function.get-headers.php