Skip to content

Default PHP upload limits (8M/2M) reject the worker's 20 MB upload chunks #797

@akirayamamoto

Description

@akirayamamoto

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:

  1. 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.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions