Streaming through Nginx

EventStreamResponse and StreamingResponse accept Django's response headers. Proxy buffering is a deployment choice; it needs no middleware patch and no change to other responses.

from aiodrf.response import EventStreamResponse, StreamingResponse

response = EventStreamResponse(events(), headers={"X-Accel-Buffering": "no"})
export = StreamingResponse(rows(), headers={"X-Accel-Buffering": "no"})

Or configure a location dedicated to streaming:

location /api/events/ {
    proxy_pass http://application;
    proxy_http_version 1.1;
    proxy_buffering off;
}

Do not set proxy_ignore_headers X-Accel-Buffering if the application is to control buffering. An explicit yes enables buffering even where the location turns it off. Nginx consumes this header, so its absence from the client response does not show it was ignored. See the Nginx buffering contract.

Buffering, caching and compression are separate policies. aiodrf adds no default header: SSE keeps its Cache-Control: no-cache, and NDJSON gets none because it streams. Choose compression and proxy read timeouts for the application, heartbeat intervals included. gzip does not keep the first-item latency of an uncompressed response, and proxy_buffering on does not hold every short chunk.

Tested behaviour

Server-Sent Events and NDJSON responses were tested through Nginx with buffering off, buffering on, buffering on with X-Accel-Buffering ignored, and gzip with buffering off and on, each with the application sending no header, no and yes. In every case the decoded payload, content type and cache headers are correct and the producer is closed at the end. Only unbuffered locations guarantee that an item reaches the client before the producer finishes. Compression applies only when the client negotiates it.

These tests use local HTTP/1.1, plus a separate HTTP/2 test with TLS for client disconnects (see deployment behaviour). They say nothing about a production TLS setup, CDN or ingress controller; test your deployed chain separately.