BaseHTTPMiddleware as Anti-Pattern
2026 Apr 30BaseHTTPMiddleware as Anti-Pattern
This week I was working on something related to BaseHTTPMiddleware where, due to StreamingResponse and specific production settings, it can transform your async REST API into a completely synchronous one.
The issue arises because BaseHTTPMiddleware can cause hanging when used with StreamingResponse. As mentioned in some discussions, the reading of the response body can get exhausted in the first read, and when it comes to the second read, it waits indefinitely, leading to a blocked application. In ASGI applications, messages are sent between the client and the app using types like http.response.start and http.response.body.
To avoid this, you can use pure ASGI middleware instead of BaseHTTPMiddleware:
from starlette.types import ASGIApp, Receive, Send, Message, Scope
class LogResponseMDW:
def __init__(self, app: ASGIApp) -> None:
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send):
if scope["type"] != "http":
await self.app(scope, receive, send)
return
async def send_wrapper(message: Message):
# This will capture response coming from APP Layer
# You will want to check for message["type"] first
# response body will be in message where the type is
# "http.response.body"
if message["type"] == "http.response.body":
print(f"message: {message}") # log here
await send(message)
await self.app(scope, receive, send_wrapper)
# You can add this to your app this way:
# app.add_middleware(LogResponseMDW)
For more context, see this StackOverflow discussion.