The challenge looked ordinary until the proxy and origin each chose a different request boundary. That is the whole trick: two parsers, one TCP connection, and one disagreement.

A small request-path diagram

I started with the smallest possible probe and changed one header at a time. Keeping the payload readable made it much easier to see which server consumed which bytes.

POST / HTTP/1.1
Host: challenge.local
Content-Length: 4
Transfer-Encoding: chunked

0

G

The lesson was less glamorous than the bug: always map the boundary decisions before reaching for a complicated payload.