Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/content/docs/2.image-variations/frankenphp.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ The format follows Caddy's default as well. Caddy [writes human-readable `consol
- `CADDY_LOG_FORMAT=console` if you read logs by eye with `docker compose logs` or `docker service logs` and want the colored, human-readable lines whether or not a terminal is attached.
- `CADDY_LOG_FORMAT=json` if a container runs with a terminal attached but you still want structured logs.

In both formats the request log redacts the `authorization` query parameter, so the JWT that [Mercure subscribers pass in the URL](https://mercure.rocks/spec#authorization){target="_blank"} never lands in your logs. This is the same filter that [FrankenPHP's own Caddyfile](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile){target="_blank"} recommends.
In both formats the request log redacts the `authorization` query parameter, so the JWT that Mercure 0.x subscribers pass in the URL never lands in your logs. This is the same filter that [FrankenPHP's own Caddyfile](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile){target="_blank"} recommends.

::warning
Laravel Octane only relays FrankenPHP's `stderr` and only understands JSON, so leave `CADDY_LOG_OUTPUT` and `CADDY_LOG_FORMAT` at their defaults when you run Octane. See [Logging with Octane](/docs/framework-guides/laravel/octane#logging).
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/3.framework-guides/1.laravel/octane.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ Octane also decides the log level. It sets `CADDY_SERVER_LOG_LEVEL` to `INFO` wh
If you want request logs in production, pass `--log-level=INFO` to `octane:start`. You get one JSON object per request on `stderr`, ready for your log collector.
::

The request log redacts the `authorization` query parameter, so the JWT that [Mercure subscribers pass in the URL](https://mercure.rocks/spec#authorization){target="_blank"} never lands in your logs. This is the same filter that [FrankenPHP's own Caddyfile](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile){target="_blank"} recommends.
The request log redacts the `authorization` query parameter, so the JWT that Mercure 0.x subscribers pass in the URL never lands in your logs. This is the same filter that [FrankenPHP's own Caddyfile](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile){target="_blank"} recommends.

## PHP Settings Still Apply
Octane does not change how PHP loads its configuration. FrankenPHP reads the same `php.ini` files from `/usr/local/etc/php/conf.d/` in every mode, so all of the `PHP_*` environment variables (like `PHP_MEMORY_LIMIT` and `PHP_OPCACHE_ENABLE`) work exactly as they do in classic mode. Any custom `.ini` files you mount into that directory apply as well.
Expand Down
12 changes: 11 additions & 1 deletion docs/content/docs/5.guide/5.major-version-migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,15 @@ Octane also needs the Caddy admin API and sets `CADDY_GLOBAL_OPTIONS` for itself
#### FrankenPHP: `REMOTE_ADDR` is now the client IP resolved from trusted proxies
Version 4 set `$_SERVER['REMOTE_ADDR']` to the TCP peer on FrankenPHP, even when Caddy had already worked out the real client IP for the access log. It now matches what Caddy resolved, the same as NGINX and Apache, and Caddy runs in strict mode so a client behind a trusted proxy cannot forge it. You are affected if anything in your app compares `REMOTE_ADDR` to a proxy's address. [Read the trusted proxies guide →](/docs/guide/configuring-trusted-proxies)

#### FrankenPHP: Mercure 1.0 rejects `publisher_jwt` and `subscriber_jwt`
Version 5 ships FrankenPHP 1.13, which includes Mercure 1.0. If you enable the Mercure hub with `publisher_jwt` or `subscriber_jwt`, including through the `mercure` array in `config/octane.php`, FrankenPHP now fails to start. Move the keys into an `issuer` block as shown in [FrankenPHP's Laravel guide](https://frankenphp.dev/docs/laravel/#mercure-support){target="_blank"}, or add `protocol_version_compatibility 8` to keep your current settings while you migrate. The [Mercure 1.0 upgrade guide](https://github.com/dunglas/mercure/blob/v1.0.3/docs/UPGRADE.md){target="_blank"} covers the client changes.

#### FrankenPHP: Caddy limits request headers
FrankenPHP 1.13 includes [Caddy 2.11.7](https://github.com/caddyserver/caddy/releases/tag/v2.11.7){target="_blank"}. Requests with more than 16 KiB of headers now get a `431 Request Header Fields Too Large` response, where Version 4 allowed 1 MB. Large cookies are the usual cause. Headers with a `.` in their name are now dropped, like headers with a `_`, because PHP reads both as `-` and a client could use them to spoof headers like `X-Forwarded-For`.

#### FrankenPHP: `num_threads` no longer includes worker threads
If you set `num_threads` in `FRANKENPHP_CONFIG` while running workers, including Laravel Octane, FrankenPHP now starts that many threads on top of the worker threads, and fails to start if `max_threads` is lower than the total. Lower `num_threads` to the number of threads you want for regular requests.

### Fixes
- `PHP_OPCACHE_FORCE_RESTART_TIMEOUT` existed in Version 4 but never reached `php.ini`. It now works. The default of `180` matches PHP's own default, so nothing changes unless you had set it to something else.

Expand Down Expand Up @@ -182,7 +191,7 @@ The OPcache values apply only when `PHP_OPCACHE_ENABLE=1`, and the memory is onl
- **Trusted proxies on every web server** - `TRUSTED_PROXY` gives `fpm-nginx`, `fpm-apache`, and `frankenphp` the same Cloudflare, Sucuri, local, or off behavior, and all three resolve the client IP through more than one Docker hop. [Read the trusted proxies guide →](/docs/guide/configuring-trusted-proxies)
- **Short-lived and IP-address certificates** - FrankenPHP can request Let's Encrypt's short-lived profile with `CADDY_ACME_PROFILE`. [Read about short-lived certificates →](/docs/deployment-and-production/configuring-ssl#short-lived--ip-address-certificates)
- **Laravel Nightwatch health check** - `healthcheck-nightwatch` runs `php artisan nightwatch:status` so Docker can watch the agent. [Read the Nightwatch guide →](/docs/framework-guides/laravel/nightwatch)
- **FrankenPHP redacts the `authorization` query parameter** - Request logs never contain the JWT that Mercure subscribers pass in the URL, in every log format. [Read about FrankenPHP logging →](/docs/image-variations/frankenphp#logging)
- **FrankenPHP redacts the `authorization` query parameter** - Request logs never contain the JWT that Mercure 0.x subscribers pass in the URL, in every log format. [Read about FrankenPHP logging →](/docs/image-variations/frankenphp#logging)
- **Every image is tested before it is published** - Each image is started on `amd64` and `arm64` and checked before it reaches Docker Hub. If one image fails, nothing from that build is published. [Read what happens when you open a pull request →](/docs/getting-started/contributing#what-happens-when-you-open-a-pull-request)

### V5 Migration Checklist
Expand All @@ -195,6 +204,7 @@ The OPcache values apply only when `PHP_OPCACHE_ENABLE=1`, and the memory is onl
- If your app calls `session_start()` itself and reads the session cookie from JavaScript, add `PHP_SESSION_COOKIE_HTTPONLY=Off`
- If you run FrankenPHP and something reads only `stdout`, add `CADDY_LOG_OUTPUT=stdout`. If something parses the `console` lines, or you prefer them when reading logs by eye, add `CADDY_LOG_FORMAT=console`. Skip both if you run Laravel Octane
- If you run Laravel Octane, add `--caddyfile=/etc/frankenphp/Caddyfile` to your `octane:start` command and remove any `FRANKENPHP_CONFIG` worker block or `CADDY_PHP_SERVER_OPTIONS` you added to make Octane work
- If you run the Mercure hub on FrankenPHP, move `publisher_jwt` and `subscriber_jwt` into an `issuer` block, or add `protocol_version_compatibility 8`

#### Dockerfile
- If you append to `/etc/s6-overlay/s6-rc.d/<service>/dependencies` for `php-fpm`, `nginx`, or `apache2`, move each line to an empty file in that service's `dependencies.d/` directory
Expand Down
47 changes: 20 additions & 27 deletions src/variations/frankenphp/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
ARG BASE_OS_VERSION='trixie'
ARG PHP_VERSION='8.5'
ARG BASE_IMAGE="php:${PHP_VERSION}-zts-${BASE_OS_VERSION}"
ARG FRANKENPHP_VERSION='1.12.7'
ARG CADDY_VERSION='v2.11.4'
ARG GOLANG_VERSION='1.26'
ARG FRANKENPHP_VERSION='1.13.0'
ARG GOLANG_VERSION='1.27'

########################
# Common
Expand Down Expand Up @@ -52,7 +51,6 @@ FROM golang:${GOLANG_VERSION} AS golang-image
####################
FROM common AS frankenphp-build
ARG FRANKENPHP_VERSION
ARG CADDY_VERSION
ARG GOLANG_VERSION
ARG BUILD_DEPENDENCY_PACKAGES_ALPINE="\
argon2-dev \
Expand Down Expand Up @@ -95,9 +93,6 @@ COPY --from=golang-image /usr/local/go /usr/local/go
ENV PATH="/usr/local/go/bin:${PATH}"
ENV GOTOOLCHAIN="local"

# Copy xcaddy in the builder image
COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy

# Install dependencies & Download FrankenPHP
RUN docker-php-serversideup-dep-install-alpine "$PHPIZE_DEPS ${BUILD_DEPENDENCY_PACKAGES_ALPINE}" && \
docker-php-serversideup-dep-install-debian "${BUILD_DEPENDENCY_PACKAGES_DEBIAN}"
Expand All @@ -119,33 +114,31 @@ RUN WATCHER_TARBALL_URL=$(docker-php-serversideup-download https://api.github.co
ldconfig; \
fi

# Download and build FrankenPHP
# Build FrankenPHP the way the official images do. go install takes every module
# version from caddy/go.sum and applies the PGO profile in caddy/frankenphp,
# while xcaddy resolves the latest Caddy release at build time (see #712)
WORKDIR /go/src/app
ENV GOBIN=/usr/local/bin

RUN git clone --depth 1 --branch "v${FRANKENPHP_VERSION}" \
https://github.com/php/frankenphp.git .

WORKDIR /go/src/app/caddy/frankenphp
RUN if cat /etc/os-release | grep -q 'debian'; then \
export ADDITIONAL_BUILD_FLAGS=''; \
export ADDITIONAL_LDFLAGS=''; \
elif cat /etc/os-release | grep -q 'alpine'; then \
export ADDITIONAL_BUILD_FLAGS="-extldflags '-Wl,-z,stack-size=0x80000'"; \
export ADDITIONAL_LDFLAGS="-extldflags '-Wl,-z,stack-size=0x80000'"; \
fi; \
git clone --depth 1 --branch v${FRANKENPHP_VERSION} \
https://github.com/php/frankenphp.git .; \
CGO_ENABLED=1 \
XCADDY_SETCAP=1 \
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
CGO_CFLAGS="-DFRANKENPHP_VERSION=${FRANKENPHP_VERSION} $(php-config --includes) $ADDITIONAL_BUILD_FLAGS" \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
# CADDY_VERSION and the plugin versions must match caddy/go.mod at the pinned
# FRANKENPHP_VERSION tag, otherwise xcaddy resolves the latest release, which
# may need a newer Go or ship a regression upstream never tested against
xcaddy build "${CADDY_VERSION}" \
--output /usr/local/bin/frankenphp \
--with github.com/dunglas/frankenphp=./ \
--with github.com/dunglas/frankenphp/caddy=./caddy/ \
--with github.com/dunglas/caddy-cbrotli@v1.0.1 \
# Mercure and Vulcain are included in the official build, but feel free to remove them
--with github.com/dunglas/mercure/caddy@v0.24.2 \
--with github.com/dunglas/vulcain/caddy@v1.4.2
CGO_CFLAGS="-DFRANKENPHP_VERSION=v${FRANKENPHP_VERSION} ${PHP_CFLAGS}" \
CGO_CPPFLAGS="${PHP_CPPFLAGS}" \
CGO_LDFLAGS="-L/usr/local/lib -lssl -lcrypto -lreadline -largon2 -lcurl -lonig -lz ${PHP_LDFLAGS}" \
../../go.sh install \
-ldflags "-w -s ${ADDITIONAL_LDFLAGS} -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP v${FRANKENPHP_VERSION} PHP ${PHP_VERSION} Caddy' -X 'github.com/caddyserver/caddy/v2.CustomBinaryName=frankenphp' -X 'github.com/caddyserver/caddy/v2/modules/caddyhttp.ServerHeader=FrankenPHP Caddy'" \
-buildvcs=true && \
setcap cap_net_bind_service=+ep /usr/local/bin/frankenphp && \
frankenphp version && \
frankenphp build-info

####################
# FrankenPHP Final
Expand Down