Skip to main content

proxy-buffering

The proxy-buffering plugin disables the NGINX proxy_buffering directive for selected routes. This allows incremental responses to reach clients as the upstream sends them instead of waiting in the proxy buffer.

This behavior is useful for server-sent events (SSE), watch APIs, and other upstream services that send incremental or chunked responses.

Examples​

Compare Buffered and Incremental Delivery​

The following example compares NGINX's default response buffering with a route that disables buffering through proxy-buffering. A test upstream declares a fixed response length and sends three lines at two-second intervals. The buffered route delivers all three lines together, while the route with buffering disabled delivers each line as it arrives.

Create the following Python script for the streaming upstream:

stream-server.py
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import time

class Handler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"

def do_GET(self):
chunks = [b"one\n", b"two\n", b"three\n"]

self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.send_header("Content-Length", str(sum(map(len, chunks))))
self.send_header("Connection", "close")
self.end_headers()

for chunk in chunks:
self.wfile.write(chunk)
self.wfile.flush()
time.sleep(2)

self.close_connection = True

def log_message(self, *_):
pass

ThreadingHTTPServer(("0.0.0.0", 8000), Handler).serve_forever()

Start the upstream in the environment used by the gateway:

Set GATEWAY_CONTAINER to the running APISIX or API7 Gateway container. Create a dedicated network and connect the gateway to it:

export GATEWAY_CONTAINER=replace-with-gateway-container-name

docker network create gateway-stream-net
docker network connect gateway-stream-net "$GATEWAY_CONTAINER"

Start the streaming server from the directory containing stream-server.py:

docker run -d \
--name stream-server \
--network gateway-stream-net \
-v "$PWD/stream-server.py:/stream-server.py:ro" \
python:3.13.15-alpine3.24 \
python -u /stream-server.py

Create two routes to the same upstream. The first route uses the default NGINX buffering behavior, and the second disables buffering with proxy-buffering:

Create the baseline route without proxy-buffering:

curl "http://127.0.0.1:9180/apisix/admin/routes/proxy-buffering-on" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/buffered",
"upstream": {
"type": "roundrobin",
"nodes": {
"stream-server:8000": 1
}
}
}'

Create the comparison route with buffering disabled:

curl "http://127.0.0.1:9180/apisix/admin/routes/proxy-buffering-off" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/unbuffered",
"plugins": {
"proxy-buffering": {
"disable_proxy_buffering": true
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"stream-server:8000": 1
}
}
}'

Create a Python client that records when each response line becomes available:

stream-client.py
import time
import urllib.request

for path in ("buffered", "unbuffered"):
start = time.monotonic()
print(path)

with urllib.request.urlopen(f"http://127.0.0.1:9080/{path}") as response:
for line in response:
elapsed = time.monotonic() - start
print(f"{elapsed:.1f}s {line.decode().strip()}")

Run the client:

python stream-client.py

The baseline route should release all three lines together after about four seconds. The route with proxy-buffering should release each line as the upstream writes it:

buffered
4.0s one
4.0s two
4.0s three
unbuffered
0.0s one
2.0s two
4.0s three

This difference confirms that proxy-buffering disables NGINX response buffering for the configured route.