From 44f9a4f7f95ecdb4cd9929299956a160c94a35e8 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Wed, 9 Sep 2026 22:35:48 +0200 Subject: [PATCH] fix: stop forwarding upstream Server and Date headers from the OpenAI and Ollama proxies (#29843) Streamed chat completions and the other proxied OpenAI and Ollama responses went out with two Server and two Date headers: the upstream's copies, forwarded verbatim, plus uvicorn's own. nginx in front of Open WebUI logs "upstream sent duplicate header line" for both on every streamed request. Server and Date belong to whoever terminates the connection, so both proxies now drop the upstream's copies next to the encoding headers they already stripped. The filter also compares header names case-insensitively. Before, it matched title-case names only, so uvicorn-based upstreams such as vLLM and LiteLLM, which send lowercase header names, had none of their headers stripped at all, including the Content-Encoding entry the filter exists for. Same fix as #29824 for the terminal proxy, applied to the other two proxy paths. --- backend/open_webui/routers/ollama.py | 7 ++++--- backend/open_webui/routers/openai.py | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/backend/open_webui/routers/ollama.py b/backend/open_webui/routers/ollama.py index 23a43aaaa9..b283db9ae8 100644 --- a/backend/open_webui/routers/ollama.py +++ b/backend/open_webui/routers/ollama.py @@ -55,14 +55,15 @@ log = logging.getLogger(__name__) # response body. Forwarding them verbatim causes desktop / programmatic # clients to attempt decompression of an already-decoded payload, resulting # in ZlibError. See https://github.com/aio-libs/aiohttp/issues/4462. -_STRIP_PROXY_HEADERS = frozenset({'Content-Encoding', 'Content-Length', 'Transfer-Encoding'}) +# Also drop server and date: uvicorn adds its own and forwarding both duplicates them. +_STRIP_PROXY_HEADERS = frozenset({'content-encoding', 'content-length', 'transfer-encoding', 'server', 'date'}) _MODEL_LIST_TIMEOUT = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST) BASE_MODELS_CACHE_KEY = f'{REDIS_KEY_PREFIX}:models:base' def _clean_proxy_headers(raw_headers) -> dict: - """Return a copy of *raw_headers* with stale encoding headers removed.""" - return {k: v for k, v in raw_headers.items() if k not in _STRIP_PROXY_HEADERS} + """Return a copy of *raw_headers* without the encoding, server and date headers.""" + return {k: v for k, v in raw_headers.items() if k.lower() not in _STRIP_PROXY_HEADERS} async def send_get_request( diff --git a/backend/open_webui/routers/openai.py b/backend/open_webui/routers/openai.py index c62b2f7194..8c8f96183e 100644 --- a/backend/open_webui/routers/openai.py +++ b/backend/open_webui/routers/openai.py @@ -74,15 +74,16 @@ log = logging.getLogger(__name__) # response body. Forwarding them verbatim causes desktop / programmatic # clients to attempt decompression of an already-decoded payload, resulting # in ZlibError. See https://github.com/aio-libs/aiohttp/issues/4462. -_STRIP_PROXY_HEADERS = frozenset({'Content-Encoding', 'Content-Length', 'Transfer-Encoding'}) +# Also drop server and date: uvicorn adds its own and forwarding both duplicates them. +_STRIP_PROXY_HEADERS = frozenset({'content-encoding', 'content-length', 'transfer-encoding', 'server', 'date'}) _MODEL_LIST_TIMEOUT = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST) _UNSUPPORTED_OPENAI_MODEL_KEYWORDS = ('babbage', 'dall-e', 'davinci', 'embedding', 'tts', 'whisper') BASE_MODELS_CACHE_KEY = f'{REDIS_KEY_PREFIX}:models:base' def _clean_proxy_headers(raw_headers) -> dict: - """Return a copy of *raw_headers* with stale encoding headers removed.""" - return {k: v for k, v in raw_headers.items() if k not in _STRIP_PROXY_HEADERS} + """Return a copy of *raw_headers* without the encoding, server and date headers.""" + return {k: v for k, v in raw_headers.items() if k.lower() not in _STRIP_PROXY_HEADERS} async def send_get_request(