Uvicorn(ユーヴィコーン)は、FastAPIやStarletteなどの非同期Webアプリを動かすPython製のASGIサーバーです。アプリ側が書いたルーティングや処理を、ポートを開いてHTTPリクエストを受け取るサーバー側の仕事と切り離しているのがASGIで、Uvicornはその「サーバー側」を担います。2026年10月16日時点の最新版は0.54.0(PyPIでの公開は2026年9月25日、Python 3.10以上)です。
以下の挙動と数値は、手元のmacOSで0.54.0を実際に動かして確かめたものです。
まとめ:Uvicornの役割と本番構成の要点
- 役割:FastAPIはアプリ本体、UvicornはそれをHTTPで公開するサーバーです。FastAPIの
fastapi runも内部でUvicornを使います。 - インストール:本番は
pip install "uvicorn[standard]"を選びます。手元の計測では、最小構成に比べて処理数が約2.5倍になりました。 - Gunicornとの関係:UvicornのGunicorn用ワーカー
uvicorn.workersは0.30.0で非推奨になり、移行先は別パッケージのuvicorn-workerです。Gunicorn 25.1.0からは、Gunicorn単体でもASGIアプリを動かせます。 - タイムアウト:Uvicornにはリクエスト処理時間の上限がありません。イベントループが止まったワーカーを強制終了させたいなら、Gunicornの
--timeoutを使います。 - Nginx配下:UNIXソケットでつなぐ場合、既定の
--forwarded-allow-ipsではX-Forwarded-*が無視されます。--forwarded-allow-ips='*'を指定し、Nginx側でX-Forwarded-Forを$remote_addrに上書きします(追記のままだと送信元IPを偽装できます)。
Uvicornとは:FastAPIを動かすASGIサーバー
Uvicornは、PythonのWebサーバーとアプリをつなぐ規格ASGI(Asynchronous Server Gateway Interface)を実装したサーバーです。作者はDjango REST frameworkやStarletteで知られるTom Christieで、リポジトリはKludex/uvicornに移っています。対応するのはHTTP/1.1とWebSocketで、HTTP/2は0.53.0(2026年9月14日)で実験的なオプトインとして加わったばかりです(pip install "zttp>=0.0.34"で依存を追加し、--http zttp --http2で有効化。リリースノートは本番投入を勧めていません)。
Uvicornの読み方と名前
英語の発音は「ユーヴィコーン」に近く、日本語の記事では「ユビコーン」と表記されることが多くあります。どちらでも通じます。名前の由来は、公式のREADMEとドキュメントのトップには書かれていません。uvloopの「uv」とGunicornの「corn」を合わせたという説明も見かけますが、作者自身によるものは確認できませんでした。なおGunicornは「Green Unicorn」の略で、RubyのアプリケーションサーバーUnicornを移植したものとREADMEに書かれています。
ASGIとWSGIの違い
WSGIのアプリは「リクエストを受け取ってレスポンスを返す、1つの同期関数」です。ASGIのアプリはscope・receive・sendの3つを受け取る非同期関数で、1本の接続の中で何度もイベントを送受信できます(ASGI仕様の現行版は3.0)。この違いのおかげで、WebSocketや、起動・終了時の処理を通知するlifespanをサーバーとアプリの間でやり取りできます。
フレームワークを使わない最小のASGIアプリは次のとおりです。このファイルをuvicorn rawapp:appで起動すると、FastAPIなしでもレスポンスが返ります。
# rawapp.py
async def app(scope, receive, send):
if scope["type"] != "http":
return # lifespan等は省略
await send({"type": "http.response.start", "status": 200,
"headers": [(b"content-type", b"text/plain")]})
await send({"type": "http.response.body", "body": b"hello"})
非同期で処理するといっても、async defの中でtime.sleep()や同期のDBドライバを呼ぶと、そのワーカーの全リクエストが止まります。async/defの使い分けはFastAPIとは|最短で動かす手順とasync/defの使い分け【2026年版】、イベントループの扱いはanyio・Trio・asyncioの違いと使い分けで解説しています。
FastAPI・Starlette・Uvicornの役割分担
| 層 | 担当 | やること |
|---|---|---|
| アプリ | FastAPI | 型による検証・OpenAPI生成・依存性注入 |
| ASGIフレームワーク | Starlette | ルーティング・リクエスト/レスポンス |
| ASGIサーバー | Uvicorn | ポート待受・HTTP解析・プロセス管理 |
| リバースプロキシ | Nginx等 | TLS終端・静的ファイル・遅いクライアントの吸収 |
「UvicornとFastAPIの違い」はこの層の違いです。FastAPIだけではポートを開けないので、何らかのASGIサーバーが必要になります。サーバーはUvicornに限らず、Hypercorn・Granian・GunicornのASGIワーカーにも差し替えられます。
Uvicornのインストール:uvicornとuvicorn[standard]の違い
pip install uvicornで入るのは、本体とclick・h11が基本で、Python 3.10ではtyping_extensionsも依存関係に含まれます。pip install "uvicorn[standard]"にすると、次のパッケージが追加されます(0.54.0のpyproject.tomlの定義)。
| 追加パッケージ | 役割 | 入らないと |
|---|---|---|
| httptools | C実装のHTTPパーサー | 純Pythonのh11を使う |
| uvloop | libuvベースのイベントループ | 標準asyncioを使う |
| websockets | WebSocket実装 | 代替のwsprotoもなければ利用不可 |
| watchfiles | --reloadの変更監視 |
ポーリングで監視 |
| python-dotenv | --env-fileの読込 |
--env-fileが使えない |
| PyYAML | YAMLのログ設定 | --log-configはJSON等のみ |
uvloopはWindows・Cygwin・PyPyでは入らないため、Windowsでは上の表からuvloopを除いた5つが入ります。Windows向けに追加されていたcoloramaは、0.51.0でstandardから外れました。
standard版の性能比較:HTTPパーサーとイベントループ
手元のMac(Intel Core i9-9880H・macOS 26.6.2・Python 3.13.14)で、PIDとイベントループ名を短いJSONで返すだけの素のASGIアプリを1ワーカーで起動し、ab -n 20000 -c 50を3回流した中央値です(いずれも失敗0件)。アクセスログは切っています。アプリはほぼ何もしないため、絶対値ではなく比率だけを見てください。
| 構成 | HTTPパーサー | ループ | req/秒 |
|---|---|---|---|
| uvicorn(最小) | h11 | asyncio | 1,411 |
uvicorn[standard] --http h11 |
h11 | uvloop | 1,536 |
uvicorn[standard] --loop asyncio |
httptools | asyncio | 2,978 |
| uvicorn[standard](自動選択) | httptools | uvloop | 3,603 |
パーサーをh11のままuvloopだけに替えた構成は最小構成とほぼ同じで、asyncioのままhttptoolsに替えた構成で約2.1倍、両方をそろえた自動選択で約2.5倍になりました。standard版は「uvloopで速くなる」と説明されがちですが、今回の計測では、uvloopの効果はhttptoolsと組み合わせたときに初めて表れました。起動ログには選ばれたループが出ないため、確かめるならアプリ内でtype(asyncio.get_running_loop())を返すと分かります(standard版ではuvloopでした)。
Uvicornの起動コマンドと主要オプション
起動は「モジュール名:変数名」で指定します。main.pyのappならuvicorn main:app、app/main.pyならuvicorn app.main:appです。python -m uvicornの形は、仮想環境のPythonで確実に起動したいときに使います。
# 開発(変更を自動で再読込)
uvicorn main:app --reload
# コンテナや別マシンから受ける(既定のhostは127.0.0.1)
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000
# 本番の単体運用(4ワーカー)
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
Dockerで--hostを付け忘れると、コンテナ内の127.0.0.1でしか待ち受けないため、ホストからつながりません。Dockerfileとcompose構成はFastAPI×ReactのDocker本番デプロイ|uv対応Dockerfileとcompose構成にまとめています。
主要オプションと既定値
| オプション | 既定値 | 用途 |
|---|---|---|
--host・--port |
127.0.0.1・8000 | 待受アドレス |
--workers |
$WEB_CONCURRENCY、無ければ1 | ワーカープロセス数 |
--uds |
なし | UNIXソケットで待受 |
--timeout-keep-alive |
5秒 | keep-alive接続を閉じるまで |
--timeout-graceful-shutdown |
なし(無期限) | 停止時に処理中を待つ上限 |
--limit-max-requests |
なし | 指定件数で終了。再起動には管理プロセスが必要 |
--limit-concurrency |
なし | 超えたら503を返す |
--proxy-headers |
有効 | X-Forwarded-For/Protoを反映 |
--forwarded-allow-ips |
127.0.0.1,::1 | 上記を信頼する接続元 |
--log-level |
info | ログの粒度 |
既定値は公式のSettingsとuvicorn --help(0.54.0)で確認しました。
--reloadと--workersの併用時の挙動
両方を付けると、0.54.0ではWARNING: "workers" flag is ignored when reloading is enabled.と出て、ワーカー1本で起動します。エラーで止まらないため、本番用のコマンドに--reloadが残っていると、ワーカー数を指定したつもりで1本しか動いていない状態になります。
UvicornとGunicornの違い:2026年の3つの構成
以前は「GunicornはWSGI専用で、ASGIを動かすにはUvicornのワーカーを差し込む」という整理で済みました。2026年1月のGunicorn 24.0.0でネイティブのASGIワーカーがベータとして入り、25.1.0(2月13日)で安定版になったため(Gunicornの更新履歴)、いまは次の3通りから選びます。
| 観点 | Uvicorn単体 | Gunicorn+uvicorn-worker | Gunicorn -k asgi |
|---|---|---|---|
| 起動例 | uvicorn main:app --workers 4 |
gunicorn main:app -k uvicorn_worker.UvicornWorker -w 4 |
gunicorn main:app -k asgi -w 4 |
| プロセスの作り方 | spawn | pre-fork | pre-fork |
| 固まったワーカーの強制終了 | しない | --timeout(既定30秒) |
--timeout(既定30秒) |
| ワーカーの増減 | SIGTTIN/SIGTTOU | SIGTTIN/SIGTTOU | SIGTTIN/SIGTTOU |
| 処理数(1ワーカー) | 3,603 req/秒 | 3,312 req/秒 | 2,412 req/秒 |
| 追加パッケージ | なし | uvicorn-worker | なし(uvloopは任意) |
処理数は前の章と同じ条件(素のASGIアプリ)です。Gunicorn -k asgiはC実装のパーサー(gunicorn[fast])を入れて--http-parser fastを指定しても2,744 req/秒でした。
uvicorn.workers.UvicornWorkerからuvicorn-workerへの移行
uvicorn.workersモジュールは0.30.0(2024年5月)で非推奨になりましたが、0.54.0でも削除されておらず、-k uvicorn.workers.UvicornWorkerのままでも起動します。非推奨の警告はDeprecationWarningなので、Pythonの既定では表示されません。python -W default -c "import uvicorn.workers"で初めて警告文が見えます。気づかないまま残りやすいため、次のように置き換えておきます。
pip install uvicorn-worker
gunicorn main:app -w 4 -k uvicorn_worker.UvicornWorker
Uvicorn公式ドキュメントのDeploymentは、非推奨の注意書きを載せた直後のコマンド例がまだuvicorn.workers.UvicornWorkerのままです。例をコピーするときは、uvicorn-workerのREADMEの書き方に合わせてください。
Gunicornのネイティブ ASGI ワーカー
pip install gunicornだけで、gunicorn main:app -k asgiでFastAPIが動きます。GunicornのASGIワーカー解説によると、WebSocket・lifespan・HTTP/2に対応しています。HTTP/2はTLSが前提でしたが、26.2.0からはプロキシからの平文接続(h2c)も、forwarded_allow_ipsで信頼した相手に限って受けられます。ただし既定では無効で、gunicorn[http2]の導入と--http-protocols h2,h1、用途に応じた--http2-cleartextの指定が必要です。手元でも、FastAPIへのGETが200で返ることと、HEADへの405レスポンスから本文を落とした警告がログに出ることを確認しました。
Gunicorn 26.2.0は、起動するとコントロールソケット(25.1.0で追加された管理用の窓口)を$XDG_RUNTIME_DIR/gunicorn.ctl、その変数が無ければ~/.gunicorn/gunicorn.ctlに作ります(uvicorn-workerで起動した場合も同じです)。読み取り専用のファイルシステムで動かすコンテナでは、--no-control-socketを付けるか、書き込めるパスを--control-socketで指定します。
3つの構成の選び方
新規にFastAPIを本番へ出すなら、まずはUvicorn単体の--workersで足ります。FastAPI公式のServer Workersも、fastapi run --workers 4かuvicorn --workers 4を案内しています。Gunicornを挟むべきなのは、次の章で扱う「固まったワーカーを殺す」仕組みが必要な場合です。その場合も、処理数が高いのはuvicorn-workerのほうでした。Gunicorn -k asgiを選ぶのは、依存をGunicorn 1つにまとめたい場合か、プロキシからアプリまでHTTP/2を通したい場合に限ります。
Uvicornのタイムアウト設定と処理時間制限の違い
Uvicornのタイムアウト系オプションは、--timeout-keep-alive・--timeout-graceful-shutdown・--timeout-worker-healthcheckの3つだけです。どれも「1リクエストの処理時間」を打ち切るものではありません。
keep-aliveの5秒とロードバランサーの関係
レスポンスを返した後に接続を開いたまま待つと、既定では5秒で閉じられました(--timeout-keep-alive 75にすると7秒後も開いたまま)。AWSのApplication Load Balancerはアイドルタイムアウトの既定が60秒です。AWSのドキュメントは、アプリ側のアイドルタイムアウトをロードバランサーより長くするよう求めており、短いと502 Bad Gatewayの原因になると書いています。ALBの直後にUvicornを置くなら、--timeout-keep-alive 65のように60秒より長くします。
イベントループ停止時のワーカー監視と強制終了
async defの中でtime.sleep(12)を呼ぶエンドポイントを作り、2つの構成で確かめました。
uvicorn --workers 2:12秒後に200が返り、ワーカーは再起動されませんでした。親プロセスのヘルスチェック(既定5秒)は、ワーカー内の別スレッドが応答する作りになっています(uvicorn/supervisors/multiprocess.py)。そのため、イベントループが止まっていても「生きている」と判定されます。gunicorn -k uvicorn_worker.UvicornWorker --timeout 8:8秒で[CRITICAL] WORKER TIMEOUTがログに出てワーカーがSIGABRTで落とされ、新しいワーカーが起動しました。クライアントにはレスポンスが返らず、接続が切れます。-k asgiでも同じくWORKER TIMEOUTになり、こちらはSIGKILLで終了しました。
Gunicornの--timeoutが検知するのは「ワーカーからの生存通知が途絶えたこと」で、1リクエストの実行時間ではありません。イベントループが動いたまま長く待つawaitは、この設定では打ち切れません。リクエスト単位で待ち時間を制限するなら、アプリ内でasyncio.timeout()(Python 3.11以降)を使い、キャンセルできる非同期処理を打ち切ります。上のtime.sleep()のような同期処理によるループ停止はasyncio.timeout()でも中断できないため、同期処理をループの外へ出すのが先です。
Uvicornのワーカー数と再起動の運用
--workersの既定は1で、環境変数WEB_CONCURRENCYがあればその値を使います(WEB_CONCURRENCY=3で3プロセス起動を確認)。GunicornのWSGI運用でよく使う「2×コア数+1」は、1ワーカーで多数の接続をさばくASGIには当てはまりません。CPUコア数から始めて、メモリ使用量を見ながら決めます。Uvicornはpre-forkではなくspawnでワーカーを起動するため、アプリのimportはワーカーごとに走り、読み込み済みのメモリを親と共有しません。
0.51.0以降は、親プロセスにSIGHUPを送ると、新しいワーカーを起動し終えてから古いワーカーを1つずつ止めます。4ワーカーで試すと、ログは「Started server process(新)」→「Terminated child process(旧)」の順に4回並びました。デプロイ時の切り替えに使えます。停止時に処理中のリクエストをどう待つかはグレースフルシャットダウンとは?SIGTERM受信後の停止順序と猶予時間の設計を実装者目線で解説が詳しく、Uvicornでは--timeout-graceful-shutdownで待つ上限を決めます。
メモリリークの疑いがあるアプリは、--limit-max-requests 10000 --limit-max-requests-jitter 1000のように一定件数でワーカーを入れ替えます。jitterを付けない場合、各ワーカーの処理件数が近いと再起動時期が重なる可能性があります。
Nginxをリバースプロキシに置く構成
Uvicorn公式のDeploymentの章は、Nginxを前段に置き、UNIXソケットで接続する構成を推奨しています。次の設定例はこの章の例をもとに、X-Forwarded-Forの渡し方(理由は次の節)とproxy_http_versionを変えたものです。WebSocketを通すためのUpgradeヘッダの受け渡しも含みます。
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream uvicorn {
server unix:/run/uvicorn.sock;
}
server {
listen 80;
server_name example.com;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://uvicorn;
}
location /static {
root /path/to/app;
}
}
WebSocketのハンドシェイクの仕組みはWebSocketとは?仕組み・ハンドシェイクからNode.js実装・運用の判断まで実装者向けに解説で解説しています。
UNIXソケット接続時の転送ヘッダーの信頼設定
UvicornはX-Forwarded-ForとX-Forwarded-Protoを既定で読みますが、信頼するのは接続元が127.0.0.1か::1の場合だけです。接続方法と指定を変えて、ヘッダX-Forwarded-For: 203.0.113.9とX-Forwarded-Proto: httpsを付けたリクエストを送ると、アプリが受け取る値は次のようになりました。
| 接続方法 | 指定 | client | scheme |
|---|---|---|---|
| TCP 127.0.0.1 | 既定 | 203.0.113.9 | https |
| UNIXソケット | 既定 | None | http |
| UNIXソケット | ソケットのパス | None | http |
| UNIXソケット | --forwarded-allow-ips='*' |
203.0.113.9 | https |
UNIXソケット経由では接続元アドレスが空になり、信頼判定で常に「信頼しない」側に倒れます(uvicorn/middleware/proxy_headers.py)。この状態だとアプリは自分がhttpで呼ばれたと判断し、FastAPIのリダイレクト先やurl_forがhttpのURLになります。対策は--forwarded-allow-ips='*'の指定ですが、'*'のときUvicornはX-Forwarded-Forの先頭の値を接続元として採用します。X-Forwarded-For: 1.2.3.4, 203.0.113.9を送るとclientは1.2.3.4になり、利用者が自分で付けたヘッダで接続元IPを偽装できました。そのため上の設定例では、受け取ったX-Forwarded-Forに追記せず、Nginxから見た接続元$remote_addrで上書きしています。Nginxの前にさらにロードバランサーがある場合は、Nginxのrealipモジュールで信頼するアドレス範囲を絞ってから実IPを取り出します。Nginxが別ホストや別コンテナにあり、TCPで接続する場合は、そのIPかネットワーク(例:172.18.0.0/16)を指定します。
DjangoのUvicorn起動と静的ファイル配信
Django 3.0以降のプロジェクトにはasgi.pyがあり、Django公式の手順どおりpython -m uvicorn mysite.asgi:applicationで起動できます。Django 6.1.2のstartproject直後の状態で試すと、トップページと管理画面のログインページはともに200を返しました。注意点は2つです。
- 起動時に
ASGI 'lifespan' protocol appears unsupported.と出ます。Djangoがlifespanを実装していないためで、動作に影響はありません。消したいときは--lifespan offを付けます。 /static/admin/css/base.cssは404でした。runserverと違ってUvicornは静的ファイルを配信しないため、collectstaticした先をNginxで配信するか、WhiteNoiseを入れます。
同期ビュー中心のDjangoなら、WSGIのままGunicornで動かすほうが単純です。その構成はDjangoのデプロイ手順:gunicorn+nginx構成と本番設定・配置先の選び方、WebSocketのためにASGIへ移る判断はDjango Channelsとは?ASGIとConsumer・Channel Layerの構成と採用判断で扱っています。
Uvicornを選ばないほうがよい場面
- アプリがFlaskや同期のDjangoだけ:WSGIアプリをASGIサーバーで動かすには変換層が要り、得るものがありません。Gunicornの同期ワーカーで十分です。
- 処理がCPUを長く占有する:画像変換や重い集計を
async defで書くと、そのワーカーの他のリクエストがすべて待たされます。CPU負荷の高い処理は、ジョブキューやプロセスプールで別プロセスに分離します。FastAPIの同期エンドポイントをdefで定義するとスレッドプールに移りますが、GILを保持するPython処理のCPU並列化にはなりません。 - アプリまでHTTP/2で通したい:UvicornのHTTP/2は0.54.0時点で実験的です。必要ならGunicorn -k asgi(26.2.0で平文のh2cにも対応)かHypercornを検討します。
どのサーバーで動かすかも含めて、Pythonで業務システムを組む段階から相談したい場合は、一創のフルスクラッチ開発で設計から運用まで対応しています。
Uvicornに関するよくある質問
Uvicornの読み方は?
英語の発音に近いのは「ユーヴィコーン」で、日本語の記事では「ユビコーン」とも書かれます。公式に定められた読み方はありません。
UvicornとFastAPIの違いは何ですか?
FastAPIはルーティングや入力検証を書くためのフレームワークで、Uvicornはそれをポートで公開するASGIサーバーです。FastAPIだけではリクエストを受けられないため、Uvicornなどのサーバーと組み合わせます。
GunicornとUvicornの違いは何ですか?
Gunicornは固まったワーカーを--timeoutで強制終了できますが、Uvicornは処理時間の上限を持ちません。Gunicorn 25.1.0以降は-k asgiでGunicorn単体でもFastAPIを動かせます。
uvicorn.workers.UvicornWorkerは使えなくなりましたか?
0.54.0でも動きますが、0.30.0から非推奨です。pip install uvicorn-workerを入れて-k uvicorn_worker.UvicornWorkerに置き換えてください。
Uvicornのデフォルトのワーカー数は?
環境変数WEB_CONCURRENCYがあればその値、無ければ1です。--reloadを付けると--workersは無視され、1本で起動します。