Release 5.0: Add IP address SSL certificates, Additonal Proxy Support, web server improvements, and other fixes - #645
Open
jaydrogers wants to merge 56 commits into
Open
jaydrogers wants to merge 56 commits into
jaydrogers wants to merge 56 commits into
Conversation
…643) * Enhance trusted proxy support across configurations Updated documentation and configuration files to improve trusted proxy handling. Introduced customizable trusted proxy settings for Cloudflare, Sucuri, and local proxies, ensuring accurate IP logging. Removed hardcoded Cloudflare IPs from NGINX and Apache configurations, replacing them with a dynamic inclusion based on the TRUSTED_PROXY environment variable. * Clarify trusted proxy documentation for Cloudflare and Sucuri Updated the documentation to specify that both Cloudflare and Sucuri configurations now automatically include local Docker networks. Added a tip to inform users that they can use the `cloudflare` setting while also trusting local proxies, enhancing clarity on trusted proxy usage. * Add global Caddy configuration support in FrankenPHP Updated the Dockerfile to create a directory for global Caddy configurations and modified the Caddyfile to import additional configuration files from the new caddyfile-global.d directory, enhancing flexibility in Caddy setup. * Fix Dockerfile syntax by correcting line continuation for Caddy configuration paths
This comment was marked as outdated.
This comment was marked as outdated.
* Config checks for SSL certificate generation Add checks for DISABLE_DEFAULT_CONFIG and set default SSL file paths. * Removed comment since the code is readable --------- Co-authored-by: Jay Rogers <3174134+jaydrogers@users.noreply.github.com>
…atabase connection checks (#628) * Refactor Laravel migration script to improve isolation handling and database connection checks - Removed migration isolation argument from test-db-connection script. - Introduced a helper function for running migrations per database, enhancing clarity and reusability. - Updated migration flag assembly logic to better manage isolation settings. - Improved error handling and logging for database connection issues. * Remove debug log from Laravel migration script to streamline output during migrations * Update Laravel migration script to change error message from error to warning for version check * Update PHP base opertating systems. Add Alpine 3.23 support (#638) PHP repo no longer has images available for alpine3.21 for latest minor versions, this removes 3.21 if its no longer available and adds 3.23 if available * Update PHP extension installer version to 2.9.27 * Update NGINX version to 1.28.1 * Refactor and improve security headers, file blocks, etc (#631) * Fix blocking .well-known path for FrankenPHP * Remove specific endpoint checks because the whole folder should be allowed * Enhance security configurations across Apache, NGINX, and Caddy by implementing best practices for HTTP headers and file access restrictions. Added protections against clickjacking, MIME type sniffing, and sensitive file exposure while allowing necessary access to well-known URIs as per RFC 8615. --------- Co-authored-by: Jay Rogers <3174134+jaydrogers@users.noreply.github.com> Co-authored-by: Jay Rogers <jaydrogers@users.noreply.serversideup.net> * Update GitHub Actions to use actions/checkout@v6 in multiple workflows * Update GitHub Actions to use actions/upload-artifact@v6 for improved artifact handling * Update GitHub Actions to use actions/download-artifact@v7 for improved artifact handling * Upgrade FrankenPHP to v1.11.1 * Increase size of GitHub Actions Runners because of memory segmentation fault issues (Thanks Depot! 😅) * Add security measures to block PHP execution in storage directory (#641) Implemented restrictions across Apache, NGINX, and FrankenPHP configurations to prevent the execution of PHP files in the /storage directory, addressing potential vulnerabilities related to arbitrary file uploads (GHSA-29cq-5w36-x7w3). * Refactor Laravel version check and isolation mode handling in automation script - Updated the logic for enabling isolation mode based on Laravel version, ensuring it only activates for versions 9.38.0 and above. - Improved error handling and logging for Laravel version detection, providing clearer output when version determination fails. - Simplified version comparison logic to enhance readability and maintainability. * Update container info script to include automation status --------- Co-authored-by: alloylab <trevor@alloylab.com> Co-authored-by: Marcel Arns <38068686+marns93@users.noreply.github.com>
This was referenced Jan 27, 2026
* Add Nightwatch health check script * Add documentation for Laravel Nightwatch --------- Co-authored-by: Jay Rogers <3174134+jaydrogers@users.noreply.github.com> Co-authored-by: Jay Rogers <jaydrogers@users.noreply.serversideup.net>
…n get-php-versions.sh - Added a retry mechanism with a maximum of 3 attempts for fetching PHP versions from the specified JSON feed. - Included validation to ensure the response is valid JSON, with error handling for invalid responses. - Enhanced error messages to provide more context in case of failures.
…andling - Modified the curl command in get-php-versions.sh to include a custom user agent string for better identification of requests. - This change aims to enhance compatibility with the PHP versions JSON feed.
… configurations (#646)
* Add import for DB facade * Move DB facade import to file scope --------- Co-authored-by: Jay Rogers <jaydrogers@users.noreply.serversideup.net>
…e/webserver-improvements-and-fixes' of github.com:serversideup/docker-php into release/webserver-improvements-and-fixes
* Enhance logging and SSL generation scripts - Updated the `LOG_OUTPUT_LEVEL` description in the documentation to clarify verbosity levels and their impact on logging behavior. - Modified the entrypoint script to remove unnecessary checks for `LOG_OUTPUT_LEVEL` when displaying container info. - Added checks in the SSL generation script to conditionally log messages based on the `LOG_OUTPUT_LEVEL`, improving debugging capabilities. * Update log level in debug Caddyfile from INFO to DEBUG for enhanced logging detail
… rule (#691) * fix: update security rules to prevent edge cases in PHP execution for storage directories Expanded the regex patterns in Apache, Nginx, and Caddy configurations to block PHP file execution more reliably by accounting for trailing slashes after `.php`. * Add storage directory and enforce PHP execution restrictions --------- Co-authored-by: Jay Rogers <3174134+jaydrogers@users.noreply.github.com> Co-authored-by: Jay Rogers <jaydrogers@users.noreply.serversideup.net>
… depend on base (#689) * fix(s6): make web services wait for their config oneshots to fix root-mode startup race When a container built on the s6 images runs as root, php-fpm and the web server (nginx/apache2) are brought up in parallel with the entrypoint oneshots that configure them, because the long-running services have no dependency on those oneshots. As root this races: - php-fpm reads its pool before `5-fpm-pool-user` appends `user`/`group`, failing with "ALERT: [pool www] user has not been defined" -> "ERROR: FPM initialization failed". - the web server starts before `10-init-webserver-config` renders its config (e.g. nginx: open() "/etc/nginx/nginx.conf" failed). s6 restarts the crashed services so the container eventually recovers, which is why the failure is intermittent and hard to reproduce (see discussion #425), but it emits alarming errors, slows startup, and leaves a brief window with no service. docker-php-serversideup-s6-init now adds a dependency from each web service to the entrypoint oneshot that configures it, appending to the existing flat `dependencies` file. The oneshots are chained in alphabetical order, so depending on one transitively waits for all earlier ones (php-fpm -> 5-fpm-pool-user; nginx/apache2 -> 10-init-webserver-config). Entries are de-duplicated and appended newline-safely (nginx's shipped `dependencies` has no trailing newline). Dependencies are only added when both the service and the oneshot exist, so cli/fpm/frankenphp images and images that remove a script are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Migrate shipped services and generated oneshots from the deprecated flat dependencies file to dependencies.d, make every user service depend on base as s6-overlay recommends, and make php-fpm, nginx, and apache2 wait for the oneshots that write their config. Fixes root-mode FPM 'user has not been defined' race (#425, #689). Co-authored-by: LorenzoRogai <1665768+LorenzoRogai@users.noreply.github.com> Co-authored-by: mbrodala <5037116+mbrodala@users.noreply.github.com> --------- Co-authored-by: Lorenzo Rogai <lorenzo@shippypro.com> Co-authored-by: Jay Rogers <3174134+jaydrogers@users.noreply.github.com> Co-authored-by: Jay Rogers <jaydrogers@users.noreply.serversideup.net> Co-authored-by: LorenzoRogai <1665768+LorenzoRogai@users.noreply.github.com> Co-authored-by: mbrodala <5037116+mbrodala@users.noreply.github.com>
…and PHP_SESSION_COOKIE_HTTPONLY environment variables (#692) * Add new configureable variables * Reorganized variables and added documentation * Add validation for PHP_* environment variables in php.ini --------- Co-authored-by: Jay Rogers <3174134+jaydrogers@users.noreply.github.com> Co-authored-by: Jay Rogers <jaydrogers@users.noreply.serversideup.net>
…ile (#702) The Dockerfile declared FRANKEN_PHP_CONFIG, but the Caddyfile and the docs reference FRANKENPHP_CONFIG. Rename the ENV so the default matches the variable Caddy actually reads.
…e ranges and implement strict header processing for enhanced security. Fixes #703
This was referenced Sep 10, 2026
* Enhance OPcache configuration and documentation - Updated default OPcache settings for production mode in various documentation files, emphasizing the importance of `PHP_OPCACHE_ENABLE` and its impact on performance. - Added a new guide on PHP OPcache tuning, detailing how to measure and adjust OPcache settings for optimal application performance. - Clarified the behavior of `PHP_OPCACHE_VALIDATE_TIMESTAMPS` and its implications for development and production environments. - Adjusted Dockerfiles across multiple variations to reflect new OPcache defaults, including increased memory consumption and maximum accelerated files. - Improved scripts and entrypoint messages to provide clearer guidance on OPcache usage and best practices for deployment. * Refactor OPcache status retrieval and update production mode notice for clarity * Update migration and tuning documentation for OPcache improvements * Refactor OPcache documentation: update migration guide and add production performance tuning guide * Enhance OPcache configuration and documentation: update defaults, improve CLI handling, and clarify production settings * Update OPcache notice link for performance tuning documentation * Refine OPcache performance tuning guide: remove placeholder text and clarify custom ini file options * Clarify OPcache explanation in production performance tuning guide
avvertix
reviewed
Sep 16, 2026
* Provide further Octane support for Caddyfile Specify the `CADDY_SERVER_WORKER_DIRECTIVE` and `CADDY_SERVER_WATCH_DIRECTIVES` directives that are populated by Laravel Octane * Enhance FrankenPHP integration with Laravel Octane - Updated environment variable documentation to clarify the behavior of CADDY_ADMIN, CADDY_GLOBAL_OPTIONS, CADDY_LOG_FORMAT, and CADDY_LOG_OUTPUT when using Laravel Octane. - Added a new health check command for Octane to verify server status. - Refactored the test image script to handle multiple containers and improve health check logic. - Modified SSL generation script to account for FrankenPHP's unique requirements. - Adjusted Dockerfile to set default logging format and output for better compatibility with Octane. - Enhanced Caddyfile configuration to support Octane's worker and global options. - Introduced new log format files to handle authorization redaction in logs. - Created README files in configuration directories to guide users on custom Caddyfile usage. - Implemented separate Caddyfile configurations for Octane to manage worker and global settings effectively. * Update FrankenPHP dependencies in Dockerfile to match specific plugin versions --------- Co-authored-by: Jay Rogers <3174134+jaydrogers@users.noreply.github.com> Co-authored-by: Jay Rogers <jaydrogers@users.noreply.serversideup.net> Co-authored-by: Robin Lyons <39706150+aSeriousDeveloper@users.noreply.github.com> Co-authored-by: kohenkatz <88755+kohenkatz@users.noreply.github.com>
…ed PHP versions, and new environment variables
- Document changes to S6 Overlay user bundle location and usage. - Enhance `docker-php-serversideup-s6-init` script for better service management and execution order.
avvertix
reviewed
Sep 17, 2026
avvertix
reviewed
Sep 17, 2026
…ntation for entrypoint scripts
nginx: [warn] low address bits of 127.0.0.1/8 are meaningless in /etc/nginx/trusted-proxy/cloudflare.conf:9
…etails and health check command description
…failures in forks
This branch has not been deployed
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.
👨🔬 How to test
You can test this image using our serversideup/php-dev repository, which automatically builds on push to this PR.
View the available testing images →
🌎 Docs Preview
You can view the latest docs for this release here:
🤩 What's new
Short-lived & IP address certificates for FrankenPHP
Configure short-lived & IP certificates →
Native Laravel Octane support with a hardened and optimized Caddyfile (#604)
Add
--caddyfile=/etc/frankenphp/Caddyfiletooctane:startand FrankenPHP switches into worker mode with the same trusted proxies, security headers, asset caching, SSL modes, and health check as classic mode. Octane's--workers,--watch,--max-requests,octane:status, andoctane:reloadall work. No wrapper script, no second Caddyfile.Thanks to @aSeriousDeveloper for opening the original PR and coolio85 on Discord for the workaround that started it.
Read the Octane guide →
Expanded OPcache support and better defaults (#704)
OPcache is still off by default to save your hair when you're in development. When you set
PHP_OPCACHE_ENABLE=1, you now get the settings from Symfony's performance guide instead of PHP's stock values: more memory, room for more files, and no filesystem checks per request. Preloading is available withPHP_OPCACHE_PRELOAD. This is a breaking change if you mount your code as a volume with OPcache enabled (see below).Read the production performance tuning guide →
Trusted proxy support for every variation (#643)
TRUSTED_PROXYnow works the same way across all variations (FrankenPHP, NGINX, and Apache), so your app sees the real client IP whether it's behind Cloudflare, a load balancer, or your own reverse proxy. The version in thev4.6.0betas returned the wrong IP in multi-hop Docker setups (#703). That's fixed, and FrankenPHP'sREMOTE_ADDRnow matches what Caddy resolves. FrankenPHP users also get/etc/frankenphp/caddyfile-global.d/for global Caddy config without escapingCADDY_GLOBAL_OPTIONS.Configuring trusted proxies →
Native Laravel Nightwatch support (#572)
Added a native health check for Laravel Nightwatch, complete with documentation to wire it up.
Laravel Nightwatch with Docker →
CI improvements: Every image is tested before it's published (#700)
Build and publish used to be one step. Now every image is built for
amd64andarm64, pulled on native runners, and checked before anything ships: it starts, runs unprivileged, loads the default extensions, passes its health check, and serves PHP through its web server. If one image fails, nothing from that run is published. Read more about how this works in #700.New environment variables
All of them are documented in the environment variable specification.
TRUSTED_PROXYcloudflarecloudflare,sucuri,local, oroff.CADDY_ACME_PROFILEoffshortlived(~6-day certs, required for IP-address certificates),tlsserver,classic, oroff.LARAVEL_OCTANEAUTORUN_LARAVEL_SKIP_IF_NOT_FOUNDfalsetrue, Laravel automations exit gracefully if Laravel is not found inAPP_BASE_DIRinstead of failing the container. RequiresAUTORUN_ENABLED=true.PHP_OPCACHE_ENABLE_CLI1PHP_OPCACHE_ENABLE=1.1is what v4 did. Set to0to keep OPcache on for the web server only.PHP_OPCACHE_PRELOAD""PHP_OPCACHE_PRELOAD_USER""PHP_FILE_UPLOADSOnOffto harden apps that never accept uploads.PHP_MAX_FILE_UPLOADS20PHP_DISABLE_FUNCTIONS""exec,shell_exec. Empty because Laravel, Composer, and Symfony Process rely onproc_open.PHP_HTML_ERRORSOnPHP_DISPLAY_ERRORSis on. Never affects logged errors, and PHP forces it off for the CLI.PHP_REALPATH_CACHE_SIZE4096Kvendor/directory may benefit from raising it.PHP_SESSION_COOKIE_HTTPONLYOnHttpOnlyflag to the native PHP session cookie so browser scripts cannot read it. See the note below.Changed defaults
The
PHP_OPCACHE_*values only apply whenPHP_OPCACHE_ENABLE=1.CADDY_*applies to FrankenPHP only.PHP_OPCACHE_VALIDATE_TIMESTAMPS10PHP_OPCACHE_MEMORY_CONSUMPTION128256PHP_OPCACHE_INTERNED_STRINGS_BUFFER832PHP_OPCACHE_MAX_ACCELERATED_FILES1000032531PHP_REALPATH_CACHE_TTL120600CADDY_LOG_OUTPUTstdoutstderrCADDY_LOG_FORMATconsoleautoThe v4 → v5 migration guide walks through each of these with a checklist. To stay on v4 while you review, pin your tag to
v4.5.1(for exampleserversideup/php:8.4-fpm-nginx-v4.5.1).Dropped PHP 7.4 and 8.0 (#699)
Debian 11 (Bullseye) LTS ended on 2026-08-31, and the following week Debian removed the Bullseye packages from its mirrors, so we can no longer install anything inside the build. PHP 7.4 and 8.0 only ever shipped on Bullseye and Alpine 3.16, which reached EOL in 2024, so both go with it. PHP 8.1 is EOL too, but stays built (pinned to 8.1.34) on bookworm, trixie, and alpine3.22 because those bases still receive security updates.
PHP 8.1 also drops
alpine3.21. Alpine 3.21 is supported until 2026-11-01, but 8.1 is EOL and only needs one Alpine base, so 8.1 on Alpine now meansalpine3.22. Change8.1-*-alpine3.21tags to8.1-*-alpine3.22.Your existing tags still work. Every 7.4, 8.0, and 8.1
alpine3.21tag stays pullable, frozen at its last successful build. If you need to install a package on one while you migrate, the migration guide has an unsupported recipe.Our supported matrix is now PHP 8.2 – 8.5 on bookworm, trixie, alpine3.23, and alpine3.24, plus PHP 8.1 on bookworm, trixie, and alpine3.22. SECURITY.md now states the rule: we build an image while its official base image exists and the operating system still receives security updates.
OPcache no longer checks for file changes (#704)
PHP_OPCACHE_VALIDATE_TIMESTAMPSnow defaults to0, so with OPcache enabled, PHP files are cached until the container restarts. You are affected if you setPHP_OPCACHE_ENABLE=1and mount your code as a volume, update WordPress on a volume outside the admin, or rundocker exec ... artisan optimizeagainst a live container. Restart the container after code changes, or setPHP_OPCACHE_VALIDATE_TIMESTAMPS=1to keep the v4 behavior. Nothing changes withPHP_OPCACHE_ENABLE=0.FrankenPHP logs go to
stderrand default to JSON (#604)CADDY_LOG_OUTPUTstdoutstderrCADDY_LOG_FORMATconsoleauto(Caddy's default:consoleon a terminal,jsonotherwise)docker logs, Compose, and Kubernetes capture both streams, so most setups only notice the format. You are affected if you read the two streams separately or parse theconsolelines. SetCADDY_LOG_FORMAT=consoleandCADDY_LOG_OUTPUT=stdoutto get the v4 behavior back, unless you run Octane, which needs the new defaults. These are Caddy's own defaults. Fixing both in the same major release means one upgrade instead of two.PHP_SESSION_COOKIE_HTTPONLYnow defaults toOn(#692)PHP recommends
session.cookie_httponly=Onfor production, so the images now ship it that way. Laravel, Symfony, and WordPress manage their own session cookies and are not affected. If your app callssession_start()directly and reads the session cookie from JavaScript, setPHP_SESSION_COOKIE_HTTPONLY=Offto keep the previous behavior.S6 dependency files moved to
dependencies.d/(#689)If your Dockerfile appends lines to
/etc/s6-overlay/s6-rc.d/<service>/dependenciesforphp-fpm,nginx, orapache2, S6 now ignores that file. Create an empty file independencies.d/instead:RUN touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/my-serviceStock images and rootless containers are not affected.
Laravel Octane: remove the old workaround (#604)
If you combined a
worker { }block inFRANKENPHP_CONFIGwith--caddyfile, FrankenPHP now fails with "global workers must not have duplicate filenames". Remove the block and anyCADDY_PHP_SERVER_OPTIONSyou added for Octane.CADDY_ADMINandCADDY_GLOBAL_OPTIONSare also ignored when Octane starts FrankenPHP. Use/etc/frankenphp/caddyfile-global.d/for global options.🔐 Security
PATH_INFObypass of the/storagePHP rule in Apache, NGINX, and FrankenPHP./storage/evil.phpwas denied, but/storage/evil.php/anythingstill ran (Security Improvement: Block PATH_INFO bypass of storage PHP execution rule #691)trusted_proxies_strict, so a client behind a trusted proxy can't forge its IP (TRUSTED_PROXY does not resolve the correct client IP in any variation #703)authorizationquery parameter that Mercure subscribers pass in the URL (Provide further Octane support for Caddyfile #604)HttpOnlyby default (Add PHP_DISABLE_FUNCTIONS, PHP_HTML_ERRORS, PHP_REALPATH_CACHE_SIZE, and PHP_SESSION_COOKIE_HTTPONLY environment variables #692)👨💻 DX improvements
composer install. The SSL script only runs when the container is starting its web server (Improve verbosity for log output #671)LOG_OUTPUT_LEVELdocs list all eight valid values.offwas removed because the container never accepted it. UseSHOW_WELCOME_MESSAGE=falseto hide the banner (Improve verbosity for log output #671)docker-php-serversideup-downloadretries downloads, anddocker-php-serversideup-install-php-extensionslets you swap an extension's source with thePHP_EXTENSION_OVERRIDESbuild arg (Drop PHP 7.4 and 8.0 support #699, Improve CI builds #700)🐛 Bug fixes
All variations
--isolated, and first deployments skip it until the migrations table exists (Refactor Laravel migration script to improve isolation handling and database connection checks #628, fixes Migration isolation failing even with script saying version is higher #627)DBfacade import forLARAVEL_AUTORUNon modern Laravel versions (Add import for DB facade #673, fixes Laravel autorun failing on database connection. #672)PHP_OPCACHE_FORCE_RESTART_TIMEOUTnever reachedphp.ini(Enhance OPcache configuration and documentation #704)All web servers (FrankenPHP, NGINX, Apache)
/index.php/pathnow redirects to/path(Fix duplicate SEO issues with Apache, NGINX, and FrankenPHP #646)DISABLE_DEFAULT_CONFIGis true (Do not generate SSL if DISABLE_DEFAULT_CONFIG is set #644)NGINX and Apache
php-fpmcould start before its pool user was set (Prevent S6 startup races: services wait for their config oneshots and depend on base #689)fpm-nginxandfpm-apachefailing to start with s6-overlay 3.2.3. This only affected thev4.6.0betas (Improve CI builds #700)docker-php-serversideup-s6-initnow works when a base image runs it and an image built on that base runs it again. Scripts you add in the second image are ordered with the ones converted earlier, and a script with the same name replaces the earlier one instead of running twice, once through the entrypoint and once through S6. This matches what aCOPYinto/etc/entrypoint.ddoes without the commandApache
.well-known(Apache: Change DirectoryMatch to LocationMatch in Apache security configuration #652, fixes Apache to denying requests to .well-known #649)FrankenPHP
FRANKENPHP_CONFIGtypo in the image's declared environment (Fix FRANKENPHP_CONFIG environment variable typo in FrankenPHP Dockerfile #702)REMOTE_ADDRwas the TCP peer instead of the client IP resolved from trusted proxies (fixes TRUSTED_PROXY does not resolve the correct client IP in any variation #703)SERVERSIDEUP_DEFAULT_COMMAND(Improve verbosity for log output #671)LOG_OUTPUT_LEVEL=debugdidn't apply to the per-site Caddy log (Improve verbosity for log output #671)SSL_MODE=fullnow works when Octane is the container command (Provide further Octane support for Caddyfile #604)⏫ Dependency updates
🙏 Thanks
Thanks to @aSeriousDeveloper, @Abdulmajeed-Jamaan, @agabi10, @devhammed, @LorenzoRogai, @victorlap, @5ergiu, and @arnaud-ritti for the PRs, and to @mbrodala, @kohenkatz, @QarthO, @dbpolito, @tomschlick, @sertxudev, and @andreasdorfer for the reviews and testing that went into this release.