Search code examples
pythonnginxfastapiuvicorn

How to improve Nginx/Uvicorn upload speed in FastAPI application?


I have an Ubuntu server, running a Nginx reverse proxy for a FastAPI application using Uvicorn. The server is an AWS EC2 g4n.xlarge in Virginia. On the frontend I'm using HTMX My personal home upload speed is close to 1GBPs, fiber optics, I'm connected via the ethernet cable.

The application uploads the file first and then processes it. To upload 48mb, 35 mins mp3 file, the application takes 10min or so, it's not acceptable. In fact, given my fast internet speed, anything above 30 seconds or a minute is not acceptable. I already tried chunk upload, didn't make a difference

async with aiofiles.open(video_file_path, "wb") as out_file:
while True:
    content = await file.read(1024 * 1024)  # Read chunks of 1 MB
    if not content:
        break
    await out_file.write(content)

I believe the issue has to do with NGINX, because on my PC for testing, I use Uvicorn directly without NGINX and the upload is instant. My upload speed is fast and EC2 upload speed is fast, so the only one left to blame is Nginx, I think

nginx config

server {
    server_name example.com;

    # Increase client max body size
    client_max_body_size 6000M;  # Allow 6GB uploads

    # Adjust timeouts
    client_body_timeout 7200s;
    client_header_timeout 7200s;

    location / {
        proxy_pass http://127.0.0.1:8001; # Proxy pass to the FastAPI port
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Necessary for WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Proxy timeouts
        proxy_read_timeout 7200s;
        proxy_connect_timeout 7200s;
        proxy_send_timeout 7200s;
    }

Solution

  • I fixed the issue by adding those two lines to nginx sites-enabled website .conf file:

    Header set Accept-Ranges bytes;  # inside the server block
    proxy_force_ranges on;  # inside the location block.
    

    I wasn't trying to fix the upload issue, I was trying to fix videojs currentTime issue on Chrome only.

    As per HTTP range requests documentation:

    An HTTP Range request asks the server to send only a portion of an HTTP message back to a client. Range requests are useful for clients like media players that support random access, data tools that know they need only part of a large file, and download managers that let the user pause and resume the download.

    The documentation of proxy_force_ranges can be found here

    Enables byte-range support for both cached and uncached responses from the proxied server regardless of the “Accept-Ranges” field in these responses.