大量画像サイトの表示速度を劇的に改善する!Nginxキャッシュと「Cache-Control: immutable」完全設計ガイド

この記事は約9分で読めます。
この記事が役立ったらブックマーク! あとで読み返したり、環境構築時のリファレンスに活用できます
B! はてなブックマークに追加

WebP変換した数万枚規模の画像やアイコン、サムネイルを抱えるWebサイトでは、アクセスが増えるにつれてサーバーのディスクI/Oや帯域が圧迫されやすくなります。

「CDNを使わずに、オリジンサーバー(Nginx)だけで極限まで配信負荷を落としたい」

「一度配信した画像は、ブラウザ側で一切問い合わせさせずに即時表示させたい」

こうした要件を満たす鍵となるのが、Cache-Control: immutable ディレクティブと、Nginxの静的アセット最適化(open_file_cache / sendfile)の組み合わせです。本記事では、大量画像を抱えるサイトに最適なNginxキャッシュ設定と、その仕組みを詳しく解説します。

1. 「Cache-Control: immutable」とは何か?

従来のブラウザは、Cache-Control: max-age=31536000(1年間有効)と指定されていても、ユーザーがページを「F5(リロード)」した際には、サーバーへ「ファイルが更新されていないか?」を確認するリクエスト(304 Not Modified の問い合わせ)を飛ばす挙動を取ります。
画像が数万枚規模になると、この「念のための304問い合わせ」だけで数千件のリクエストがサーバーに殺到し、CPUやネットワーク帯域を無駄に消費します。

そこで登場したのが immutable(不変) 属性です。

  • 動作:「このアセットの内容は絶対に変わらない」とブラウザに通知します。
  • 効果:ユーザーがページをリロードしても、ブラウザはサーバーへの問い合わせを完全にスキップし、手元のディスクキャッシュから直接読み込みます。
  • メリット:サーバー側のリクエスト数が激減し、体感のページ再表示速度がほぼゼロ秒になります。

2. Nginxの画像配信特化型コンフィグ

サイトの設定ファイル(/etc/nginx/sites-available/example.com など)内の http ブロックおよび server ブロックに、以下の設定を組み込みます。

Nginx
# --------------------------------------------------
# 1. ファイルディスクリプタのキャッシュ(httpブロック内に推奨)
# --------------------------------------------------
# 静的ファイルの存在確認によるディスクI/Oを激減させる
open_file_cache          max=10000 inactive=120s;
open_file_cache_valid    60s;
open_file_cache_min_uses 2;
open_file_cache_errors   on;

server {
    listen 443 ssl http2;
    server_name example.com;
    root /var/www/example.com/htdocs;

    # --------------------------------------------------
    # 2. カーネルレベルでのゼロコピー転送
    # --------------------------------------------------
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    # --------------------------------------------------
    # 3. 大量静的画像(WebP, PNG, JPG, SVG, WebV)向け設定
    # --------------------------------------------------
    location ~* \.(webp|jpe?g|png|gif|ico|svg|avif)$ {
        # 1年間キャッシュ + immutable指定
        add_header Cache-Control "public, max-age=31536000, immutable" always;

        # 不要なアクセスログ・エラーログを止めてディスクI/Oを保護
        access_log off;
        log_not_found off;

        # CORS許可(必要に応じて)
        add_header Access-Control-Allow-Origin "*";

        # 静的ファイルが存在しなければ404で打ち切り(PHPに流さない)
        try_files $uri =404;
    }

    # WordPressなどの通常ルーティング(参考)
    location / {
        try_files $uri $uri/ /index.php?$args;
    }
}

3. ディスクI/Oを削ぎ落とすチューニングの要点

① open_file_cache によるメタデータキャッシュ

数万枚のファイルが存在する環境でリクエストが来るたび、OSは「そのファイルが実在するか」「サイズや更新日はいくつか」をディスクから読み出します。
open_file_cache を有効にしておくと、これらのファイル情報(ディスクリプタ)をメモリ上に保持するため、ストレージへの不要なアクセスが消滅し、ロードアベレージの上昇を抑えられます。

② access_log off による書き込み削減

1ページに画像が30枚ある場合、1PVごとにアクセスログが30行ディスクへ書き込まれます。
画像の access_log を無効化するだけで、バッファキャッシュの浪費やディスク書き込み待ち(I/O wait)を大幅に抑制できます。

③ try_files $uri =404; でPHP-FPMの誤爆を防ぐ

画像が見つからなかった際に、try_files $uri $uri/ /index.php?$args; が適用されてしまうと、存在しない画像へのリクエストがすべて重いPHP-FPM(WordPress)へ流れてしまいます。
画像拡張子のブロック内では =404 を明示し、存在しない画像はNginx単体で即座に404を返して処理を完結させます。

4. immutable 運用時の必須ルール:キャッシュバスティング

immutable を指定した画像は、ブラウザ側で1年間強制キャッシュされるため、サーバー側で同名ファイルを差し替えてもブラウザ側は絶対に再取得しません。
そのため、画像を差し替える可能性がある場合は、必ずキャッシュバスティング(ファイル名変更またはクエリ付与)を採用します。

  • URLクエリ型: /wp-content/uploads/banner.webp?v=2
  • ファイル名変更型(推奨): /wp-content/uploads/banner_v2.webp

WordPressのメディアライブラリ内の画像は、基本的に「アップロード後に中身を直接変更しない(新しい画像は別名でアップロードされる)」という運用が前提になっているため、immutable との相性は抜群です。

5. 設定の反映と検証

構文テストを行ってからNginxを再読み込みします。

Bash
# 構文チェック
sudo nginx -t

# 反映
sudo systemctl reload nginx

レスポンスヘッダーの確認

curl コマンドで画像URLへリクエストを送り、ヘッダーを確認します。

Bash
curl -I https://example.com/wp-content/uploads/test.webp

出力例:

HTTP
HTTP/2 200 
server: nginx
content-type: image/webp
content-length: 45120
cache-control: public, max-age=31536000, immutable
...

cache-control に immutable が含まれていれば設定完了です。

まとめ

画像の配信処理をブラウザ側とNginxのメモリ層で完結させることで、オリジンサーバーの負荷は最小化されます。

  • Cache-Control: immutable でリロード時の304問い合わせを完全遮断
  • open_file_cache でファイル探索のディスクI/Oをゼロに近づける
  • 画像ログの無効化と =404 の明示でPHPへの不要なリクエスト流入を防御

アクセス急増時にもサーバーのCPUやストレージ帯域を安定させたい場合、真っ先に導入すべき必須のインフラ設定です。

コメント

タイトルとURLをコピーしました