uvicorn review
Uvicorn 0.52.4 is an ASGI server that turns HTTP/1.1 and WebSocket connections into events for applications built with FastAPI, Starlette, async Django, or another ASGI framework. It handles lifespan startup and shutdown, process workers, development reloads, proxy headers, sockets, TLS, access logs, and connection limits. The base package uses Python's asyncio and h11; the standard extra can add uvloop, httptools, watchfiles, WebSocket support, dotenv loading, and YAML logging config. Version 0.52.4 fixes duplicate Date headers on accepted WebSocket handshakes using websockets-sansio. The new zttp HTTP parser remains explicitly experimental and should stay out of production traffic.
Uvicorn 0.52.4 installed in 0.4 seconds, occupied 2 MB across three packages, and returned zero audit findings in our sandbox, so it is a small default server for HTTP/1.1 ASGI applications. Choose another server for application-level HTTP/2 or Trio, and do not treat proxy trust or worker resource limits as framework defaults.
We installed it
| Install | ✓ · 0.4s | 3 packages on disk · 2 MB |
| Import | ✓ | import uvicorn in 0.46s · pure Python · py.typed · requires Python >=3.10 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does uvicorn install cleanly?
Yes. In a fresh container with an empty cache, pip install uvicorn finished in 0.4s, leaving 3 packages and 2 MB on disk. pip-audit reported no known vulnerabilities.
What does uvicorn need to run?
Python >=3.10, and nothing compiled: it is pure Python. In our run import uvicorn succeeded in 0.46s, and the package ships py.typed for type checkers.
uvicorn or hypercorn: which should you use?
hypercorn: Use it when HTTP/2, HTTP/3, or Trio support belongs in the application server. Uvicorn 0.52.4 installed in 0.4 seconds, occupied 2 MB across three packages, and returned zero audit findings in our sandbox, so it is a small default server for HTTP/1.1 ASGI applications.
When should you not use uvicorn?
HTTP/2 or HTTP/3 is required at the application server; Uvicorn's documented protocol support is HTTP/1.1 and WebSockets
Discussed on
Use it if
- A FastAPI, Starlette, or other ASGI application needs a direct HTTP/1.1 and WebSocket server
- A container should run one bounded server process with lifespan hooks and graceful termination
- A reverse proxy will forward to a loopback port or Unix socket with a known trust boundary
- Development needs file reload while production needs separate worker processes
- HTTP/2 or HTTP/3 is required at the application server; Uvicorn's documented protocol support is HTTP/1.1 and WebSockets
- The deployment needs Trio instead of asyncio, which Hypercorn supports directly
- The platform invokes functions per request and does not allow a listening server process; an ASGI adapter such as Mangum fits AWS Lambda
- Operators cannot identify which proxy addresses may set forwarding headers, because trusting every source permits client IP and scheme spoofing
- The application performs blocking CPU or database work on the event loop and has no plan to move that work to threads, processes, or an async driver
Setup reality
We installed Uvicorn 0.52.4 in a clean Python 3.12 container in 0.4 seconds. Three packages used 2 MB on disk, and pip-audit found zero known vulnerabilities. The measured metadata counted 9 direct dependencies, required Python 3.10 or newer, described a pure-Python package, included py.typed, and supplied no usable license value. import uvicorn completed in 0.46 seconds. The 3-package base install uses h11 and does not include all components from uvicorn[standard].
No credentials or config file are required. --env-file loads variables for the ASGI application; it does not read UVICORN_* server settings from that file. Pass server options on the CLI, to uvicorn.run(), or through the actual process environment. Reload mode and worker mode are mutually exclusive. If code enables either one, keep uvicorn.run() under the if __name__ == "__main__" guard and pass the application as an import string.
A reverse proxy changes Uvicorn's view of the client address and URL scheme. --proxy-headers only accepts forwarded values from --forwarded-allow-ips, whose documented default is loopback. Set the proxy IP, network, or Unix socket path explicitly; * trusts every peer. Configure --root-path when the proxy publishes the application below a prefix. Four workers also mean four application startups, four memory spaces, and four sets of database pools or in-process caches.
--limit-concurrency sends an immediate 503 after the limit is reached; it does not put excess requests in the socket backlog. Idle keep-alive connections count toward that limit. Set request recycling and graceful-shutdown timeouts around the application's resource behavior, then test SIGTERM with live requests. Version 0.52.4 contains several recent WebSocket handshake and close-path fixes. Keep --http zttp in experiments because the 0.52 release notes tell operators not to send it production traffic yet.
Patterns
Reload a local application on file changes run-development
uvicorn main:app --host 127.0.0.1 --port 8000 --reload`--reload` and `--workers` are mutually exclusive. Without watchfiles, reload checks Python file modification times and cannot use the full include and exclude behavior.
Start multiple production workers run-workers
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4Four workers are four processes with separate startup hooks, caches, and connection pools. Calculate downstream limits per process.
Start from Python without recursive workers run-programmatically
import uvicorn
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
workers=2,
)Reload or workers require the main guard and an import string. Passing an application object only works without multiprocessing and reload.
Call a factory to create the ASGI app load-app-factory
# main.py
def create_app():
from fastapi import FastAPI
app = FastAPI()
return app
# shell
uvicorn main:create_app --factory`--factory` expects a zero-argument callable that returns an ASGI application. Each worker invokes the factory in its own process.
Use the external Gunicorn worker package run-with-gunicorn
pip install gunicorn uvicorn-worker
gunicorn main:app \
--workers 4 \
--worker-class uvicorn_worker.UvicornWorker \
--bind 0.0.0.0:8000Uvicorn's bundled `uvicorn.workers` module is deprecated. The separately installed `uvicorn-worker` package provides `uvicorn_worker.UvicornWorker`.
Accept forwarded headers from a known proxy trust-reverse-proxy
uvicorn main:app \
--host 127.0.0.1 \
--port 8000 \
--proxy-headers \
--forwarded-allow-ips=10.20.0.0/16 \
--root-path=/apiThe literal `*` trusts forwarding headers from every peer. Supply the proxy network or socket path so clients cannot choose their reported IP or scheme.
Serve a reverse proxy over a Unix socket bind-unix-socket
uvicorn main:app --uds /run/example/uvicorn.sock
# nginx location block:
# proxy_pass http://unix:/run/example/uvicorn.sock;The proxy user needs access to the socket and its parent directory. Remove stale socket files through the service lifecycle if startup cannot replace them.
Refuse excess work at a known limit cap-concurrency
uvicorn main:app \
--limit-concurrency 200 \
--backlog 1024 \
--timeout-keep-alive 5`--limit-concurrency` returns 503 immediately when reached. The backlog is the OS accept queue and does not hold requests rejected by the application-level limit.
Bound request count and shutdown time recycle-workers
uvicorn main:app \
--workers 4 \
--limit-max-requests 10000 \
--limit-max-requests-jitter 1000 \
--timeout-graceful-shutdown 30Jitter staggers worker recycling. After the graceful timeout, Uvicorn begins terminating requests that have not completed.
Load application variables from a file load-environment
# .env.application
DATABASE_URL=postgresql://db/app
FEATURE_X=1
# shell
UVICORN_PORT=9000 uvicorn main:app --env-file .env.application`--env-file` configures the ASGI application. Put `UVICORN_PORT` in the process environment or CLI because Uvicorn does not read its own settings from that file.
Use a logging configuration file configure-logging
uvicorn main:app --log-config logging.yaml
# Or disable access records explicitly:
uvicorn main:app --no-access-log --log-level warningYAML log configuration needs PyYAML, which the standard extra installs. Access records can expose paths and query strings, so define redaction and retention outside Uvicorn defaults.
Pin the current WebSocket implementation select-websocket
pip install 'uvicorn[standard]'
uvicorn main:app --ws websockets-sansioThe automatic WebSocket choice uses websockets-sansio when the `websockets` dependency is installed. Version 0.52.4 fixes its duplicate Date header on accepted handshakes.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| hypercorn | PyPI | Use it when HTTP/2, HTTP/3, or Trio support belongs in the application server. |
| daphne | PyPI | Use it for Django Channels deployments already built around Daphne and its HTTP/2 support. |
| granian | PyPI | Use it when a Rust-based ASGI server and HTTP/2 are worth a separate production benchmark. |
| mangum | PyPI | Use it to adapt an ASGI application to AWS Lambda and API Gateway without opening a listener. |
More web backend guides
urllib3 · requests · ws · anyio · undici · httpx · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

