From d0f759ce40ce9788a3448f41327f766df461885d Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Fri, 24 Jul 2026 01:09:40 +0200 Subject: [PATCH] fix: capture uncompressed response bodies in audit logs (#27369) CompressMiddleware was registered before AuditLoggingMiddleware. Starlette prepends on add_middleware, so the audit layer ended up outside compression and, at the REQUEST_RESPONSE level, recorded the zstd/brotli/gzip bytes of every response, decoded with errors='replace'. Any client that sent Accept-Encoding (i.e. every browser) therefore produced audit entries whose response_object was unreadable mojibake. Registering the audit middleware before the compression middleware places it inside compression, so it observes the response body exactly as the route produced it while the client still receives the compressed stream. Verified with a stacked ASGI harness: in the old order the captured body is not parseable; in the new order the captured body round-trips as the original JSON and the client response stays compressed. --- backend/open_webui/main.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index b0107cc178..1ac45f3e70 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -717,6 +717,25 @@ app.state.speech_speaker_embeddings_dataset = None app.state.MODELS = MODELS # Add the middleware to the app +try: + audit_level = AuditLevel(AUDIT_LOG_LEVEL) +except ValueError as e: + logger.error(f'Invalid audit level: {AUDIT_LOG_LEVEL}. Error: {e}') + audit_level = AuditLevel.NONE + +# Added before CompressMiddleware so audit sits inside compression and +# captures response bodies before they are compressed (last added runs +# outermost). +if audit_level != AuditLevel.NONE: + app.add_middleware( + AuditLoggingMiddleware, + audit_level=audit_level, + excluded_paths=AUDIT_EXCLUDED_PATHS, + included_paths=AUDIT_INCLUDED_PATHS, + audit_get_requests=ENABLE_AUDIT_GET_REQUESTS, + max_body_size=MAX_BODY_LOG_SIZE, + ) + if ENABLE_COMPRESSION_MIDDLEWARE: app.add_middleware(CompressMiddleware) @@ -795,21 +814,6 @@ if ENABLE_SCIM: app.include_router(scim.router, prefix='/api/v1/scim/v2', tags=['scim']) -try: - audit_level = AuditLevel(AUDIT_LOG_LEVEL) -except ValueError as e: - logger.error(f'Invalid audit level: {AUDIT_LOG_LEVEL}. Error: {e}') - audit_level = AuditLevel.NONE - -if audit_level != AuditLevel.NONE: - app.add_middleware( - AuditLoggingMiddleware, - audit_level=audit_level, - excluded_paths=AUDIT_EXCLUDED_PATHS, - included_paths=AUDIT_INCLUDED_PATHS, - audit_get_requests=ENABLE_AUDIT_GET_REQUESTS, - max_body_size=MAX_BODY_LOG_SIZE, - ) ################################## # # Chat Endpoints