Skip to content

bug: client_max_body_size 0 rejects all chunked transfer-encoding requests with 413 #13694

Description

@Nicolfo

Current Behavior

When nginx_config.http.client_max_body_size is set to 0 (documented as "no limit", and also the default value), APISIX rejects every request that uses Transfer-Encoding: chunked with 413 Request Entity Too Large — regardless of the actual body size. Even a 1-byte chunked body is rejected immediately.

Requests carrying a Content-Length header are handled correctly: with client_max_body_size: 0 they pass through at any size (tested up to 300 MB).

This deviates from stock nginx semantics, where client_max_body_size 0; disables the body size check for both sized and chunked requests.

Real-world impact: docker push streams blob uploads (PATCH /v2/<name>/blobs/uploads/<uuid>) with chunked transfer encoding, so any container registry (e.g. Harbor) behind APISIX 3.17 with the default body size setting fails with 413 on every push. The apisix-helm-chart 2.16.0 renders client_max_body_size: 0 by default, so a default Helm installation is affected out of the box.

Setting an explicit non-zero value (e.g. 1g) restores correct behavior for chunked requests.

Expected Behavior

client_max_body_size: 0 should mean "unlimited" for chunked requests as well, consistent with the Content-Length case and with nginx's documented behavior.

Error Logs

Nothing is written to the error log. The access log shows the 413 being generated by APISIX itself with no upstream involved:

172.17.0.1 - - [13/Jul/2026:09:20:11 +0000] 127.0.0.1:19080 "POST /anything HTTP/1.1" 413 255 0.000 "-" "curl/8.7.1" - - - "http://127.0.0.1:19080"

Response:

< HTTP/1.1 413 Request Entity Too Large
< Server: APISIX/3.17.0

Steps to Reproduce

  1. config.yaml (standalone mode, minimal):
deployment:
  role: data_plane
  role_data_plane:
    config_provider: yaml
apisix:
  node_listen: 9080
nginx_config:
  http:
    client_max_body_size: 0
  1. apisix.yaml (any upstream works; it is never reached for chunked requests):
routes:
  -
    uri: /*
    upstream:
      nodes:
        "127.0.0.1:9180": 1
      type: roundrobin
#END
  1. Run APISIX:
docker run -d --name apisix-413 -p 19080:9080 \
  -v $PWD/config.yaml:/usr/local/apisix/conf/config.yaml:ro \
  -v $PWD/apisix.yaml:/usr/local/apisix/conf/apisix.yaml:ro \
  apache/apisix:3.17.0-debian
  1. Send the same 5-byte body with and without chunked encoding:
printf 'hello' > body.bin

# Content-Length request -> passes through (502 because the dummy upstream is dead)
curl -s -o /dev/null -w '%{http_code}\n' -X POST \
  --data-binary @body.bin http://127.0.0.1:19080/anything
# => 502

# chunked request -> rejected by APISIX before reaching the upstream
curl -s --http1.1 -o /dev/null -w '%{http_code}\n' -X POST \
  -H 'Transfer-Encoding: chunked' --data-binary @body.bin http://127.0.0.1:19080/anything
# => 413
  1. Change client_max_body_size to 1g and restart: the chunked request now returns 502 (passes through) as expected.

Notes:

  • Removing nginx_config.http.client_max_body_size entirely reproduces the same 413, so the default configuration is affected too.
  • Reproduced over both HTTP/1.1 and HTTP/2 (TLS), any method (GET/POST/PATCH), any route.

Environment

  • APISIX version: 3.17.0 (official image apache/apisix:3.17.0-debian, also deployed via apisix-helm-chart 2.16.0)
  • Operating system: Docker 29.4.0 on Windows 11 (also reproduced on Kubernetes/containerd on Linux)
  • OpenResty / Nginx version: bundled with the 3.17.0 image (Server: APISIX/3.17.0)
  • etcd version: n/a (standalone mode; also reproduced with etcd 3.5 in the Helm deployment)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    ✅ Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions