From acda75510c9b8097b7707a51f686dcc8a44af672 Mon Sep 17 00:00:00 2001 From: Agustin Torres Date: Thu, 18 Jun 2026 16:03:08 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Set=20anti-buffering=20headers?= =?UTF-8?q?=20on=20JSONL=20streaming=20responses?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Server-Sent Events responses set `Cache-Control: no-cache` and `X-Accel-Buffering: no` so proxies (e.g. Nginx) deliver events incrementally instead of buffering the whole response. JSONL streaming responses are incremental in the same way, but were missing these headers, so a buffering proxy would hold back the lines and defeat the streaming. Set the same headers on the JSONL response for consistency with SSE. --- fastapi/routing.py | 3 +++ .../test_tutorial001.py | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/fastapi/routing.py b/fastapi/routing.py index 4a55fda8a..563531d91 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -658,6 +658,9 @@ def get_request_handler( media_type="application/jsonl", background=solved_result.background_tasks, ) + response.headers["Cache-Control"] = "no-cache" + # For Nginx proxies to not buffer the streamed response + response.headers["X-Accel-Buffering"] = "no" response.headers.raw.extend(solved_result.response.headers.raw) elif dependant.is_async_gen_callable or dependant.is_gen_callable: # Raw streaming with explicit response_class (e.g. StreamingResponse) diff --git a/tests/test_tutorial/test_stream_json_lines/test_tutorial001.py b/tests/test_tutorial/test_stream_json_lines/test_tutorial001.py index 0b5f9d95b..783f74a85 100644 --- a/tests/test_tutorial/test_stream_json_lines/test_tutorial001.py +++ b/tests/test_tutorial/test_stream_json_lines/test_tutorial001.py @@ -43,6 +43,25 @@ def test_stream_items(client: TestClient, path: str): assert lines == expected_items +@pytest.mark.parametrize( + "path", + [ + "/items/stream", + "/items/stream-no-async", + "/items/stream-no-annotation", + "/items/stream-no-async-no-annotation", + ], +) +def test_stream_sets_streaming_headers(client: TestClient, path: str): + """JSONL streaming responses should disable proxy buffering and caching, + so intermediaries deliver items incrementally (matching SSE responses).""" + response = client.get(path) + assert response.status_code == 200, response.text + # Tells proxies (e.g. Nginx) not to buffer the streamed response + assert response.headers["x-accel-buffering"] == "no" + assert response.headers["cache-control"] == "no-cache" + + def test_openapi_schema(client: TestClient): response = client.get("/openapi.json") assert response.status_code == 200, response.text