Nginxの速度を向上させる:必須のバッファ、圧縮、キャッシュのヒント

バッファ最適化、Gzip圧縮、スマートなキャッシュ戦略に関するこの必須ガイドで、Nginxのピークパフォーマンスを引き出しましょう。クライアントおよびプロキシバッファの設定方法、帯域幅を削減するための堅牢なコンテンツ圧縮の実装方法、ブラウザとNginxプロキシキャッシュの両方を活用して超高速応答時間を実現する方法を学びます。実用的なNginx設定例とベストプラクティスを満載したこの記事では、ウェブサーバーの速度と効率を大幅に向上させるための実践的な洞察を提供します。

Nginxの速度を向上させる:必須のバッファ、圧縮、キャッシュのヒント

Nginxはデフォルトでも高速ですが、初期設定は意図的に控えめになっています。小規模なサイト、テストサーバー、またはトラフィックが少ないリバースプロキシには適していますが、大きなCookie、遅いアップストリーム、大きなAPIレスポンス、または1時間に何千回もダウンロードされる静的アセットを扱うようになると、必ずしも最適とは限りません。

有用なチューニングは通常、バッファ、圧縮、キャッシュの3つの領域に集約されます。バッファは、Nginxがリクエストとレスポンスのデータをメモリに保持できるか、一時ファイルに書き出す必要があるかを決定します。圧縮は、ネットワーク経由で送信するテキストベースのデータ量を決定します。キャッシュは、Nginxとブラウザが同じ作業を再度行うのを回避できるかどうかを決定します。これらの設定に魔法はありません。不適切なキャッシュルールはプライベートコンテンツを漏洩させる可能性があり、過剰に大きなバッファはメモリを浪費する可能性があります。目標は、意図的にチューニングし、実際のトラフィックパターンでテストし、後任者が理解できるように設定を読みやすく保つことです。

効率的なデータ処理のためのNginxバッファの最適化

Nginxは、リクエストとレスポンスの処理中にデータを一時的に保存するためにさまざまなバッファを使用します。これらのバッファを適切にサイジングすることは、パフォーマンスにとって重要です。サイズが不適切なバッファは、過剰なメモリ消費または頻繁なディスク書き込み(スプーリング)のいずれかを引き起こし、どちらもパフォーマンスを低下させます。ここでは、クライアント関連のバッファとプロキシ/FastCGIバッファについて説明します。

クライアント関連のバッファ

これらのバッファは、クライアントからNginxに送信されるデータを管理します。

  • client_body_buffer_size: このディレクティブは、クライアントのリクエストボディを読み取るためのバッファサイズを設定します。リクエストボディがこのサイズを超えると、ディスク上の一時ファイルに書き込まれます。これにより、大きなアップロードによるメモリ枯渇を防ぎますが、頻繁なディスク書き込みはパフォーマンスを低下させる可能性があります。

    • ヒント: POSTリクエストを介して非常に大きなファイルのアップロードを処理しない一般的なWebアプリケーションの場合、8kまたは16kで十分なことがよくあります。Nginxを介して直接大きなフォームや小さなファイルのアップロードを処理する場合は、増やしてください。
    http {
        client_body_buffer_size 16k;
        # ...
    }
    
  • client_header_buffer_size: クライアントのリクエストヘッダーを読み取るためのバッファサイズを定義します。接続ごとに1つのバッファが割り当てられます。

    • ヒント: 1kがデフォルトで、ほとんどのヘッダーに通常は十分です。多くのCookieや複雑な認証ヘッダーが原因で「client sent too large header」エラーが発生する場合にのみ増やしてください。
    http {
        client_header_buffer_size 1k;
        # ...
    }
    
  • large_client_header_buffers: このディレクティブは、大きなクライアントリクエストヘッダーを読み取るために使用されるバッファの最大数とサイズを設定します。ヘッダーがclient_header_buffer_sizeを超える場合、Nginxはこのディレクティブを使用してバッファの割り当てを試みます。

    • ヒント: 4 8k(8KBのバッファ4つ)が一般的な設定です。client_header_buffer_sizeを増やしても一貫してヘッダーエラーが発生する場合は調整してください。
    http {
        large_client_header_buffers 4 8k;
        # ...
    }
    

プロキシおよびFastCGIバッファ

これらのバッファは、Nginxがリバースプロキシとして機能する場合、またはFastCGIバックエンド(PHP-FPMなど)と通信する場合にデータを管理します。

Nginxがリクエストをプロキシする場合、バックエンドサーバーからレスポンスをチャンクで受信し、クライアントに送信する前にバッファリングします。これにより、Nginxはクライアント接続をブロックせずに遅いバックエンドレスポンスを処理できます。

  • proxy_buffer_size: プロキシサーバーから受信したレスポンスの最初の部分のバッファサイズ。これには通常、レスポンスヘッダーが含まれます。

  • proxy_buffers: プロキシサーバーからのレスポンスの読み取りに使用されるバッファの数とサイズを定義します。

  • proxy_busy_buffers_size: 任意の時点でアクティブ(ビジー)にできるバッファの最大サイズを設定します。これは、クライアントへのデータ送信またはバックエンドからの読み取りのいずれかです。これにより、Nginxがバッファを長時間保持してメモリを過剰に消費するのを防ぎます。

    • プロキシパスの例: 一般的なWebアプリケーションの場合、proxy_buffer_sizeを予想されるヘッダーサイズに一致させ、proxy_buffersをディスクに書き込まずに平均的なコンテンツサイズを処理するように設定できます。
    http {
        proxy_buffer_size          128k;
        proxy_buffers              4 256k; # 4 buffers, each 256KB
        proxy_busy_buffers_size    256k;
        # ...
    }
    
  • fastcgi_buffer_sizefastcgi_buffersfastcgi_busy_buffers_size: これらのディレクティブは、対応するproxy_ディレクティブと同様に機能しますが、特にFastCGIサーバーからのレスポンスに適用されます。

    • FastCGIの例: ここでも同様のロジックが適用され、PHP/FastCGIアプリケーションのレスポンスサイズに合わせて調整します。
    http {
        fastcgi_buffer_size        128k;
        fastcgi_buffers            4 256k;
        fastcgi_busy_buffers_size  256k;
        # ...
    }
    

警告: バッファを大きく設定しすぎると、接続ごとにより多くのRAMを消費し、ビジーなサーバーでメモリがすぐに枯渇する可能性があります。小さく設定しすぎると、Nginxが一時ファイルをディスクに書き込むため、I/Oオーバーヘッドが発生します。サーバーのメモリとディスクI/Oを監視して、最適なバランスを見つけてください。

Gzipによる効果的な圧縮の有効化

主にGzipを使用したコンテンツ圧縮は、送信データのサイズを大幅に削減し、ページの読み込みを高速化し、帯域幅の消費を抑えることができます。Nginxのgzipモジュールは高度に設定可能です。

必須のGzipディレクティブ

これらのディレクティブをhttpブロック内、または特定のserverまたはlocationブロック内に追加します。

  • gzip on;: Gzip圧縮を有効にします。

  • gzip_types: 圧縮する必要があるMIMEタイプを指定します。特定のテキストベースのタイプのみが圧縮の恩恵を大きく受けます。

    • ベストプラクティス: 一般的なWebタイプを含めますが、画像(image/*)、動画(video/*)、および既に圧縮されているファイル(.zip.rar.gz)の圧縮は避けてください。これらはCPUサイクルを無駄にするだけでメリットがありません。
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
    
  • gzip_proxied: レスポンスとリクエストの条件に基づいて、プロキシされたリクエストの圧縮を有効にします。これは主に、Nginxがアプリケーションサーバーまたは別のプロキシの前に配置されている場合に役立ちます。

    • any: それ以外の条件を満たす場合、プロキシされたレスポンスを圧縮します。
    • no-cacheno-storeprivateexpiredauth: レスポンスまたはリクエストがそれらの条件に一致する場合にのみ圧縮します。これらのトークンは「圧縮しない」フラグではなく、プロキシされたレスポンスの圧縮を許可する条件です。
    gzip_proxied any;
    
  • gzip_min_length: Nginxが圧縮するレスポンスボディの最小長を設定します。小さなファイルは圧縮の恩恵をあまり受けず、圧縮のオーバーヘッドによりサイズが大きくなることさえあります。

    • ヒント: 1000バイト(1KB)や256バイトなどの値が良い出発点です。
    gzip_min_length 1000;
    
  • gzip_comp_level: 圧縮レベル(1〜9)を設定します。レベルが高いほど圧縮率は向上しますが、より多くのCPUリソースを消費します。レベルが低いほど高速ですが、圧縮効果は低くなります。

    • ヒント: ほとんどのサーバーでは、4〜6が圧縮率とCPU使用率のバランスが良いです。
    gzip_comp_level 5;
    
  • gzip_vary on;: クライアントから送信されたAccept-Encodingヘッダーに応じて、プロキシがファイルの圧縮バージョンと非圧縮バージョンの両方をキャッシュするように指示します。これは、適切なキャッシュと配信のために重要です。

    gzip_vary on;
    
  • gzip_disable: Gzipに問題がある可能性のある特定のブラウザーまたはユーザーエージェントの圧縮を無効にします。

    gzip_disable "MSIE [1-6]\."; # 例:古いInternet Explorerでは無効にする
    

考慮事項: Gzipは非常に有益ですが、圧縮はCPUサイクルを消費します。ディスクから直接提供される静的ファイル(事前圧縮された.gzファイルなど)の場合、Nginxは再圧縮せずに直接提供できるため、さらに効率的です。動的コンテンツの場合、Gzipは通常、純利益をもたらします。

スマートなキャッシュ戦略の実装

キャッシュは、コンテンツを再生成または再取得する必要性を減らすことで、Webサーバーのパフォーマンスを向上させる最も効果的な方法の1つです。Nginxは、ブラウザ側(クライアント側)とサーバー側(プロキシ)の両方のキャッシュをサポートしています。

ブラウザキャッシュ(HTTPヘッダー)

ブラウザキャッシュは、HTTPヘッダーに依存して、クライアントブラウザに静的アセットを保存する期間を指示します。これにより、画像、CSS、JavaScriptファイルなどの変更されないリソースの繰り返しのダウンロードを防ぎます。

  • expires: ExpiresヘッダーとCache-Control: max-ageヘッダーを設定するためのシンプルなディレクティブ。

    location ~* \.(jpg|jpeg|gif|png|webp|ico|css|js|woff|woff2|ttf|otf|eot)$ {
        expires 365d; # 1年間キャッシュ
        add_header Cache-Control "public, no-transform";
        # オプション:静的ファイルのログを無効にする
        access_log off;
        log_not_found off;
    }
    
  • add_header Cache-Control: キャッシュポリシーをより細かく制御できます。一般的な値は次のとおりです。

    • public: 任意のキャッシュ(ブラウザ、プロキシ)でキャッシュ可能。
    • private: クライアントのプライベートキャッシュ(ブラウザなど)のみでキャッシュ可能。
    • no-cache: 使用前にサーバーで再検証する必要がありますが、コピーを保存することはできます。
    • no-store: まったくキャッシュしない。
    • max-age=<seconds>: リソースが新しいと見なされる期間を指定します。
  • 条件付きリクエスト(EtagおよびIf-Modified-Since: Nginxは静的ファイルのEtagおよびLast-Modifiedヘッダーを自動的に処理し、ブラウザが条件付きリクエスト(If-None-MatchまたはIf-Modified-Since)を送信できるようにします。コンテンツが変更されていない場合、Nginxは304 Not Modifiedで応答し、帯域幅を節約します。

Nginxプロキシキャッシュ

Nginxは、強力なキャッシュリバースプロキシとして機能できます。有効にすると、Nginxはバックエンドサーバーからのレスポンスのコピーを保存し、クライアントに直接提供するため、バックエンドの負荷が大幅に軽減されます。

1. キャッシュゾーンの定義

これはhttpブロック内で行う必要があります。proxy_cache_pathは、キャッシュのディレクトリ、メモリゾーンパラメータ、およびその他の設定を定義します。

http {
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m inactive=60m max_size=1g;
    # levels=1:2: キャッシュファイル用の2レベルのディレクトリ階層を作成します(例:/var/cache/nginx/c/29/...)。ファイルを分散するのに役立ちます。
    # keys_zone=my_cache:10m: 'my_cache'という名前の10MBの共有メモリゾーンを定義し、キャッシュキーとメタデータを保存します。これは迅速なルックアップのために重要です。
    # inactive=60m: 60分間アクセスされなかったキャッシュアイテムはディスクから削除されます。
    # max_size=1g: ディスク上のキャッシュの最大サイズを設定します。これを超えると、Nginxは最も最近使用されていないデータを削除します。
    # ...
}

2. ロケーションのキャッシュを有効にする

serverまたはlocationブロック内で、キャッシュを有効にし、その動作を定義します。

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend_upstream; # または http://127.0.0.1:8000;
        proxy_cache my_cache; # 上記で定義したキャッシュゾーンを使用
        proxy_cache_valid 200 302 10m; # 成功レスポンス(200、302)を10分間キャッシュ
        proxy_cache_valid 404 1m;      # 404レスポンスを1分間キャッシュ
        proxy_cache_revalidate on;     # 再検証にIf-Modified-SinceおよびIf-None-Matchヘッダーを使用
        proxy_cache_min_uses 1;       # アイテムが少なくとも1回リクエストされた場合のみキャッシュ
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
                                       # バックエンドがダウンしているか更新中の場合は、古いコンテンツを提供

        # レスポンスがキャッシュされたかどうかを確認するためのヘッダーを追加
        add_header X-Cache-Status $upstream_cache_status;

        # オプション:特定の条件でキャッシュをバイパス
        # proxy_cache_bypass $http_pragma $http_authorization;
        # proxy_no_cache $http_pragma $http_authorization;
    }
}

重要なキャッシュディレクティブ

  • proxy_cache_valid: HTTPステータスコードと期間に基づいてキャッシュルールを定義します。複数のルールを指定できます。

  • proxy_cache_revalidate on;: Nginxがキャッシュされたコンテンツがまだ新しいかどうかを確認する際に、If-Modified-SinceおよびIf-None-Matchヘッダーを使用できるようにします。これは単にキャッシュを期限切れにするよりも効率的です。

  • proxy_cache_use_stale: バックエンドが利用できないか遅い場合に、Nginxがキャッシュから古い(期限切れの)コンテンツを提供するように指示する強力なディレクティブです。これにより、バックエンドの問題が発生した場合のユーザーエクスペリエンスが大幅に向上します。

  • proxy_cache_bypass / proxy_no_cache: これらを使用して、キャッシュをバイパスする条件(認証済みリクエストや特定のクエリパラメータなど)を定義します。

    # 特定のクエリパラメータまたはCookieを持つリクエストをキャッシュしない例
    # if ($request_uri ~* "(\?|&)nocache") { set $no_cache 1; }
    # if ($http_cookie ~* "SESSIONID") { set $no_cache 1; }
    # proxy_cache_bypass $no_cache;
    # proxy_no_cache $no_cache;
    

キャッシュのクリア

Nginxキャッシュを手動でクリアするには、proxy_cache_pathディレクトリ内のファイルを削除するだけです。より制御された無効化を行うには、ngx_cache_purgeモジュールを使用するか、キャッシュ無効化リクエストを処理する特定のlocationを設定することを検討してください。

警告: プロキシキャッシュの設定を誤ると、ユーザーが古いコンテンツを表示する可能性があります。本番環境にデプロイする前に、ステージング環境でキャッシュ戦略を徹底的にテストしてください。頻繁に変更される動的コンテンツやユーザー固有のコンテンツが積極的にキャッシュされないようにしてください。

実践的なロールアウト計画

Nginxをチューニングする最も安全な方法は、一度に1つのレイヤーを変更することです。リスクが低く、メリットがわかりやすいため、ヘッダーと静的アセットから始めてください。バージョン管理されたファイル(/app.8f3a2c.js/styles.2025-11-02.cssなど)に対してのみ、長いブラウザキャッシュの有効期間を追加してください。アセット名がファイルの変更時に変更されない場合は、1年間キャッシュしないでください。代わりに、より短いexpires値を使用するか、最初にビルドパイプラインを修正してください。

次に、テキスト形式に対してGzipを有効にし、機能することを確認します。

curl -I -H 'Accept-Encoding: gzip' https://example.com/app.js

圧縮可能なレスポンスにはContent-Encoding: gzipが表示され、Vary: Accept-Encodingが表示されるはずです。画像やZIPファイルが圧縮されて表示される場合は、gzip_typesを絞り込んでください。これらの形式は既に圧縮されており、通常はCPUを無駄に消費します。

その後、バッファリングを確認します。エラーログで、アップストリームのレスポンスが一時ファイルにバッファリングされているというメッセージを確認します。これらのメッセージは自動的に災害を意味するわけではありませんが、Nginxがレスポンスデータをディスクに書き込んでいることを示しています。通常のページ読み込みやAPIレスポンス中に発生する場合、プロキシバッファがワークロードに対して小さすぎる可能性があります。巨大なエクスポートやダウンロードでのみ発生する場合は、すべてのリクエストパスに大きなバッファを割り当てるよりも、ディスクスプーリングを受け入れる方が良い場合があります。

プロキシキャッシュは最後に行う必要があります。最大のメリットと最も簡単な障害モードがあります。明らかに安全なコンテンツから始めてください。公開ドキュメントページ、匿名の製品カタログページ、画像変換レスポンス、パッケージメタデータ、またはすべての訪問者に対して同一のAPIレスポンスなどです。アプリケーションが明示的にそのように設計されていない限り、セッションCookie、Authorizationヘッダー、ショッピングカート、ユーザーダッシュボード、管理ページ、またはパーソナライズされたレコメンデーションに関連するものはキャッシュしないでください。

以下は、よく見かける短い例よりも慎重なキャッシュバイパスパターンです。

map $http_authorization $skip_cache_auth {
    default 1;
    ""      0;
}

map $http_cookie $skip_cache_cookie {
    default 0;
    ~*"(session|sid|auth|token)" 1;
}

server {
    location / {
        proxy_pass http://backend_upstream;
        proxy_cache my_cache;
        proxy_cache_valid 200 10m;
        proxy_cache_bypass $skip_cache_auth $skip_cache_cookie;
        proxy_no_cache $skip_cache_auth $skip_cache_cookie;
        add_header X-Cache-Status $upstream_cache_status always;
    }
}

これは依然としてアプリケーションに一致する必要がありますが、重要な考え方を明確にしています。認証済みおよびセッションのように見えるリクエストは、共有キャッシュに静かに入るべきではないということです。

変更後に監視すべきこと

Nginxのチューニングを単一のベンチマークだけで判断しないでください。通常のトラフィックサイクルを数回サーバーを監視してください。有用なシグナルには、/var/lib/nginxまたは/var/cache/nginxへのディスク書き込み、アップストリームの応答時間、キャッシュヒット率、圧縮有効化後のCPU使用率、499/502/504エラー、ワーカーあたりのメモリ使用量が含まれます。Gzip後にCPUが急上昇したが帯域幅がほとんど変わらない場合は、gzip_comp_levelを下げるか、MIMEタイプを制限してください。キャッシュヒット率が低い場合、キャッシュがCookie、クエリ文字列、またはアプリケーションからのレスポンスヘッダーによってバイパスされている可能性があります。

また、障害動作をテストしてください。ステージングでアップストリームを停止または遅くし、proxy_cache_use_staleが期待どおりに動作することを確認してください。バックエンドの短時間の停止中に古い公開ページを提供することは問題ないかもしれません。古いアカウント残高、請求書、または管理ページは問題外です。

最後に

優れたNginxパフォーマンスの作業は、主に慎重なハウスキーピングです。不必要なディスクI/Oを避けるために十分な大きさのバッファを使用しますが、すべてのビジーワーカーが高コストにならないように大きすぎないようにします。テキストを圧縮し、既に圧縮されているアセットは圧縮しません。最初に公開され、繰り返し可能なレスポンスをキャッシュし、プライベートコンテンツは明確にオプトアウトします。そして、測定を続けてください。小さなPHPアプリ、静的なドキュメントサイト、ビジーなAPIゲートウェイに適した設定は同じではないからです。