Nginxで静的アセットをキャッシュしてサイトを高速化する方法
静的アセットのキャッシュが重要な理由
ユーザーがあなたのウェブサイトを訪れるたびに、ブラウザはCSSファイル、JavaScript、画像、フォントなど、数十の静的アセットをリクエストします。適切なキャッシュがなければ、各リクエストがサーバーに到達し、読み込み時間と帯域幅の使用量が増加します。高性能ウェブサーバーであるNginxは、ブラウザにこれらのアセットをローカルにキャッシュするよう指示し、サーバー側でもキャッシュすることで、これを劇的に改善できます。
このガイドでは、Nginxを設定して静的アセットを効果的にキャッシュし、レイテンシとサーバー負荷を削減する方法を学びます。
ブラウザキャッシュとサーバー側キャッシュの理解
ここで関連するキャッシュには主に2つの種類があります:
- ブラウザキャッシュ: ブラウザは
Cache-ControlやExpiresなどのHTTPヘッダーに基づいてアセットをローカルに保存します。以降の訪問ではディスクからアセットを読み込み、ネットワークリクエストを回避します。 - サーバー側キャッシュ: Nginxはレスポンスをメモリまたはディスクに保存し、繰り返しのリクエストに対して直接配信することで、バックエンドの負荷を軽減します。
どちらもパフォーマンスにとって重要です。両方について説明します。
ステップ1: Expiresヘッダーでブラウザキャッシュを設定する
ブラウザキャッシュを有効にする最も簡単な方法は、Nginx設定にexpiresディレクティブを追加することです。これによりExpiresとCache-Controlヘッダーが設定されます。
サイトの設定ファイル(例: /etc/nginx/sites-available/example.com)を開き、静的アセット用のlocationブロックを追加します:
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
これにより、ブラウザはこれらのファイルを1年間キャッシュします。immutableディレクティブはファイルが決して変更されないことを示すため、ブラウザは再読み込み時にも再検証を行いません。
ベストプラクティス: バージョン付きのファイル名(例: style.abc123.css)を使用して、キャッシュを壊さずにアセットを更新できるようにします。ファイルが変更されるとファイル名も変わり、ブラウザは新しいバージョンを取得します。
ステップ2: プロキシキャッシュでサーバー側キャッシュを有効にする
Nginxの背後でアプリケーションサーバー(Node.js、Python、PHPなど)を実行している場合、Nginxで静的レスポンスをキャッシュしてバックエンドへのリクエストを削減できます。
まず、nginx.confのhttpブロックでキャッシュパスを定義します:
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=static_cache:10m max_size=1g inactive=60m use_temp_path=off;
次に、serverブロックでこれを使用します:
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg)$ {
proxy_cache static_cache;
proxy_cache_valid 200 302 1y;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://backend;
}
これにより、成功したレスポンスが1年間キャッシュされ、バックエンドがダウンしている場合は古いコンテンツが配信されます。X-Cache-Statusヘッダーはデバッグに役立ちます(HIT、MISSなど)。
ステップ3: 圧縮とHTTP/2で最適化する
キャッシュはリクエストを削減しますが、gzip圧縮とHTTP/2を有効にすることで転送をさらに高速化できます。
nginx.confでgzipを有効にします:
gzip on;
gzip_types text/css application/javascript image/svg+xml;
gzip_min_length 1000;
HTTP/2はリクエストを多重化し、listenディレクティブにhttp2を追加することで有効になります:
listen 443 ssl http2;
注: HTTP/2にはHTTPSが必要です。まだTLSを設定していない場合は、HTTPSとTLSの仕組みに関するガイドをご覧ください。
比較: Cache-Controlディレクティブ
| ディレクティブ | 意味 | 推奨用途 |
|---|---|---|
public |
レスポンスは任意のキャッシュでキャッシュ可能 | 静的アセット |
private |
レスポンスは単一ユーザー用 | ユーザー固有のデータ |
no-cache |
サーバーで再検証が必要 | HTMLページ |
no-store |
キャッシュしない | 機密データ |
immutable |
コンテンツは変更されない | バージョン付きアセット |
ステップ4: テストと検証
変更を適用したら、Nginxを再読み込みします: sudo nginx -s reload。次にcurlでテストします:
curl -I https://example.com/style.css
Cache-ControlとExpiresヘッダーを確認します。ブラウザのDevTools(Networkタブ)を使用して、アセットがキャッシュから配信されているか(ステータス200または304)を確認することもできます。
サーバー側キャッシュについては、X-Cache-Statusヘッダーを監視します。
よくある落とし穴とベストプラクティス
- HTMLをキャッシュしない: HTMLは動的にするか、更新を反映するために短いキャッシュ時間にします。
- バージョニングを使用する: コンテンツが変更されたときにキャッシュを破棄するため、ファイル名にハッシュを追加します。
- 適切なmax-ageを設定する: バージョン付きアセットには1年間が安全です。
- キャッシュサイズを監視する: サーバー側キャッシュは大きくなる可能性があるため、
max_sizeとinactiveを設定します。 - CDNを検討する: グローバルなオーディエンスには、CDNがユーザーにより近い場所でアセットをキャッシュできます。
FAQ
アセットがキャッシュされているかどうかを確認するには?
curl -IまたはブラウザのDevToolsでレスポンスヘッダーを確認します。Cache-Control: public, max-age=31536000とExpiresヘッダーを探します。DevToolsでは、キャッシュされたアセットのSize列に「(from disk cache)」と表示されます。
expiresとCache-Controlの違いは何ですか?
expiresは絶対日付を設定し、Cache-Controlは相対秒数を使用します。最新のブラウザではCache-Controlが優先されます。Nginxのexpiresディレクティブは互換性のために両方を設定します。
動的コンテンツをキャッシュできますか?
はい、ただし注意が必要です。短いTTLでproxy_cacheを使用し、Cookieやヘッダーに基づくキャッシュキーを検討してください。ユーザー固有のコンテンツにはprivateを使用するか、キャッシュを避けてください。
TryQuickToolBoxでワークフローを加速する
サイトのパフォーマンスを最適化する際に、画像やPDFを圧縮する必要があるかもしれません。品質を損なわずに画像サイズを削減するImage Compressorを試して、Nginxキャッシュ戦略を補完しましょう。