From 8ace4f0a8a599d0a4cce9c5db1b117701a00b275 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Thu, 23 Jul 2026 18:29:01 +0200 Subject: [PATCH] perf: stop recomputing static work in per-response ASGI middlewares (#27229) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SecurityHeadersMiddleware called set_security_headers() on every response — 14 os.environ.get lookups plus a regex validation per configured header, for values that are static for the process lifetime. Compute the header list once at construction; when no security env vars are set, skip wrapping send entirely. RedirectMiddleware decoded and parse_qs'd the query string of every GET, though it only acts on /watch?v= and ?shared= URLs. Add a cheap path/substring precheck first; a false positive just falls through to the previous full parse, so no redirect behavior changes. Verified byte-identical responses (status, Location, header values) against the previous implementations across redirect, passthrough, and no-env cases. Claude-Session: https://claude.ai/code/session_01MHg5zs1VBjvRWQ54qHpfYD Co-authored-by: Claude --- backend/open_webui/utils/asgi_middleware.py | 10 +++++++++- backend/open_webui/utils/security_headers.py | 7 +++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/backend/open_webui/utils/asgi_middleware.py b/backend/open_webui/utils/asgi_middleware.py index 89154b6103..7189bfcad8 100644 --- a/backend/open_webui/utils/asgi_middleware.py +++ b/backend/open_webui/utils/asgi_middleware.py @@ -227,7 +227,15 @@ class RedirectMiddleware: return path = scope.get('path', '') - query_string = scope.get('query_string', b'').decode('latin-1', errors='replace') + raw_query = scope.get('query_string', b'') + # This middleware only acts on /watch?v= and ?shared= URLs; skip the + # decode + parse_qs work for every other GET. (A false positive on the + # substring check just falls through to the full parse below.) + if not (path.endswith('/watch') or b'shared' in raw_query): + await self.app(scope, receive, send) + return + + query_string = raw_query.decode('latin-1', errors='replace') query_params = parse_qs(query_string) redirect_params: dict[str, str] = {} diff --git a/backend/open_webui/utils/security_headers.py b/backend/open_webui/utils/security_headers.py index 090abeea33..c6cbbce2ba 100644 --- a/backend/open_webui/utils/security_headers.py +++ b/backend/open_webui/utils/security_headers.py @@ -15,16 +15,19 @@ class SecurityHeadersMiddleware: def __init__(self, app: ASGIApp) -> None: self.app = app + # Headers derive only from env vars, which are static for the process + # lifetime — compute them once instead of per response. + self._headers = list(set_security_headers().items()) async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: - if scope['type'] != 'http': + if scope['type'] != 'http' or not self._headers: await self.app(scope, receive, send) return async def send_with_security_headers(message: Message) -> None: if message['type'] == 'http.response.start': headers = MutableHeaders(scope=message) - for key, value in set_security_headers().items(): + for key, value in self._headers: headers[key] = value await send(message)