Search code examples
pythonhttpbackendserver-sidefastapi

What is the maximum size of upload file we can receive in FastAPI?


I am trying to figure out the maximum file size, my client can upload , so that my python fastapi server can handle it without any problem.


Solution

  • Your request doesn't reach the ASGI app directly. It goes through reverse proxy (Nginx, Apache), ASGI server (uvicorn, hypercorn, gunicorn) before handled by an ASGI app.

    Reverse Proxy

    For Nginx, the body size is controlled by client_max_body_size, which defaults to 1MB.

    For Apache, the body size could be controlled by LimitRequestBody, which defaults to 0.

    ASGI Server

    The ASGI servers don't have a limit of the body size. At least it's the case for gunicorn, uvicorn, hypercorn.

    Quote from Hypercorn doc.

    Large request body

    This attack is of the second type and aims to exhaust the server’s memory by inviting it to receive a large request body (and hence write the body to memory). A poorly configured server would have no limit on the request body size and potentially allow a single request to exhaust the server.

    It is up to the framework to guard against this attack. This is to allow the framework to consume the request body if desired.

    Note: Gunicorn doesn't limit the size of request body, but sizes of the request line and request header.

    • --limit-request-line, size limit on each req line, default 4096
    • --limit-request-fields, number of header fields, default 100
    • --limit-request-field_size, size of headef fields, default 8190

    ASGI App/Framework

    Since FastAPI is based upon Starlette. How to reading the body is handled by Starlette. Reading from the source (0.14.3), there seems no limit on request body either.

    class Request(HTTPConnection):
        ...
        async def stream(self) -> typing.AsyncGenerator[bytes, None]:
            if hasattr(self, "_body"):
                yield self._body
                yield b""
                return
    
            if self._stream_consumed:
                raise RuntimeError("Stream consumed")
    
            self._stream_consumed = True
            while True:
                message = await self._receive()
                if message["type"] == "http.request":
                    body = message.get("body", b"")
                    if body:
                        yield body
                    if not message.get("more_body", False):
                        break
                elif message["type"] == "http.disconnect":
                    self._is_disconnected = True
                    raise ClientDisconnect()
            yield b""
    
        async def body(self) -> bytes:
            if not hasattr(self, "_body"):
                chunks = []
                async for chunk in self.stream():
                    chunks.append(chunk)
                self._body = b"".join(chunks)
            return self._body
    

    Conclusion: If you get 413 Payload Too Large error, check the reverse proxy.