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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions