Nginx高度な設定実践:監視、ログ管理、圧縮、暗号化およびURLRewrite

1. サーバー運用状況の可視化

Nginxの稼働状況をリアルタイムで確認するには、ngx_http_stub_status_moduleを利用します。ソースビルド時に--with-http_stub_status_moduleオプションを付与してコンパリングする必要があります。このモジュールは仮想ホスト単位ではなく、Nginxプロセス全体の接続状態を返却します。

server {
    listen 80;
    server_name monitoring.internal;
    access_log off;

    location /health/metrics {
        stub_status;
        allow 127.0.0.1;
        deny all;
    }
}

設定反映後はnginx -tで構文検証を行い、nginx -s reloadで適用します。該当エンドポイントにアクセスすると、アクティブな接続数、合計接続数、リクエスト処理数がテキスト形式で出力されます。

2. 開発・デバッグ用サードパーティモジュール

OpenRestyが提供するecho_nginx_moduleは、リクエストライフサイクル中に文字列や変数値を直接応答ボディに挿入できます。変数の状態確認やプロトコル仕様の検証に有用です。

server {
    listen 80;
    server_name dev.test.local;
    root /var/www/html;

    location /debug/request_info {
        echo "Client Source: $remote_addr";
        echo "Request URI: $request_uri";
        echo "Forwarded Header: $http_x_forwarded_for";
    }
}

モジュール未インストール状態でechoディレクティブを使用すると構文エラーが発生するため、コンパイル時の依存パッケージ確認が必須です。

3. 変数の活用と制御

3.1 組み込み変数

Nginxはリクエスト処理の各フェーズで自動的に値を保持する組み込み変数を提供します。代表的なものを以下に整理します。

  • $remote_addr:直結クライアントのIPアドレス(プロキシ環境では最終逆プロキシのIPになる)
  • $proxy_add_x_forwarded_for:X-Forwarded-Forヘッダーに現在の$remote_addrをカンマ区切りで追記する値
  • $args:URLのクエリ文字列部分(?key=valuekey=value
  • $document_root:現在のロケーションが解決するドキュメントルート絶対パス
  • $document_uri:クエリパラメータを除いたパス部分
  • $host:Hostヘッダーまたはサーバー名
  • $request_filenamerootまたはaliasから解決されたファイルの物理パス
  • $scheme:リクエストプロトコル(httpまたはhttps
  • $http_<header_name>:任意のリクエストヘッダー(例:$http_user_agent
  • $arg_<param_name>:クエリパラメータの特定値(例:$arg_id

変数を確認する際はechoモジュールと組み合わせ、ブラウザの開発者ツールで応答を確認すると効率的です。

3.2 独自変数の定義

setディレクティブを使用することで、serverlocationifコンテキスト内で独自変数を定義できます。

location /api/v1 {
    set $api_version "v1.2.4";
    set $internal_port $server_port;
    echo "Version: $api_version | Port: $internal_port";
}

4. アクセスログの設計と出力形式

エラーログとは異なり、アクセスログはユーザーのリクエスト内容を記録します。log_formatで出力形式を定義し、access_logでパスと形式を割り当てます。注意すべきは、includeディレクティブより上位にログフォーマットを配置しないと、読み込み順序により未定義エラーになる点です。

4.1 JSON形式ログの定義

外部のログ収集基盤(ELK StackやLokiなど)と連携する際、構造化されたJSON形式が推奨されます。

http {
    log_format json_structured escape=json '{'
        '"timestamp":"$time_iso8601",'
        '"server_ip":"$server_addr",'
        '"client_ip":"$remote_addr",'
        '"method":"$request_method",'
        '"uri":"$request_uri",'
        '"status":$status,'
        '"bytes_sent":$body_bytes_sent,'
        '"request_time":$request_time,'
        '"upstream_response_time":"$upstream_response_time",'
        '"user_agent":"$http_user_agent",'
        '"forwarded_ip":"$http_x_forwarded_for"'
    '}';

    access_log /var/log/nginx/app_access.log json_structured;
    # ... server blocks
}

5. ログローテーションの自動化

ログファイルが肥大化しないよう、定期的に分割・圧縮・削除する必要があります。以下はBashスクリプトを用いた実装例です。

#!/bin/bash
LOG_TARGET_DIR="/var/log/nginx/archive"
NGINX_LOG_DIR="/usr/local/nginx/logs"
NGINX_PID_FILE="${NGINX_LOG_DIR}/nginx.pid"
RETENTION_DAYS=30

mkdir -p "$LOG_TARGET_DIR"

DATE_SUFFIX=$(date -d "yesterday" +"%Y%m%d_%H%M%S")

mv "${NGINX_LOG_DIR}/access.log" "${LOG_TARGET_DIR}/access_${DATE_SUFFIX}.log"
mv "${NGINX_LOG_DIR}/error.log" "${LOG_TARGET_DIR}/error_${DATE_SUFFIX}.log"

if [ -f "$NGINX_PID_FILE" ]; then
    kill -USR1 $(cat "$NGINX_PID_FILE")
fi

find "$LOG_TARGET_DIR" -type f -name "*.log" -mtime +${RETENTION_DAYS} -delete

kill -USR1はNginxマスタープロセスに対して新規ログファイルの生成を促すシグナルです。このスクリプトをcrontabに組み込むことで定期実行が実現できます。

6. gzipによる応答データ圧縮

ngx_http_gzip_moduleは、特定のMIMEタイプを持つレスポンスをメモリ内で圧縮し、クライアントへ転送します。帯域幅削減には有効ですが、CPU使用率が上昇するトレードオフがあるため、設定値の調整が重要です。画像ファイルは元々圧縮済みであるため対象外とします。

gzip on;
gzip_comp_level 6;
gzip_min_length 512;
gzip_vary on;
gzip_proxied any;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml+rss text/javascript image/svg+xml;
gzip_static on;

gzip_static on;を有効にすると、拡張子.gzの事前圧縮ファイルが存在する場合、サーバー側の動的圧縮をスキップして直接配信します。これによりCPU負荷を大幅に軽減できます。

7. HTTPS/SSL/TLS通信の構築

Webアプリケーションの機密情報保護にはTLS暗号化が必須です。ハンドシェイクの概要は以下の通りです:

  1. クライアントがサーバーのポート443に接続要求
  2. サーバーが証明書(公開鍵を含む)を送信
  3. クライアントが証明書の正当性(署名者、有効期限)を検証
  4. クライアントがシムキー(事前共有鍵)を生成し、公開鍵で暗号化して送信
  5. サーバーが秘密鍵で復号し、以降の通信はシムキーによる対称暗号化で実施

YUMやAPTでインストールしたパッケージ版は通常SSLモジュールが有効ですが、ソースビルド時は--with-http_ssl_moduleが必要です。

7.1 セキュアなTLS設定例

server {
    listen 443 ssl http2;
    server_name secure.example.com;
    
    ssl_certificate /etc/ssl/certs/fullchain.pem;
    ssl_certificate_key /etc/ssl/private/privkey.pem;
    
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers on;
    
    ssl_session_cache shared:TLS_session_cache:50m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;
}

自己署名証明書の発行にはopensslコマンドを使用し、クライアント証明書をサーバー証明書に連結したファイルを作成します。ブラウザのセキュリティ警告を回避するには、信頼できる認証局(CA)が発行した証明書の利用が推奨されます。

8. ファビコンの指定と最適化

ブラウザはページ読み込み時に自動的に/favicon.icoをリクエストします。該当ファイルが存在しないと404ログが累積し、パフォーマンスにも影響します。明示的なロケーション定義とキャッシュ制御を設定しましょう。

location = /favicon.ico {
    root /var/www/assets;
    expires 30d;
    log_not_found off;
    access_log off;
}

9. URL Rewriteと応答制御

ngx_http_rewrite_moduleはPCREライブラリに依存し、リクエストURIの置換やリダイレクト、条件分岐を実現します。システム構成変更時のレガシーURL対応や、セキュリティ強化(不要なメソッドの拒否など)に利用されます。

9.1 条件マッチングとifディレクティブ

ifブロックは正規表現やファイル属性に基づいて評価します。Nginxのifは配置場所によっては予期せぬ挙動を起こすため、returnrewriteと組み合わせて慎重に使用します。

location /app {
    # POSTリクエストのみ許可する制限
    if ($request_method !~ ^(GET|HEAD|POST)$) {
        return 405;
    }

    # 旧ディレクトリ構造からの自動転送
    if ($request_uri ~ ^/legacy/(.*)$) {
        rewrite ^/legacy/(.*)$ /modern/$1 permanent;
    }
}

主な演算子:

  • = / !=:文字列の完全一致/不一致
  • ~ / !~:大文字小文字を区別する正規表現マッチ
  • ~* / !~*:大文字小文字を区別しない正規表現マッチ
  • -f / -d:ファイル/ディレクトリの存在確認

9.2 リダイレクトステータスの違い

returnディレクティブは処理を中断し、即座にクライアントへステータスを返却します。

# 恒久的な移行(ブラウザがキャッシュし、以降はサーバーを問い合わせずローカルで解決)
return 301 https://$host$request_uri;

# 一時的な転送(ブラウザがキャッシュせず、毎回サーバーへ再確認する)
return 302 /maintenance.html;

301はSEO上の権移譲やドメイン変更時、302はメンテナンス画面への一時的誘導やA/Bテストなどで利用されます。サーバー停止時でも301はローカルキャッシュにより遷移可能ですが、302はサーバーが到達不能だとエラーとなります。用途に応じたステータスコードの選定が、ユーザー体験と検索エンジン索引の両面で重要です。

タグ: nginx ssl/tls gzip url-rewrite log-rotation

5月27日 02:48 投稿