1. The problem I’m having:
I want caddy to always read the complete POST body, I doubt it does.
I tried to use a simple POST endpoint and sent arbitrary amounts of data to that endpoint. No matter how big the request was, caddy returned a 204 within several milliseconds:
{
### Global options
# no admin interface
admin off
# log to stdout for systemd or Docker
log {
output stdout
format console
level debug
}
debug
}
# -------------------------------------------------
# HTTP listener
# -------------------------------------------------
http:// {
request_body {
max_size 3KiB
}
handle /upload {
respond "" 204
}
handle {
respond "Not found" 404
}
}
Now I try to pass the request through a reverse proxy. I’ve put the request_body directive to many places but I cannot get caddy to respond with 413 on too large input. I use curl for my tests:
$ time curl -ki -H 'Content-type: application/octet-stream' --data-binary @verylargebin --noproxy \* http://10.100.200.32:10080/upload
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
Connection: close
Content-Length: 0
Date: Sun, 08 Feb 2026 14:56:05 GMT
Server: Caddy
Via: 1.1 Caddy
real 0m0,044s
user 0m0,013s
sys 0m0,031s
verylargebin is a 15MB file, using 1GB results in an out of memory error.
My actual goal is to measure the time required for valid HTTP POST requests. Any idea where I am wrong? I would happily skip the reverse proxy if there is an easier way.
2. Error messages and/or full log output:
2026/02/08 14:50:35.997 DEBUG http.handlers.reverse_proxy selected upstream {"dial": "127.0.0.1:60080", "total_upstreams": 1}
2026/02/08 14:50:35.997 DEBUG http.handlers.reverse_proxy upstream roundtrip {"upstream": "127.0.0.1:60080", "duration": 0.000463496, "request": {"remote_ip": "10.100.200.32", "remote_port": "50320", "client_ip": "10.100.200.32", "proto": "HTTP/1.1", "method": "POST", "host": "10.100.200.32:10080", "uri": "/upload", "headers": {"Content-Type": ["application/octet-stream"], "Content-Length": ["15728640"], "Expect": ["100-continue"], "X-Forwarded-For": ["10.100.200.32"], "X-Forwarded-Host": ["10.100.200.32:10080"], "Via": ["1.1 Caddy"], "User-Agent": ["curl/8.5.0"], "Accept": ["*/*"], "X-Forwarded-Proto": ["http"]}}, "headers": {"Server": ["Caddy"], "Date": ["Sun, 08 Feb 2026 14:50:35 GMT"], "Content-Length": ["0"]}, "status": 200}
3. Caddy version:
$ caddy --version
v2.10.2 h1:g/gTYjGMD0dec+UgMw8SnfmJ3I9+M2TdvoRL/Ovu6U8=
4. How I installed and ran Caddy:
docker run --rm \
-p 10080:80\
--user "$(id -u):$(id -g)" \
-v $(pwd)/conf:/config \
-v $(pwd)/conf:/etc/caddy \
-v $(pwd)/data:/data \
-v $(pwd)/certs:/etc/caddy/certs:ro \
-v $(pwd)/large.bin:/srv/static/large.bin:ro \
-e 'CORS_ORIGINS=https://localhost,https://[::1]' \
-e 'MAX_CONNS=20' \
-e 'MAX_UPLOAD_SIZE=2097152' \
caddy:2
a. System environment:
Docker version 27.2.1
b. Command:
caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
c. Service/unit/compose file:
d. My complete Caddy config:
{
### Global options
# no admin interface
admin off
# log to stdout for systemd or Docker
log {
output stdout
format console
level debug
}
debug
}
##### upload size limiter <= NOT GLOBALLY
####@upload-api path /upload
# -------------------------------------------------
# internal reverse proxy endpoint
# -------------------------------------------------
http://127.0.0.1:60080 {
request_body {
max_size 3KiB
}
handle /upload {
header +X-Reverse-Proxy-Handler yes
respond "Read the file" 200
#import upload
}
handle {
respond "Not found" 404
}
}
# -------------------------------------------------
# HTTP listener
# -------------------------------------------------
http:// {
request_body {
max_size 3KiB
}
handle /upload {
#request_body {
# max_size 3KiB
#}
reverse_proxy http://127.0.0.1:60080
}
handle {
respond "Not found" 404
}
}