Webサーバーとアプリケーションサーバーの違い|Nginx+Gunicorn・PHP-FPM・Node.jsの構成を理解する
目次
- Webサーバーとアプリケーションサーバーの役割分担
- 「HTTPを喋る係」と「コードを動かす係」
- 言語別の典型構成|WSGI・ASGI・PHP-FPM・Node.js
- Python: Nginx → Gunicorn/Uvicorn(WSGI/ASGI)
- PHP: Nginx → PHP-FPM(FastCGI)
- Node.js: Nginx → Node(それ自体がHTTPサーバー)
- Unixソケット接続の利点と注意点
- 同一ホストならTCPよりUnixソケット
- なぜ本番でアプリサーバーを直接公開しないのか
- 遅いクライアントがワーカーを占有する
- その他の理由
- トラブル事例:Nginxが502を返し「Permission denied」が出る
- 症状
- 原因
- 対処
- まとめ
「FlaskやFastAPIは python app.py で動くのに、なぜ本番ではGunicornとNginxが必要なのか」「PHP-FPMとは何で、Apacheとどう違うのか」「Node.jsはそれ自体がHTTPサーバーなのだから、Nginxはいらないのでは」。フレームワークのチュートリアルを終えた後、本番構成に進もうとすると必ずぶつかる疑問です。
この記事では、Webサーバーとアプリケーションサーバーの役割分担を「何を担当し、何を担当しないか」で整理し、Python(WSGI/ASGI)・PHP(PHP-FPM)・Node.jsそれぞれの典型構成、両者をつなぐUnixソケット接続、そして「なぜ本番でアプリサーバーを直接公開しないのか」を仕組みから説明します。リバースプロキシとしてのNginx設定そのものはリバースプロキシとは?で解説していますので、本記事では構成を選ぶための考え方に集中します。
Webサーバーとアプリケーションサーバーの役割分担
「HTTPを喋る係」と「コードを動かす係」
Webサーバー(Nginx・Apache・Caddyなど)は、HTTPという通信を専門に処理するプログラムです。TLSの暗号化と復号、静的ファイルの配信、リクエストの受付とバッファリング、圧縮、リダイレクト、アクセス制御などを高速かつ安全に行うことに特化しており、アプリのコードは動かしません。
アプリケーションサーバー(Gunicorn・Uvicorn・PHP-FPM・Puma・Tomcatなど)は、あなたが書いたPythonやPHPのコードをプロセスとして常駐させ、リクエストを受けてコードを実行し、レスポンスを返す役割です。フレームワーク付属の開発用サーバー(flask run や php -S)はこの役割の簡易版で、単一プロセス・エラー時の情報表示・ホットリロードといった開発向けの設計になっています。
| 処理 | Webサーバー(Nginx) | アプリサーバー(Gunicorn等) |
|---|---|---|
| TLS終端(HTTPSの復号) | 担当 | 基本的に担当しない |
| 静的ファイル配信(画像・CSS・JS) | 担当(sendfileで高速) | できるが遅く、ワーカーを浪費する |
| リクエスト・レスポンスのバッファリング | 担当 | 担当しない |
| 圧縮・キャッシュ・レートリミット | 担当 | ライブラリで可能だが本来の役割ではない |
| アプリコードの実行 | 担当しない | 担当(複数ワーカーで並列実行) |
| 複数アプリの振り分け(ドメイン・パス別) | 担当(リバースプロキシ) | 担当しない |
言語別の典型構成|WSGI・ASGI・PHP-FPM・Node.js
Python: Nginx → Gunicorn/Uvicorn(WSGI/ASGI)
Pythonでは、Webサーバーとアプリの間にWSGI(同期)またはASGI(非同期)という標準インターフェースがあり、Flask/DjangoはWSGI、FastAPI/StarletteはASGIに対応しています。Gunicornは複数のワーカープロセスを管理するマスターで、WSGIアプリを直接動かすほか、UvicornWorker を指定してASGIアプリも動かせます。
# Flask/Django(WSGI): CPUコア数×2+1程度のワーカーが目安
gunicorn --workers 5 --bind unix:/run/myapp/app.sock myproject.wsgi:application
# FastAPI(ASGI): Gunicornにプロセス管理を任せ、Uvicornで非同期実行
gunicorn --workers 4 --worker-class uvicorn.workers.UvicornWorker \
--bind unix:/run/myapp/app.sock main:app
Gunicornの公式ドキュメント自身が「Nginxのようなプロキシの背後で使うこと」を推奨しています。理由は後述する「遅いクライアント」の問題です。FastAPIの基本はFastAPI入門を参照してください。
PHP: Nginx → PHP-FPM(FastCGI)
PHPは歴史的にApacheのモジュール(mod_php)としてWebサーバー内で実行されてきましたが、現在の主流はPHP-FPM(FastCGI Process Manager)です。PHP-FPMはPHPを実行するワーカープロセスのプールを常駐させ、NginxからFastCGIプロトコルでリクエストを受け取ります。Nginx側では .php へのリクエストだけを fastcgi_pass で転送し、それ以外の静的ファイルは自分で配信します。
# /etc/php/8.3/fpm/pool.d/www.conf の主要項目
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 20 # 同時に実行できるPHPプロセス数の上限
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
# Nginx側(抜粋)
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
pm.max_children は「1プロセスあたりのメモリ使用量×この値」がサーバーのメモリに収まるように決めます。WordPressなら1プロセス30〜60MB程度になることが多く、それを基準に逆算します。
Node.js: Nginx → Node(それ自体がHTTPサーバー)
Node.jsはHTTPサーバー機能を標準で持ち、ExpressやNext.jsはそのまま listen(3000) でリクエストを受けられます。この点でPythonやPHPとは事情が異なりますが、それでも本番ではNginxを前段に置くのが一般的です。TLS終端・静的配信・複数アプリの同居・バッファリングをNodeのイベントループから切り離せるからです。マルチコアを活かすにはPM2のクラスターモードやNodeの cluster モジュールでプロセスを複数起動し、Nginxの upstream で分散します。Express自体の作り方はNode.js+ExpressでREST APIで解説しています。
Unixソケット接続の利点と注意点
同一ホストならTCPよりUnixソケット
NginxとアプリサーバーはTCP(127.0.0.1:8000)でもUnixドメインソケット(/run/myapp/app.sock)でも接続できます。同じマシン上であればUnixソケットが有利です。TCPスタックを通らないためオーバーヘッドがわずかに小さく、何よりポートを一切開かないので、ファイアウォールの設定ミスでアプリサーバーが外部から直接叩かれる事故が構造的に起きません。アクセス制御はソケットファイルのパーミッションで行い、Nginxの実行ユーザー(Ubuntuなら www-data)が読み書きできる必要があります。
# systemdでGunicornを起動する際のソケット権限の例
# /etc/systemd/system/myapp.service(抜粋)
[Service]
User=myapp
Group=www-data
RuntimeDirectory=myapp
ExecStart=/opt/myapp/.venv/bin/gunicorn --workers 4 \
--bind unix:/run/myapp/app.sock --umask 007 myproject.wsgi:application
# umask 007 → ソケットが rw-rw---- (660) で作られ、www-dataグループから接続可能
一方、アプリサーバーが別ホストやコンテナにある場合はTCPしか選べません。Docker Composeでは各サービスが独立したネットワーク名前空間を持つため、proxy_pass http://app:8000 のようにサービス名とポートで接続します。
なぜ本番でアプリサーバーを直接公開しないのか
遅いクライアントがワーカーを占有する
最も重要な理由は「遅いクライアント」の問題です。Gunicornの同期ワーカーやPHP-FPMのプロセスは、1つのリクエストを処理している間、他のリクエストを受けられません。モバイル回線のユーザーが1MBのレスポンスを30秒かけてダウンロードすると、その30秒間ワーカーが1つ塞がります。ワーカーが5つなら、遅いクライアントが5人来た時点で全員が待たされます。悪意ある攻撃者が意図的に遅く接続すれば(Slowloris攻撃)、数十本の接続でサービスを止められます。
Nginxを前に置くと、Nginxはリクエスト本文を受け取り終えてからアプリサーバーに渡し、レスポンスも一気に受け取ってバッファし、あとはクライアントのペースに合わせてNginxが送ります。アプリのワーカーが拘束されるのは「コードの実行時間」だけになり、イベント駆動で数千接続を保持できるNginxが遅いクライアントの相手を引き受けます。この「バッファリング」こそ、Webサーバーを前段に置く最大の価値です。
その他の理由
- TLS終端: 証明書の管理・更新・暗号スイートの設定をNginxに集約し、アプリは平文HTTPだけ扱えばよい。
- 静的ファイル: Pythonプロセスで画像を返すのは、Nginxの
sendfileに比べて桁違いに非効率。 - 1024番未満のポート: 80/443で待ち受けるにはroot権限か特別な設定が必要。アプリはroot以外で動かすべき。
- HTTPの厳密な処理: 不正なヘッダーや巨大リクエストの拒否、タイムアウト、リクエスト数制限など、防御をアプリに実装しなくてよい。
- 無停止デプロイ: アプリを再起動している間もNginxが接続を受け、グレースフルシャットダウンと組み合わせてリクエストの取りこぼしを防げる。
トラブル事例:Nginxが502を返し「Permission denied」が出る
症状
DjangoアプリをGunicorn+Unixソケットで構成し、curl --unix-socket /run/myapp/app.sock http://localhost/ では正常に応答するのに、Nginx経由では502 Bad Gatewayになります。Nginxのエラーログには connect() to unix:/run/myapp/app.sock failed (13: Permission denied) と記録されていました。
原因
Gunicornを myapp ユーザー・myapp グループで起動しており、ソケットファイルが srwxr-xr-x myapp myapp で作られていました。Unixソケットへの接続には書き込み権限が必要ですが、Nginxのワーカーは www-data で動いているため、「その他」に書き込み権限のないソケットに接続できなかったのです。curlでの確認はrootで実行していたため気づけませんでした。
対処
- systemdユニットの
Group=www-dataと--umask 007を設定し、ソケットを660 myapp:www-dataで作成させる。 - ソケットの置き場所
/run/myapp自体にwww-dataが入れるよう、RuntimeDirectoryMode=0750とグループ所有を確認する。 - 再起動後、
sudo -u www-data curl --unix-socket /run/myapp/app.sock http://localhost/で「Nginxと同じユーザーで」接続できることを確認する。
SELinuxが有効なRHEL系では、これに加えて httpd_t がソケットに接続できるコンテキストかどうかも関わってきます。502の切り分けは「誰のユーザーで、どのソケット/ポートに、どんな権限で」を順に確認するのが定石です。
まとめ
Webサーバーとアプリケーションサーバーは競合する存在ではなく、得意なことが違う分業相手です。
- Nginxは「HTTPを喋る係」で、TLS・静的配信・バッファリング・振り分けを担当する。
- Gunicorn/Uvicorn・PHP-FPM・Node.jsは「コードを動かす係」で、ワーカーの数とメモリで性能が決まる。
- 同一ホストならUnixソケットで接続し、ポートを開けない。パーミッションはNginxの実行ユーザーに合わせる。
- アプリサーバーを直接公開しないのは、遅いクライアントからワーカーを守るため。
まずは自分の本番構成で「TLSはどこで終端しているか」「静的ファイルは誰が返しているか」「アプリサーバーのポートは外から見えないか」の3点を確認してみてください。Nginxの基本構築はLinux×Nginx入門で解説しています。本番構成の設計や既存環境のレビューが必要な場合は、Harmonic Societyのシステム開発・インフラ支援にご相談ください。
Harmonic Society
この記事の内容、自社の業務でも活かせそうですか?
ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。
関連記事
Related / 9 articles
Notes & Insights
- プログラミング
DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用
DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。
- プログラミング
WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド
WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。
- プログラミング
セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説
CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。
- プログラミング
クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計
クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。
- プログラミング
秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除
APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。
- プログラミング
開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御
開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。
- プログラミング
ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方
NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。
- プログラミング
ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方
ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。
- プログラミング
localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決
localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。
Harmonic Society
「読んで終わり」にせず、自社の業務で試してみませんか?
AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。
無料・30分・オンラインOK|1営業日以内に返信します