From 3b3cddeb4135363fec46dcce5578ef18aa8da120 Mon Sep 17 00:00:00 2001 From: Akira Yamamoto <3007213+akirayamamoto@users.noreply.github.com> Date: Sun, 26 Apr 2026 18:36:35 +1000 Subject: [PATCH 1/6] fix(docker): raise PHP post_max_size for default 20MB upload chunks speedtest_worker.js uploads in 20 MB chunks by default (xhr_ul_blob_megabytes: 20) but the official Docker images inherit PHP's stock post_max_size = 8M / upload_max_filesize = 2M, so every upload chunk: * triggers a "POST Content-Length ... exceeds the limit" warning (leaked into the response body of /backend/empty.php on the Debian variant where display_errors is on; suppressed but still emitted on Alpine where it's off); * causes empty.php's subsequent header() calls to fail with "Cannot modify header information - headers already sent", leaving the response without proper status, cache, or CORS directives. Ship a small docker/librespeed-php.ini with post_max_size = 32M, upload_max_filesize = 32M, memory_limit = 256M and COPY it into the right conf.d for each base image (/usr/local/etc/php/conf.d on Debian, /etc/php84/conf.d on Alpine). 99- prefix follows the NN-name.ini packaging convention so this loads after distro defaults but never silently shadows operator overrides. Verified post-fix on both variants: a 20 MB POST to /backend/empty.php returns HTTP 200 / 0 bytes with no warning leakage. --- Dockerfile | 6 ++++++ Dockerfile.alpine | 6 ++++++ docker/librespeed-php.ini | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 docker/librespeed-php.ini diff --git a/Dockerfile b/Dockerfile index 8aeca244c..60665cecb 100755 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,12 @@ RUN install-php-extensions iconv gd pdo pdo_mysql pdo_pgsql pgsql \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* +COPY docker/librespeed-php.ini /tmp/librespeed-php.ini +RUN scan_dir="$(php -r 'echo rtrim(PHP_CONFIG_FILE_SCAN_DIR);')" \ + && [ -n "$scan_dir" ] \ + && install -D -m 0644 /tmp/librespeed-php.ini "$scan_dir/99-librespeed.ini" \ + && rm /tmp/librespeed-php.ini + # Prepare files and folders RUN mkdir -p /speedtest/ diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 47f694bd5..beec4c9ad 100755 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -20,6 +20,12 @@ RUN apk add --quiet --no-cache \ RUN ln -sf /dev/stdout /var/log/apache2/access.log && \ ln -sf /dev/stderr /var/log/apache2/error.log +COPY docker/librespeed-php.ini /tmp/librespeed-php.ini +RUN scan_dir="$(/usr/bin/php -r 'echo rtrim(PHP_CONFIG_FILE_SCAN_DIR);')" \ + && [ -n "$scan_dir" ] \ + && install -D -m 0644 /tmp/librespeed-php.ini "$scan_dir/99-librespeed.ini" \ + && rm /tmp/librespeed-php.ini + # Prepare files and folders RUN mkdir -p /speedtest/ diff --git a/docker/librespeed-php.ini b/docker/librespeed-php.ini new file mode 100644 index 000000000..82bcea321 --- /dev/null +++ b/docker/librespeed-php.ini @@ -0,0 +1,18 @@ +; LibreSpeed-recommended PHP override. +; +; speedtest_worker.js uploads in 20 MB chunks by default +; (xhr_ul_blob_megabytes: 20). PHP's stock post_max_size = 8M rejects +; every chunk: PHP emits a "POST Content-Length ... exceeds the +; limit" startup warning and prevents empty.php from sending its +; response headers (Cache-Control, Pragma, Connection, and CORS +; under ?cors). +; +; The upload throughput number itself is unaffected — the worker +; reads bytes-on-wire from xhr.upload.onprogress, not the response +; body — but the response from empty.php is otherwise malformed. +; +; 32M is the next round number above the worker's 20M default; it +; leaves headroom for operators who tune xhr_ul_blob_megabytes +; upwards. + +post_max_size = 32M From db4a5f8d5a5fecf3cea8bd32b58154c14836b5e2 Mon Sep 17 00:00:00 2001 From: Akira Yamamoto <3007213+akirayamamoto@users.noreply.github.com> Date: Mon, 27 Apr 2026 09:42:36 +1000 Subject: [PATCH 2/6] docker: drop runtime PHP scan-dir discovery for env var + apk path glob MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous build-time discovery via PHP_CONFIG_FILE_SCAN_DIR worked but invoked a PHP binary just to learn a path that's already a stable contract of each base image: * php:8-apache exports PHP_INI_DIR=/usr/local/etc/php as part of the docker-library image template, stable across PHP majors. Reference it directly in the COPY destination — no RUN, no validation needed. * Alpine's apk php-apache2 always installs mod_php's conf.d at /etc/phpXX/conf.d (currently /etc/php84). The FROM php:8-alpine image also ships its own PHP at /usr/local/etc/php/conf.d, but mod_php doesn't read from there. Glob /etc/php*/conf.d to track the apk-installed PHP major automatically; explicit error if the glob matches nothing. Net effect: Debian goes from a 5-line RUN block to a 1-line COPY. Alpine keeps a small RUN block but no longer invokes a PHP binary, so it doesn't matter which of the two PHP installs `php` resolves to via $PATH. Verified: both variants build, install the override at the right conf.d, mod_php reports post_max_size=32M, and a 20 MB POST to /backend/empty.php returns HTTP 200 / 0 bytes with no warnings. Co-Authored-By: Claude Opus 4.7 (1M context) --- Dockerfile | 9 ++++----- Dockerfile.alpine | 19 +++++++++++++++---- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 60665cecb..f969242b1 100755 --- a/Dockerfile +++ b/Dockerfile @@ -10,11 +10,10 @@ RUN install-php-extensions iconv gd pdo pdo_mysql pdo_pgsql pgsql \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -COPY docker/librespeed-php.ini /tmp/librespeed-php.ini -RUN scan_dir="$(php -r 'echo rtrim(PHP_CONFIG_FILE_SCAN_DIR);')" \ - && [ -n "$scan_dir" ] \ - && install -D -m 0644 /tmp/librespeed-php.ini "$scan_dir/99-librespeed.ini" \ - && rm /tmp/librespeed-php.ini +# PHP_INI_DIR is set by the official php:8-apache image to /usr/local/etc/php +# and has been stable across PHP majors. Using the env var documents intent +# and follows any future upstream change to the path automatically. +COPY docker/librespeed-php.ini ${PHP_INI_DIR}/conf.d/99-librespeed.ini # Prepare files and folders RUN mkdir -p /speedtest/ diff --git a/Dockerfile.alpine b/Dockerfile.alpine index beec4c9ad..a294ba694 100755 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -20,11 +20,22 @@ RUN apk add --quiet --no-cache \ RUN ln -sf /dev/stdout /var/log/apache2/access.log && \ ln -sf /dev/stderr /var/log/apache2/error.log +# This image has two PHP installs: the FROM php:8-alpine binary (conf.d at +# /usr/local/etc/php/conf.d) and the apk-installed php-apache2 (conf.d at +# /etc/phpXX/conf.d). mod_php uses the apk one — glob /etc/php*/conf.d to +# find it without pinning the PHP major. COPY docker/librespeed-php.ini /tmp/librespeed-php.ini -RUN scan_dir="$(/usr/bin/php -r 'echo rtrim(PHP_CONFIG_FILE_SCAN_DIR);')" \ - && [ -n "$scan_dir" ] \ - && install -D -m 0644 /tmp/librespeed-php.ini "$scan_dir/99-librespeed.ini" \ - && rm /tmp/librespeed-php.ini +RUN set -eu; \ + scan_dir=""; \ + for d in /etc/php*/conf.d; do \ + [ -d "$d" ] && scan_dir="$d" && break; \ + done; \ + if [ -z "$scan_dir" ]; then \ + echo "ERROR: no /etc/php*/conf.d directory found; apk php-apache2 install layout may have changed" >&2; \ + exit 1; \ + fi; \ + install -D -m 0644 /tmp/librespeed-php.ini "$scan_dir/99-librespeed.ini"; \ + rm /tmp/librespeed-php.ini # Prepare files and folders RUN mkdir -p /speedtest/ From 0feb8b504280c2a58bff5d0ecba63289c1061b03 Mon Sep 17 00:00:00 2001 From: Akira Yamamoto <3007213+akirayamamoto@users.noreply.github.com> Date: Tue, 9 Jun 2026 18:11:52 +1000 Subject: [PATCH 3/6] docs: update Alpine PHP ini comment --- Dockerfile.alpine | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Dockerfile.alpine b/Dockerfile.alpine index a294ba694..5290d0a6c 100755 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -20,10 +20,8 @@ RUN apk add --quiet --no-cache \ RUN ln -sf /dev/stdout /var/log/apache2/access.log && \ ln -sf /dev/stderr /var/log/apache2/error.log -# This image has two PHP installs: the FROM php:8-alpine binary (conf.d at -# /usr/local/etc/php/conf.d) and the apk-installed php-apache2 (conf.d at -# /etc/phpXX/conf.d). mod_php uses the apk one — glob /etc/php*/conf.d to -# find it without pinning the PHP major. +# Alpine installs PHP from apk; php-apache2 reads /etc/phpXX/conf.d. +# Use the versioned apk conf.d without pinning the PHP major. COPY docker/librespeed-php.ini /tmp/librespeed-php.ini RUN set -eu; \ scan_dir=""; \ From a30872a4e807cf51e160634a6d88e6b98b07494e Mon Sep 17 00:00:00 2001 From: Akira Yamamoto <3007213+akirayamamoto@users.noreply.github.com> Date: Tue, 9 Jun 2026 18:19:10 +1000 Subject: [PATCH 4/6] docs: shorten PHP upload limit comment --- docker/librespeed-php.ini | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/docker/librespeed-php.ini b/docker/librespeed-php.ini index 82bcea321..6a25f2fae 100644 --- a/docker/librespeed-php.ini +++ b/docker/librespeed-php.ini @@ -1,18 +1,5 @@ -; LibreSpeed-recommended PHP override. -; -; speedtest_worker.js uploads in 20 MB chunks by default -; (xhr_ul_blob_megabytes: 20). PHP's stock post_max_size = 8M rejects -; every chunk: PHP emits a "POST Content-Length ... exceeds the -; limit" startup warning and prevents empty.php from sending its -; response headers (Cache-Control, Pragma, Connection, and CORS -; under ?cors). -; -; The upload throughput number itself is unaffected — the worker -; reads bytes-on-wire from xhr.upload.onprogress, not the response -; body — but the response from empty.php is otherwise malformed. -; -; 32M is the next round number above the worker's 20M default; it -; leaves headroom for operators who tune xhr_ul_blob_megabytes -; upwards. +; speedtest_worker.js defaults xhr_ul_blob_megabytes to 20. +; PHP's stock post_max_size = 8M rejects those upload chunks before +; empty.php can send its headers. 32M leaves modest headroom. post_max_size = 32M From 914a29de7ac9cfcd0b8c73ae8d671f73991d64f6 Mon Sep 17 00:00:00 2001 From: Akira Yamamoto <3007213+akirayamamoto@users.noreply.github.com> Date: Tue, 9 Jun 2026 18:25:14 +1000 Subject: [PATCH 5/6] docker: simplify Alpine PHP ini install --- Dockerfile.alpine | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 5290d0a6c..1eecd354e 100755 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -20,19 +20,15 @@ RUN apk add --quiet --no-cache \ RUN ln -sf /dev/stdout /var/log/apache2/access.log && \ ln -sf /dev/stderr /var/log/apache2/error.log -# Alpine installs PHP from apk; php-apache2 reads /etc/phpXX/conf.d. -# Use the versioned apk conf.d without pinning the PHP major. +# Use PHP's scan dir without pinning the apk PHP major. COPY docker/librespeed-php.ini /tmp/librespeed-php.ini RUN set -eu; \ - scan_dir=""; \ - for d in /etc/php*/conf.d; do \ - [ -d "$d" ] && scan_dir="$d" && break; \ - done; \ + scan_dir="$(php -r 'echo rtrim(PHP_CONFIG_FILE_SCAN_DIR);')"; \ if [ -z "$scan_dir" ]; then \ - echo "ERROR: no /etc/php*/conf.d directory found; apk php-apache2 install layout may have changed" >&2; \ + echo "ERROR: PHP_CONFIG_FILE_SCAN_DIR is empty" >&2; \ exit 1; \ fi; \ - install -D -m 0644 /tmp/librespeed-php.ini "$scan_dir/99-librespeed.ini"; \ + install -m 0644 /tmp/librespeed-php.ini "$scan_dir/99-librespeed.ini"; \ rm /tmp/librespeed-php.ini # Prepare files and folders From 265c42d4bf2999034289df08418a29f014696091 Mon Sep 17 00:00:00 2001 From: Akira Yamamoto <3007213+akirayamamoto@users.noreply.github.com> Date: Tue, 9 Jun 2026 22:27:23 +1000 Subject: [PATCH 6/6] docs: trim Docker PHP ini comments Drop the Debian COPY comment (it only restated the code) and the version-fragility framing in the Alpine comment. Co-Authored-By: Claude Opus 4.8 (1M context) --- Dockerfile | 3 --- Dockerfile.alpine | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index f969242b1..ce75766e6 100755 --- a/Dockerfile +++ b/Dockerfile @@ -10,9 +10,6 @@ RUN install-php-extensions iconv gd pdo pdo_mysql pdo_pgsql pgsql \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -# PHP_INI_DIR is set by the official php:8-apache image to /usr/local/etc/php -# and has been stable across PHP majors. Using the env var documents intent -# and follows any future upstream change to the path automatically. COPY docker/librespeed-php.ini ${PHP_INI_DIR}/conf.d/99-librespeed.ini # Prepare files and folders diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 1eecd354e..e62ad9e1e 100755 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -20,7 +20,7 @@ RUN apk add --quiet --no-cache \ RUN ln -sf /dev/stdout /var/log/apache2/access.log && \ ln -sf /dev/stderr /var/log/apache2/error.log -# Use PHP's scan dir without pinning the apk PHP major. +# Install the ini into the scan dir PHP itself reports. COPY docker/librespeed-php.ini /tmp/librespeed-php.ini RUN set -eu; \ scan_dir="$(php -r 'echo rtrim(PHP_CONFIG_FILE_SCAN_DIR);')"; \