Caddy in front of a Sume webhook receiver: body limit and timeouts
Does Caddy break Sume webhook signatures? reverse_proxy passes headers and body through; set max_size above 1 MiB and an 8 s upstream timeout.

Yes: Caddy's reverse_proxy forwards the request body as-is and passes incoming headers through unchanged, so a Sume webhook arrives at your app with the same bytes and the same x-sume-webhook-signature and x-sume-webhook-timestamp headers that Sume signed. The three things to set on purpose are a request_body size limit above Sume's 1 MiB receipt ceiling, an upstream response timeout shorter than Sume's 10-second attempt budget, and a route that does not rewrite the path or buffer-and-modify the body.
Caddy facts below are from the vendor's reverse_proxy and request_body pages; Sume facts from its Run webhooks and Webhooks docs (all read 2026-10-11).
What does Caddy change on the way through?
Per Caddy's documentation, reverse_proxy sets X-Forwarded-For, X-Forwarded-Proto, and X-Forwarded-Host, and leaves other incoming headers, including Host, as they were. Sume's signature depends on the body and timestamp only, not on the URL or Host header, so those additions do not break verification.
The body is forwarded unchanged unless you configure request_buffers, which only reads it into a buffer before sending it upstream. You do not need that option here. Do not add a request_body { set ... } block either: it rewrites the body, which invalidates the signature.
| Setting | What the docs say | Choice for a Sume receiver |
|---|---|---|
| Headers | Passed through; only the three X-Forwarded-* are set | Nothing to configure |
| Request body | Forwarded as-is | Do not use set |
request_body max_size | Over the limit returns 413 | 2MB, since Sume's inline receipt limit is 1,048,576 bytes |
response_header_timeout | No timeout by default | 8s, under Sume's 10s per-attempt timeout |
| Redirects | Sume does not follow them; a 3xx is a failed attempt | Never redirect the hook path (for example, to add a slash) |
What does the Caddyfile look like?
This config (plain HTTP on :8088 so you can test locally; in production, give the site block your hostname and let Caddy get a certificate) proxies only /hooks/sume and answers 404 elsewhere.
{
auto_https off
}
:8088 {
handle /hooks/sume {
request_body {
max_size 2MB
}
reverse_proxy 127.0.0.1:9099 {
transport http {
response_header_timeout 8s
}
}
}
respond 404
}How do I prove it end to end?
I ran this Caddyfile with caddy validate and caddy run, put a small Python verifier on 127.0.0.1:9099, and sent three requests through :8088 with curl. A body signed with openssl dgst -sha256 -hmac over <timestamp>.<body> returned 200. A wrong signature returned 401 from the app. A 3 MB body was rejected with 413 by Caddy before it reached the app.
That 413 is worth noticing. Sume treats any non-2xx as a failed attempt and retries up to 10 times, so an undersized limit turns every large run receipt into ten failed deliveries. A run whose receipt is over 1 MiB arrives with payload: null and error.code of payload_too_large, which is a small body, so a limit above 1 MiB is for headroom only.
What about the 10-second budget?
Sume waits 10 seconds for each attempt. If your app needs longer, Caddy waiting for it does not help, because Sume has already given up. With response_header_timeout 8s, Caddy answers 502, and your logs show the slow handler instead of leaving you to guess. The cure is to store the event, return 200, and do the work afterward. Dedupe on job_id or on request_id for runs, because every retry carries the same one.
Sources
Related posts
More in Developers
- Clojure: Ring middleware that verifies a Sume webhook from raw bytes
Verify a Sume webhook in Clojure: slurp Ring's one-shot body as bytes, HMAC-SHA256 with javax.crypto, MessageDigest/isEqual, rotation entries, 300 s window.
- Django view for Sume webhooks: verify request.body, then parse
A Django view that verifies a Sume webhook against request.body: csrf_exempt, 300 s replay window, empty-secret refusal, multi-signature header, 204.
- Eleven rows on Sume return MP3 only: use Sonic when you need WAV
Sume's eleven-* TTS Router ids return MP3 only, and three of the five accept just one format. WAV, avatar voices and word timings belong to the sonic ids.
- Eleven v4 seed and voice settings: what each Sume router id accepts
Which of stability, similarity_boost, style, speed and seed work on eleven-v4, v4-turbo, v3, multilingual-v2 and turbo-v2.5 in the Sume TTS Router, with ranges.
Written by Sume