An image works on a fast direct connection but is incomplete through a slower forward proxy. One possible cause is the origin closing the connection while its response is still being written.

Netty I/O is asynchronous. Returning from writeAndFlush does not mean that operation has completed. This ordering is therefore unsafe for a response that still has queued bytes:

ctx.writeAndFlush(response);
ctx.close();

When the HTTP exchange is intentionally closing after its final response message, attach closure to that write's future:

ctx.writeAndFlush(finalResponseMessage)
    .addListener(ChannelFutureListener.CLOSE);

The fragment assumes the response has already been constructed and framed correctly. For a streamed body, use the completion of its final message rather than the write of its headers. For a persistent HTTP connection, do not add a close merely because a response finished.

Inspect the future's failure as part of the application's normal error handling. Completion includes failure and cancellation, so completion alone is not a delivery receipt from the browser.

The useful comparison is the complete response length and write outcome on each path. A proxy can change timing enough to reveal an existing lifecycle error; its presence does not establish that its compressor or cache discarded the bytes.

Allakkat attuumassuteqartut