13
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

「Webサーバーって結局何をしているの?」
「ApacheとNginxの違いは?」
「リバースプロキシってよく聞くけど、何のために使うの?」

Web開発をしていると必ず出会うNginx。本記事では、Nginxの基礎概念から実践的な設定まで、なぜそうするのかを丁寧に解説します。

Webサーバーとは何か

Webサーバーの役割

Webサーバーは、HTTPリクエストを受け取り、HTTPレスポンスを返すプログラムです。ブラウザでhttps://example.comにアクセスすると、以下のことが起こります:

┌─────────────────────────────────────────────────────────────┐
│                    HTTPリクエストの流れ                      │
│                                                              │
│  ブラウザ                                    Webサーバー     │
│     │                                            │          │
│     │──── GET /index.html HTTP/1.1 ─────────────▶│          │
│     │                                            │          │
│     │     ファイルを探してレスポンスを生成        │          │
│     │                                            │          │
│     │◀─── HTTP/1.1 200 OK ──────────────────────│          │
│     │     Content-Type: text/html               │          │
│     │     <html>...</html>                      │          │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Webサーバーの主な仕事は:

  1. 静的ファイルの配信: HTML、CSS、JavaScript、画像などをそのまま返す
  2. 動的コンテンツの処理: PHP、Pythonなどのアプリケーションに処理を委譲
  3. セキュリティ: HTTPS対応、アクセス制御
  4. パフォーマンス: キャッシュ、圧縮、同時接続の効率的な処理

なぜNginxが選ばれるのか

Webサーバーには主にApacheとNginxの2つの選択肢があります。

Apache HTTP Server

  • 1995年登場の老舗
  • .htaccessで柔軟な設定が可能
  • モジュールが豊富
  • プロセス/スレッドベース:リクエストごとにプロセスやスレッドを生成

Nginx

  • 2004年登場
  • 高性能・軽量
  • 設定がシンプル
  • イベント駆動(非同期):少ないリソースで大量の同時接続を処理

なぜNginxの方が高速なのか?

Apacheは「1リクエスト = 1プロセス(またはスレッド)」モデルです。10,000同時接続があれば、10,000のプロセスが必要になり、メモリを大量に消費します。

Nginxは「イベント駆動」モデルです。少数のワーカープロセスが、非同期I/Oを使って大量のリクエストを効率的に処理します。これにより、同時接続数が増えてもメモリ使用量がほぼ一定です。

┌─────────────────────────────────────────────────────────────┐
│                    処理モデルの違い                          │
│                                                              │
│  Apache(プロセスベース)                                    │
│  ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐                           │
│  │Proc │ │Proc │ │Proc │ │Proc │ ...(接続数分必要)       │
│  └─────┘ └─────┘ └─────┘ └─────┘                           │
│                                                              │
│  Nginx(イベント駆動)                                       │
│  ┌──────────────────────────────────────┐                   │
│  │ Worker Process                       │                   │
│  │ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐       │ (少数で多数処理)│
│  │ │   │ │   │ │   │ │   │ │   │ ...   │                   │
│  │ └───┘ └───┘ └───┘ └───┘ └───┘       │                   │
│  └──────────────────────────────────────┘                   │
│                                                              │
└─────────────────────────────────────────────────────────────┘

どちらを選ぶべきか?

ユースケース 推奨
静的コンテンツ配信 Nginx
リバースプロキシ Nginx
高トラフィックサイト Nginx
.htaccessが必要 Apache
レガシーPHPアプリ Apache(またはNginx + PHP-FPM)

現代のWebでは、Nginxをフロントに置き、バックエンドのアプリケーションサーバーにプロキシする構成が主流です。

環境構築

インストール

# Ubuntu/Debian
sudo apt update
sudo apt install nginx

# CentOS/RHEL
sudo yum install nginx

# macOS (Homebrew)
brew install nginx

# Docker
docker run -d --name nginx -p 80:80 nginx:alpine

基本操作

# 起動
sudo systemctl start nginx

# 停止
sudo systemctl stop nginx

# 再起動
sudo systemctl restart nginx

# 設定のリロード(ダウンタイムなし)
# 新しいワーカープロセスを起動し、古いプロセスを徐々に終了
sudo systemctl reload nginx

# 設定ファイルの文法チェック(適用前に必ず実行)
sudo nginx -t

# 状態確認
sudo systemctl status nginx

reloadrestartの違い

  • restart: プロセスを完全に停止して再起動(一瞬ダウンタイムが発生)
  • reload: 設定を再読み込みしつつ、処理中のリクエストは継続(ダウンタイムなし)

本番環境では基本的にreloadを使います。

ディレクトリ構造

/etc/nginx/
├── nginx.conf              # メイン設定ファイル
├── conf.d/                 # 追加設定ファイル(*.conf)
├── sites-available/        # 利用可能なサイト設定(Debian系)
├── sites-enabled/          # 有効なサイト設定(シンボリックリンク)
├── mime.types              # MIMEタイプ定義
└── modules-enabled/        # 有効なモジュール

/var/log/nginx/
├── access.log              # アクセスログ
└── error.log               # エラーログ

/var/www/html/              # デフォルトのドキュメントルート

設定ファイルの基本

設定ファイルの構造

Nginxの設定はディレクティブと**コンテキスト(ブロック)**で構成されます。

# nginx.conf

# グローバルコンテキスト(最上位)
# Nginx全体の動作に関する設定
user nginx;                          # 実行ユーザー
worker_processes auto;               # ワーカープロセス数(CPUコア数に合わせる)
error_log /var/log/nginx/error.log;  # エラーログの場所
pid /run/nginx.pid;                  # プロセスIDファイル

# イベントコンテキスト
# 接続処理に関する設定
events {
    worker_connections 1024;         # 1ワーカーあたりの最大同時接続数
    multi_accept on;                 # 複数の接続を一度に受け入れる
}

# HTTPコンテキスト
# HTTP通信に関する設定
http {
    # 基本設定
    include /etc/nginx/mime.types;   # MIMEタイプ定義を読み込み
    default_type application/octet-stream;
    
    # ログフォーマット
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent"';
    
    access_log /var/log/nginx/access.log main;
    
    # パフォーマンス設定
    sendfile on;           # カーネル空間でファイルを直接送信
    tcp_nopush on;         # sendfileと組み合わせて効率化
    keepalive_timeout 65;  # Keep-Alive接続のタイムアウト
    gzip on;               # レスポンスを圧縮
    
    # サーバー設定を読み込み
    include /etc/nginx/conf.d/*.conf;
}

ディレクティブのルール

  • セミコロン(;)で終わる
  • 大文字小文字を区別する
  • ブロック({})内に入れ子にできる

静的ファイルの配信

最もシンプルな使い方:HTMLファイルを配信する

# /etc/nginx/conf.d/static-site.conf

server {
    # このサーバーブロックが受け付けるポート
    listen 80;
    
    # このサーバーブロックが受け付けるドメイン名
    # 複数指定可能。_はすべてにマッチするワイルドカード
    server_name example.com www.example.com;
    
    # ドキュメントルート(ファイルを探す起点)
    root /var/www/example.com/public;
    
    # デフォルトのインデックスファイル
    index index.html index.htm;
    
    # ロケーションブロック:URLパスに応じた処理
    location / {
        # try_files: ファイルを順に探す
        # $uri → $uri/ → 404エラー の順で探す
        try_files $uri $uri/ =404;
    }
    
    # 静的アセットのキャッシュ設定
    location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ {
        # ブラウザキャッシュを30日に設定
        expires 30d;
        # キャッシュ可能であることを明示
        add_header Cache-Control "public, immutable";
    }
    
    # エラーページのカスタマイズ
    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html;
}

locationディレクティブの優先順位

locationは複数のマッチ方法があり、優先順位があります:

記法 意味 優先度
= /path 完全一致 最高
^~ /path 前方一致(正規表現より優先)
~ /pattern 正規表現(大文字小文字区別)
~* /pattern 正規表現(大文字小文字区別なし)
/path 前方一致
# 完全一致(最優先)
location = /favicon.ico {
    log_not_found off;
}

# 正規表現より優先される前方一致
location ^~ /static/ {
    root /var/www/static;
}

# 正規表現(大文字小文字区別なし)
location ~* \.(jpg|jpeg|png)$ {
    expires 30d;
}

# 通常の前方一致(最後に評価)
location / {
    try_files $uri $uri/ =404;
}

リバースプロキシ

リバースプロキシとは

リバースプロキシは、クライアントとバックエンドサーバーの間に立ち、リクエストを中継するサーバーです。

┌─────────────────────────────────────────────────────────────┐
│                   リバースプロキシの構成                      │
│                                                              │
│  クライアント                                                │
│      │                                                       │
│      │ ① HTTPリクエスト                                      │
│      ▼                                                       │
│  ┌───────────────────────┐                                  │
│  │ Nginx(リバースプロキシ)│                                 │
│  │ ・SSL終端              │                                  │
│  │ ・キャッシュ            │                                  │
│  │ ・負荷分散              │                                  │
│  └───────────────────────┘                                  │
│      │                                                       │
│      │ ② プロキシリクエスト                                  │
│      ▼                                                       │
│  ┌───────────────────────┐                                  │
│  │ アプリケーションサーバー │                                 │
│  │(Node.js, Django等)    │                                 │
│  └───────────────────────┘                                  │
│                                                              │
└─────────────────────────────────────────────────────────────┘

なぜリバースプロキシを使うのか?

  1. SSL終端: NginxでHTTPSを処理し、バックエンドはHTTPで通信(証明書管理が楽)
  2. 静的ファイルの分離: 静的ファイルはNginxが直接配信、動的コンテンツだけバックエンドへ
  3. 負荷分散: 複数のバックエンドサーバーにリクエストを分散
  4. キャッシュ: よく使うレスポンスをNginxでキャッシュして高速化
  5. セキュリティ: バックエンドを直接公開しない(攻撃対象を減らす)

基本的なプロキシ設定

# /etc/nginx/conf.d/proxy.conf

server {
    listen 80;
    server_name app.example.com;
    
    location / {
        # バックエンドサーバーにプロキシ
        # proxy_pass の末尾スラッシュの有無で動作が変わる点に注意
        proxy_pass http://localhost:3000;
        
        # ========================================
        # 重要なプロキシヘッダー設定
        # ========================================
        
        # クライアントの実際のホスト名をバックエンドに伝える
        # これがないと、バックエンドはlocalhost:3000宛のリクエストと認識
        proxy_set_header Host $host;
        
        # クライアントの実際のIPアドレスを伝える
        # ログやレート制限で正しいIPを使うために必須
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # 元のプロトコル(http/https)を伝える
        # アプリがリダイレクトURLを生成するときに必要
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # WebSocket対応
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        
        # タイムアウト設定
        proxy_connect_timeout 60s;   # バックエンドへの接続タイムアウト
        proxy_send_timeout 60s;      # バックエンドへの送信タイムアウト
        proxy_read_timeout 60s;      # バックエンドからの読み取りタイムアウト
    }
    
    # 静的ファイルはNginxが直接配信(バックエンドに負荷をかけない)
    location /static/ {
        root /var/www/app;
        expires 30d;
    }
}

負荷分散(ロードバランシング)

なぜ負荷分散が必要か

1つのサーバーには処理能力の限界があります。アクセスが増えると、レスポンスが遅くなったり、サーバーがダウンしたりします。

負荷分散は、複数のサーバーにリクエストを分散することで:

  • スケーラビリティ: 処理能力を水平に拡張
  • 可用性: 1台がダウンしても他のサーバーで継続
  • メンテナンス: ローリングアップデートが可能

upstreamによる負荷分散

# /etc/nginx/conf.d/load-balance.conf

# バックエンドサーバーのグループを定義
upstream backend {
    # ========================================
    # 負荷分散アルゴリズム
    # ========================================
    
    # デフォルト: ラウンドロビン(順番に分散)
    # 何も指定しなければラウンドロビン
    
    # 接続数が少ないサーバーを優先
    # least_conn;
    
    # IPアドレスでハッシュ(同じクライアントは同じサーバーへ)
    # セッションを維持したい場合に有効
    # ip_hash;
    
    # バックエンドサーバーの定義
    server 192.168.1.10:3000 weight=3;  # 重み付け(3倍のリクエストを受ける)
    server 192.168.1.11:3000 weight=2;
    server 192.168.1.12:3000;           # weight=1(デフォルト)
    
    # バックアップサーバー(他がすべてダウンしたときのみ使用)
    server 192.168.1.20:3000 backup;
    
    # ヘルスチェック設定
    # 3回失敗したら30秒間リクエストを送らない
    server 192.168.1.10:3000 max_fails=3 fail_timeout=30s;
    
    # Keep-Alive接続を維持(パフォーマンス向上)
    keepalive 32;
}

server {
    listen 80;
    server_name app.example.com;
    
    location / {
        proxy_pass http://backend;
        
        # Keep-Alive接続を使用
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        
        # 標準のプロキシヘッダー
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

負荷分散アルゴリズムの選び方

アルゴリズム 説明 適用場面
ラウンドロビン(デフォルト) 順番に分散 一般的な用途
least_conn 接続数が少ないサーバー優先 処理時間にばらつきがある場合
ip_hash 同じIPは同じサーバーへ セッション維持が必要な場合
hash 任意のキーでハッシュ 高度なカスタマイズ

HTTPS対応

SSL/TLS証明書の設定

server {
    # HTTPSポート(443)でリッスン
    # sslオプションでSSL/TLSを有効化
    # http2オプションでHTTP/2を有効化(パフォーマンス向上)
    listen 443 ssl http2;
    server_name example.com;
    
    # ========================================
    # SSL証明書の設定
    # ========================================
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    
    # ========================================
    # SSL/TLSセキュリティ設定
    # ========================================
    
    # 使用するプロトコルバージョン
    # TLS 1.2と1.3のみ許可(古いバージョンは脆弱性あり)
    ssl_protocols TLSv1.2 TLSv1.3;
    
    # 暗号スイートの設定
    # 安全な暗号のみ使用
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    
    # サーバー側の暗号設定を優先
    ssl_prefer_server_ciphers on;
    
    # SSLセッションキャッシュ(ハンドシェイク高速化)
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    
    # OCSP Stapling(証明書検証の高速化)
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    
    # ========================================
    # セキュリティヘッダー
    # ========================================
    
    # HSTS(HTTPSを強制)
    # ブラウザに「今後はHTTPSでのみアクセス」と記憶させる
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    
    # その他のセキュリティヘッダー
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    
    location / {
        root /var/www/example.com;
        index index.html;
    }
}

# HTTPからHTTPSへのリダイレクト
server {
    listen 80;
    server_name example.com;
    
    # 301リダイレクト(恒久的な移動)
    return 301 https://$server_name$request_uri;
}

Let's Encryptで無料SSL証明書

# Certbotのインストール
sudo apt install certbot python3-certbot-nginx

# 証明書の取得とNginx設定の自動更新
sudo certbot --nginx -d example.com -d www.example.com

# 証明書の更新テスト
sudo certbot renew --dry-run

# 自動更新の設定(cronまたはsystemdタイマー)
# Certbotはインストール時に自動で設定される

パフォーマンスチューニング

基本的な最適化

http {
    # ========================================
    # ファイル送信の最適化
    # ========================================
    
    # sendfile: ディスク→ネットワークを直接転送(コピーをスキップ)
    sendfile on;
    
    # sendfileと組み合わせてパケットを最適化
    tcp_nopush on;
    
    # 小さなパケットをすぐに送信
    tcp_nodelay on;
    
    # ========================================
    # Gzip圧縮
    # ========================================
    
    gzip on;
    gzip_vary on;                    # Varyヘッダーを付与(プロキシキャッシュ対応)
    gzip_min_length 1024;            # 1KB以上のファイルを圧縮
    gzip_comp_level 5;               # 圧縮レベル(1-9、5が良いバランス)
    gzip_proxied any;                # プロキシ経由のリクエストも圧縮
    gzip_types text/plain text/css text/xml application/json application/javascript
               application/xml application/rss+xml image/svg+xml;
    
    # ========================================
    # バッファ設定
    # ========================================
    
    # クライアントリクエストボディのバッファサイズ
    client_body_buffer_size 16k;
    
    # プロキシレスポンスのバッファ
    proxy_buffering on;
    proxy_buffer_size 4k;
    proxy_buffers 8 16k;
    
    # ========================================
    # 接続設定
    # ========================================
    
    # Keep-Alive接続のタイムアウト
    keepalive_timeout 65;
    
    # 1つのKeep-Alive接続で処理するリクエスト数
    keepalive_requests 100;
}

キャッシュ設定

http {
    # プロキシキャッシュの保存場所
    # levels: キャッシュファイルのディレクトリ階層
    # keys_zone: キャッシュのメタデータを保持するメモリ領域
    # max_size: キャッシュの最大サイズ
    # inactive: この期間アクセスがなければ削除
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m 
                     max_size=10g inactive=60m use_temp_path=off;
    
    server {
        location / {
            proxy_pass http://backend;
            
            # キャッシュを有効化
            proxy_cache my_cache;
            
            # キャッシュキー
            proxy_cache_key $scheme$request_method$host$request_uri;
            
            # ステータスコードごとのキャッシュ時間
            proxy_cache_valid 200 302 10m;  # 成功レスポンスは10分
            proxy_cache_valid 404 1m;       # 404は1分
            
            # キャッシュの使用条件
            proxy_cache_use_stale error timeout invalid_header updating 
                                  http_500 http_502 http_503 http_504;
            
            # キャッシュ状態をレスポンスヘッダーに追加(デバッグ用)
            add_header X-Cache-Status $upstream_cache_status;
        }
    }
}

セキュリティ設定

server {
    # ========================================
    # アクセス制限
    # ========================================
    
    # 特定パスへのIP制限
    location /admin {
        allow 192.168.1.0/24;   # 許可するIP
        allow 10.0.0.0/8;
        deny all;               # それ以外は拒否
        
        proxy_pass http://backend;
    }
    
    # Basic認証
    location /private {
        auth_basic "Restricted Area";
        auth_basic_user_file /etc/nginx/.htpasswd;
        
        proxy_pass http://backend;
    }
    
    # ========================================
    # レート制限(DDoS対策)
    # ========================================
    
    # リクエストレート制限ゾーンの定義
    limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
    
    location /api/ {
        # レート制限を適用
        # burst: 一時的に許可する超過リクエスト数
        # nodelay: バースト内のリクエストを即座に処理
        limit_req zone=one burst=20 nodelay;
        
        proxy_pass http://backend;
    }
    
    # ========================================
    # 不要なメソッドの拒否
    # ========================================
    if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE)$) {
        return 444;
    }
    
    # ========================================
    # 隠しファイルへのアクセス拒否
    # ========================================
    location ~ /\. {
        deny all;
    }
}

実践的な設定例

Node.jsアプリケーション

upstream nodejs {
    server 127.0.0.1:3000;
    keepalive 64;
}

server {
    listen 80;
    server_name app.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name app.example.com;
    
    ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
    
    # 静的ファイル
    location /static/ {
        alias /var/www/app/public/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
    
    # WebSocket
    location /socket.io/ {
        proxy_pass http://nodejs;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
    
    # API
    location / {
        proxy_pass http://nodejs;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

トラブルシューティング

よくある問題と解決法

1. 502 Bad Gateway

  • バックエンドサーバーが起動していない
  • バックエンドのポートが間違っている
  • ファイアウォールでブロックされている
# バックエンドの確認
curl -I http://localhost:3000

# ポートの確認
ss -tlnp | grep 3000

2. 504 Gateway Timeout

  • バックエンドの処理が遅い
  • タイムアウト設定が短い
# タイムアウトを延長
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;

3. 403 Forbidden

  • ファイルのパーミッション問題
  • SELinuxの制限
# パーミッション確認
ls -la /var/www/html/

# Nginxユーザーでアクセス確認
sudo -u nginx stat /var/www/html/index.html

# SELinux確認
getenforce
ls -Z /var/www/html/

ログの確認

# アクセスログ
tail -f /var/log/nginx/access.log

# エラーログ
tail -f /var/log/nginx/error.log

# 特定のエラーを検索
grep "error" /var/log/nginx/error.log | tail -20

まとめ

Nginxは現代のWebインフラに欠かせないツールです。この記事で学んだ内容を整理すると:

  1. Webサーバーの基本: HTTPリクエストを受けてレスポンスを返す
  2. NginxとApacheの違い: イベント駆動で高効率
  3. 静的ファイル配信: rootとlocationの設定
  4. リバースプロキシ: バックエンドへの中継
  5. 負荷分散: upstreamで複数サーバーに分散
  6. HTTPS: SSL/TLS証明書の設定
  7. パフォーマンス: Gzip、キャッシュ、バッファ
  8. セキュリティ: アクセス制限、レート制限

まずはシンプルな静的サイトの配信から始めて、徐々にプロキシや負荷分散を追加していくのがおすすめです。設定を変更したら必ずnginx -tで文法チェックを忘れずに!

13
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?