The official Docker images ship with PHP's stock post_max_size = 8M, but speedtest_worker.js uploads 20 MB chunks by default (xhr_ul_blob_megabytes: 20 at line 59). Every chunk hits the limit.
Two visible effects:
- PHP emits
POST Content-Length ... exceeds the limit. On the Debian image (display_errors = On from php:8-apache) the warning is written into the response body of /backend/empty.php, about 880 bytes of HTML where the worker expects an empty 200. On Alpine, display_errors = Off hides the warning but the body is still rejected.
- Once output has been emitted, every
header() call in empty.php (HTTP status, Cache-Control x2, Pragma, Connection, and the ?cors headers) fails with Cannot modify header information. The upload endpoint returns without those headers, and cross-origin upload tests miss their CORS headers.
The upload speed number is fine: the worker reads bytes-on-wire from xhr.upload.onprogress, not the body. But the response from empty.php is malformed.
Reproduce
docker run -d -p 8080:8080 ghcr.io/librespeed/speedtest:latest
dd if=/dev/zero bs=1M count=20 | curl -X POST --data-binary @- \
http://localhost:8080/backend/empty.php
# HTTP 200, 888 bytes
The body contains the PHP warning followed by five Cannot modify header information lines.
Affected
librespeed/speedtest:latest (FROM php:8-apache) and librespeed/speedtest:alpine (FROM php:8-alpine + apk add php-apache2). Mobile Chrome is the only client that escapes: the worker drops xhr_ul_blob_megabytes to 4 MB for that user agent at line 159, below the 8M default.
Prior art
#50 and #387 mention this as operator-tunable. #201 (2019) proposed post_max_size = 50M for Dockerfile.alpine; the file that eventually landed in #631 (2024) was written by a different contributor and didn't carry the bumps over.
Proposed fix
Ship docker/librespeed-php.ini with post_max_size = 32M and copy it into each base image's conf.d using paths each image already documents: ${PHP_INI_DIR} on Debian, /etc/php*/conf.d glob on Alpine. PR linked below.
The official Docker images ship with PHP's stock
post_max_size = 8M, butspeedtest_worker.jsuploads 20 MB chunks by default (xhr_ul_blob_megabytes: 20at line 59). Every chunk hits the limit.Two visible effects:
POST Content-Length ... exceeds the limit. On the Debian image (display_errors = Onfromphp:8-apache) the warning is written into the response body of/backend/empty.php, about 880 bytes of HTML where the worker expects an empty 200. On Alpine,display_errors = Offhides the warning but the body is still rejected.header()call inempty.php(HTTP status,Cache-Controlx2,Pragma,Connection, and the?corsheaders) fails withCannot modify header information. The upload endpoint returns without those headers, and cross-origin upload tests miss their CORS headers.The upload speed number is fine: the worker reads bytes-on-wire from
xhr.upload.onprogress, not the body. But the response fromempty.phpis malformed.Reproduce
The body contains the PHP warning followed by five
Cannot modify header informationlines.Affected
librespeed/speedtest:latest(FROM php:8-apache) andlibrespeed/speedtest:alpine(FROM php:8-alpine+apk add php-apache2). Mobile Chrome is the only client that escapes: the worker dropsxhr_ul_blob_megabytesto 4 MB for that user agent at line 159, below the 8M default.Prior art
#50 and #387 mention this as operator-tunable. #201 (2019) proposed
post_max_size = 50MforDockerfile.alpine; the file that eventually landed in #631 (2024) was written by a different contributor and didn't carry the bumps over.Proposed fix
Ship
docker/librespeed-php.iniwithpost_max_size = 32Mand copy it into each base image's conf.d using paths each image already documents:${PHP_INI_DIR}on Debian,/etc/php*/conf.dglob on Alpine. PR linked below.