Repository navigation
Pin Caddy to the version FrankenPHP's go.mod declares - #713
Merged
jaydrogers merged 2 commits intoOct 3, 2026
Merged
jaydrogers merged 2 commits into
jaydrogers merged 2 commits into
Conversation
xcaddy build with no version resolves the latest Caddy release, which overrides the pin in caddy/go.mod at the FRANKENPHP_VERSION tag. Caddy 2.11.6 segfaults the worker on HTTP/2 when PHP keeps running after fastcgi_finish_request(), which takes down every Laravel app behind HTTP/2 or a Traefik backend. Fixes #712
Contributor
Images for PR #713
Try it: docker run --rm -v "$PWD:/var/www/html" -p 8080:8080 serversideup/php-dev:713-8.5-fpm-nginxEvery image is on Docker Hub as All images with sizesSizes are compressed, per architecture.
Updated on every push to this PR. |
Nothing in the suite called fastcgi_finish_request() and then kept working, so the Caddy 2.11.6 crash reached beta3 unnoticed. The check runs against its own container with SSL_MODE=full, since HTTP/2 needs TLS and that mode stops serving the plain HTTP port, and it covers NGINX, Apache and FrankenPHP through their *_HTTPS_PORT variables. Containers no longer start with --rm. cleanup() already removes everything the script starts, while --rm discarded the logs of a container that crashed, which is exactly when they are worth reading.
jaydrogers
merged commit Oct 3, 2026
cd5b8a1
into
release/webserver-improvements-and-fixes
131 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #712
v5.0.0-beta3FrankenPHP images segfault the worker on HTTP/2 as soon as PHP keeps running afterfastcgi_finish_request(). Symfony'sResponse::send()calls it and Laravel runs terminate callbacks afterward, so every request kills the container. That covers Caddy serving browsers directly inSSL_MODE=fulland Traefik proxying to port 8443, which uses HTTP/2 to the backend.Cause
xcaddy buildwas called with no version, so xcaddy resolved the latest Caddy release at build time and overrode the pin incaddy/go.modat theFRANKENPHP_VERSIONtag. FrankenPHP v1.12.7 declarescaddyserver/caddy/v2 v2.11.4; beta2 happened to build against 2.11.4 and beta3 picked up 2.11.6. Nothing in this repo changed between the two betas to cause it, which is the other half of the problem: FrankenPHP builds were not reproducible.Upstream is unaffected because its own Dockerfile builds with
go installfrom thatgo.mod, sodunglas/frankenphp:1.12.7-php8.4ships Caddy 2.11.4.Changes
Pin Caddy. A
CADDY_VERSIONbuild arg set tov2.11.4is passed toxcaddy build, the same way the plugin versions are already pinned to matchcaddy/go.mod. Bumping Caddy is now a deliberate edit rather than whatever the build happens to resolve.Add the missing check.
scripts/test-image.shnever had a request that finished before PHP did, which is why this reached beta3. The new check serves a script that callsfastcgi_finish_request()and then keeps working, over both HTTP/1.1 and HTTP/2, and fails if the web server stops. It runs against its own container withSSL_MODE=full, because HTTP/2 needs TLS and that mode stops serving the plain HTTP port. It readsNGINX_HTTPS_PORT,APACHE_HTTPS_PORTorCADDY_HTTPS_PORT, so it covers all three web variations and skips images without a web server.Containers no longer start with
--rm.cleanup()already removes everything the script starts, while--rmdiscarded the logs of a container that crashed, which is exactly when they are worth reading. With it gone, a failure now prints the panic.Verification
Repro:
public/index.phpwithecho "ok"; fastcgi_finish_request(); usleep(10000);, run withSSL_MODE=full, onecurl -k --http2 https://localhost:8443/.8.4-frankenphp-v5.0.0-beta28.4-frankenphp-v5.0.0-beta3PHP is ruled out: dropping the official
dunglas/frankenphp:1.12.7-php8.4-bookwormbinary (same FrankenPHP, same PHP 8.4.26, Caddy 2.11.4) into the unmodified beta3 image stops the crash, 3/3.The new check was confirmed against two builds of this branch that differ only by
--build-arg CADDY_VERSION:v2.11.6: fails on❌ Web server stopped after a http2 request finished before PHP did (status: exited), after the HTTP/1.1 pass, and prints the panic. Exit 1.v2.11.4: all 15 checks pass.Full suite passes on
8.4-frankenphp-bookworm,8.4-frankenphp-alpine3.24,8.4-fpm-nginx-bookworm,8.4-fpm-apache-bookwormand8.4-fpm-bookworm(which skips the new check, having no web server). NGINX, Apache and FrankenPHP were each confirmed to really negotiate HTTP/2 rather than quietly falling back, withcurl --http2 -w '%{http_version}'.hadolint v2.15.1andshellcheck v0.11.0at warning severity are both clean, matching whatservice_lint.ymlruns.Follow-up, not in this PR
The underlying regression is in Caddy between 2.11.4 and 2.11.6 and should be reported upstream, otherwise a future deliberate bump reintroduces it. The new check in this PR turns that into a failed build rather than a broken release. The 121-commit range includes the Go 1.26 dependency floor bump, which moves
x/net/http2, andcaddyhttp: surface write timeout errors in access log.