ローカルトンネルにおけるSSEバッファーバルーンの解消:ゼロレイテンシーストリーミング保証
リバースプロキシでのSSEバッファーバルーンを排除しましょう。TCP nodelayの設定やHTTPバッファリング無効化によるゼロレイテンシーのローカルLLMトークンストリーミング方法を解説します。

Quick answer
SSEバッファーバルーン解消:ローカルLLMのゼロレイテンシーストリーミング: webhook testing answer
For local webhook testing, run your app locally, expose it with a public HTTPS tunnel, and paste the stable callback URL into the provider dashboard.
How do I test webhooks on localhost?
Start your local server, open a public HTTPS tunnel to that port, configure the provider webhook URL, and inspect events in your local logs.
Why does a stable webhook URL matter?
Stable URLs prevent provider dashboards from needing manual callback updates every time you restart a tunnel.
あなたは最新のローカルLLMをvLLM、Ollama、またはllama.cppを使って展開したばかりです。localhost上でテストすると、トークン生成はまるで魔法のようにスムーズで連続的なテキストストリームとなり、即座に反応しているように感じられます。しかし、このエンドポイントをリバースプロキシ(例:Nginx)やローカルトンネル(例:Cloudflare Tunnelsやngrok)経由で外部に公開すると、その魔法は消え失せます。
スムーズな流れの代わりに、数秒間何も受信できず、その後に大量のテキストブロックが一気に流れ出します。
リアルタイムの会話型AIインターフェースを構築している場合、この”ちょこちょこ”としたトークン出力はユーザーエクスペリエンス(UX)を損ねます。Time to First Token (TTFT)の指標も悪化し、GPUの推論速度に関係なくアプリケーションの応答性が遅く感じられることもあります。
原因は?SSE Buffer-Bloatです。
このガイドでは、標準的なネットワーク層がどのようにしてServer-Sent Events(SSE)やHTTPのチャンク転送エンコーディングを妨害しているのかを解説します。さらに、プロキシのバッファリングを完全に無効化し、TCPフラグを調整し、ローカルAIスタックの遅延ゼロのチャンク配信を保証するための設定ガイドを提供します。
根本原因:なぜプロキシはLLMのストリーミングを妨げるのか
修正方法を理解するには、失敗モードを理解する必要があります。LLMのストリーミングは標準的にServer-Sent Events (SSE)に依存しています。SSE接続では、サーバーが単一のHTTP接続を開いたままにし、生成されたトークンをTransfer-Encoding: chunkedを使って即座に送信します。
しかし、従来のWebインフラはこれに最適化されていません。プロキシやロードバランサー、トンネルは、過去には高スループットや静的、または完全にレンダリングされた動的ペイロード向けに最適化されてきました。帯域幅やCPUサイクルを節約するためにバッファリングを採用しています。
1. リバースプロキシのバッファリング
リバースプロキシ(例:Nginx)があなたのLLMとクライアントの間に位置している場合、レスポンスをバッファリングするのがデフォルトです。Nginxはアップストリームサーバーから一定量(例:4KBや8KB)のデータを蓄積してからクライアントに送信します。あなたのLLMが毎秒15トークンを生成している場合、そのバッファを満たすのに数秒かかることもあります。結果として、ユーザーは画面が固まったまま、突然大量の文章が現れることになります。
2. Nagleのアルゴリズム(トランスポート層)
TCP/IPレベルでは、Nagleのアルゴリズムが小さなパケットを遅延させてまとめ、大きなパケットにしてネットワークの混雑を抑制します。LLMのトークンは非常に小さく(数バイト程度)、このアルゴリズムは積極的にキャッシュし、人工的な遅延を生じさせます。
3. トンネルプロトコルのミスマッチ
Cloudflare Tunnels(cloudflared)やngrokのようなツールは、HTTP/2やHTTP/3を多重化してトラフィックを流します。HTTP/2の多重化は複数の小さな画像を同時に読み込むのに適していますが、トンネルクライアントの積極的なストリームバッファリングは、SSEに必要な連続的でバッファリングされないフローを妨げることがあります。
これらのバッファを層ごとに排除していきましょう。
レイヤー1:トランスポート&アプリケーションの調整(Nagleのアルゴリズム無効化)
まず、プロキシの修正前に、アプリケーションがボトルネックになっていないか確認します。Python/FastAPIを使ったカスタム推論サーバーを構築している場合は、TCP_NODELAYフラグを使ってNagleのアルゴリズムを無効にする必要があります。
TCP_NODELAYを設定すると、TCPスタックはパケットのサイズに関係なく即座にデータを送信します。
Python / FastAPI (Uvicorn)
Uvicornを使っている場合、ソケットレベルでこれを強制できます。さらに、アプリケーション層のジェネレーターが内部キャッシュなしでデータを生成していることも確認してください。
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import asyncio
app = FastAPI()
async def token_generator():
tokens = ["Hello", " world", ",", " this", " is", " streaming", " live!"]
for token in tokens:
# 重要:適切なSSEペイロードとしてフォーマット
yield f"data: {token}\n\n"
await asyncio.sleep(0.1) # 推論時間のシミュレーション
@app.get("/stream")
async def stream_llm():
return StreamingResponse(
token_generator(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no" # Nginxにバッファリング停止を指示
}
)
注意: X-Accel-Buffering: no ヘッダーは大きなチートコードです。多くのプロキシ(特にNginx)はこのヘッダーを尊重し、そのレスポンスのバッファリングを自動的に無効にします。
レイヤー2:リバースプロキシの設定
OllamaやvLLMをTLS終端やAPIキー認証のためにリバースプロキシの背後に置く場合、ストリーミング用に明示的に設定する必要があります。
Nginx
NginxはSSEバッファーバルーンの最も一般的な原因です。デフォルトではproxy_bufferingがonになっているため、これを無効にします。さらに、LLM生成には数分かかることもあるため、タイムアウトも延長してください。
server {
listen 443 ssl;
server_name api.yourdomain.com;
location /v1/chat/completions {
proxy_pass http://localhost:8000; # vLLMまたはOllamaのバックエンド
# 1. バッファリング無効化
proxy_buffering off;
proxy_cache off;
# 2. HTTP/1.1はWebSocket/SSEのために必須
proxy_http_version 1.1;
proxy_set_header Connection '';
# 3. チャンク転送エンコーディングの干渉を防ぐ
chunked_transfer_encoding on;
# 4. 長時間のプロンプトに対するタイムアウト延長
proxy_read_timeout 600s;
proxy_connect_timeout 600s;
proxy_send_timeout 600s;
# 標準ヘッダー
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Caddyサーバー
Caddyは一般的に最新のWeb標準に対応していますが、ゼロレイテンシーAIストリーミングのためには、チャンクを即座にプッシュするためにフラッシュ間隔を明示的に設定したいです。
api.yourdomain.com {
reverse_proxy localhost:8000 {
header_up Host {host}
header_up X-Real-IP {remote}
# すぐにレスポンスをフラッシュし、内部バッファリングを無効化
flush_interval -1
}
}
Traefik
TraefikをDockerやKubernetes環境で使っている場合、バッファリングはラベルやミドルウェアで無効化できます。
# docker-compose.yml例
labels:
- "traefik.http.middlewares.unbuffer.buffering.maxRequestBodyBytes=0"
- "traefik.http.middlewares.unbuffer.buffering.memRequestBodyBytes=0"
- "traefik.http.middlewares.unbuffer.buffering.maxResponseBodyBytes=0"
- "traefik.http.middlewares.unbuffer.buffering.memResponseBodyBytes=0"
レイヤー3:ローカルトンネル(Cloudflare & Ngrok)を通じて
AIエンジニアはしばしば公開ポートを公開したくなく、ポートフォワーディングも避けてローカルトンネルを使います。これらは独自の積極的なバッファリングメカニズムを持っています。
Cloudflare Tunnels (cloudflared)
Cloudflareはエッジに位置し、WAFルールやキャッシュ、圧縮のためにレスポンスをバッファリングします。SSEをCloudflareトンネル経由でパイプする場合、次の2つの重要なルールを守る必要があります:
- 厳格なContent-Type: Cloudflareは
Content-Typeヘッダーが正確にtext/event-streamでないとレスポンスをバッファリングします。ストリーミング中にapplication/jsonやプレーンテキストタイプを返すと、接続が閉じるまでペイロードが遅延します。 - ダッシュボードでレスポンスバッファリングを無効化: 遅延が続く場合、Cloudflareダッシュボードのページルールや設定でバッファリングを無効にできます。
api.yourdomain.com/*にルールを作成し、Cache LevelをBypassに設定し、Response Bufferingを無効にします。
トラブルシューティング: 高セキュリティの企業環境(Cloudflare Zero TrustやZscaler利用時)では、HTTP/2の双方向ストリーミングがセキュリティ層でインターセプトされ、バッファリングされることがあります。最新のツール(例:Cursor AIのネットワークプロトコル)は、HTTP/2ストリームバッファリングを検知するとHTTP/1.1 SSEにフォールバックします。cloudflaredを使っている場合、オリジンサーバーでHTTP/1.1を強制することで、積極的なレイヤー7ファイアウォールのバッファリングを回避できることがあります。
Ngrok
Ngrokは基本的にSSEに対して良好に動作しますが、エッジを使う場合は圧縮を無効にしてください。GzipやBrotli圧縮は、圧縮アルゴリズムが動作する前に一定量のデータをバッファリングする必要があるためです。
SSEをトンネル経由で渡す際は、アプリケーションのヘッダーで圧縮を明示的に無効化します:Accept-Encoding: identity(クライアント側)またはContent-Encoding: identity(サーバー側)。
レイヤー4:HTTP/2とHTTP/3の考慮点
WebはHTTP/2やHTTP/3(QUIC)へと進化していますが、ストリーミングは複雑になっています。
HTTP/2は単一のTCP接続を使い、複数のストリームを多重化します。これにより静的資産のヘッド・オブ・ライン・ブロッキング問題は解決しますが、長時間遅延のある遅いストリーム(SSE)ではフロー制御ウィンドウが意図せず制限やバッファリングを引き起こすことがあります。
ホームネットワークから高遅延のネットワーク(例:家庭のPCから5Gのスマホへ)へローカルLLMをリバースプロキシする場合、HTTP/3は有利です。QUICはUDPベースであり、トークンを含むパケットがドロップしても、次のトークンの配信を妨げません(TCPのようにストリームを停止し再送信しない)。
ただし、多くのAI推論サーバー(例:vLLMのFastAPIサーバー)はHTTP/3をネイティブにサポートしていません。
2026年の最良のスタック推奨:
1. vLLM/OllamaをローカルでHTTP/1.1で動かす
2. 同じマシン上のNginxやEnvoyを使いTLS終端し、proxy_buffering offを設定、インターネットにはHTTP/3(QUIC)エッジを公開
3. クライアント(React/Next.jsフロントエンド)は、最新のfetchストリーム対応のSSEパーサを使う
ゼロレイテンシーAIストリームのためのチェックリスト
トークンがチャンクで到着している場合、以下を確認してください:
- [ ] アプリ層: すぐにデータをyieldしているか?(大きな配列の追加前にyieldしているか?)
- [ ] ヘッダー: サーバーが
Content-Type: text/event-streamを返しているか? - [ ] ヘッダー:
X-Accel-Buffering: noを渡してNginxのデフォルトバッファリングを自動的に無効化しているか? - [ ] プロキシ: Nginxのlocationブロックに
proxy_buffering off;が設定されているか? - [ ] 圧縮: Gzip/BrotliがSSEルートで無効になっているか?
- [ ] トンネル: Cloudflareを使っている場合、ルーティングルールでレスポンスバッファリングが無効になっているか?
ネットワークスタックからバッファを排除することで、ローカルLLMの魔法を取り戻せます。ユーザーやフロントエンドアプリは、真のリアルタイムトークン配信を享受し、人間の会話速度に近い低遅延のインタラクションを実現します。
Related InstaTunnel pages
Continue from this article into the most relevant product guides and workflows.
Related Topics
Keep building with InstaTunnel
Read the docs for implementation details or compare plans before you ship.