WithCodeMedia-1-pc
previous arrowprevious arrow
next arrownext arrow

WithCodeMedia-1-sp
previous arrowprevious arrow
next arrownext arrow

ETagでブラウザキャッシュ最適化|Cache-Control・条件付きリクエスト・CDN実装ガイド

この記事でわかること

  • ETag・Last-Modified・Cache-Control・Expiresの違いと使い分け
  • Cache-Controlディレクティブ(max-age・no-cache・no-store・immutable等)の全解説
  • stale-while-revalidateによる速度と鮮度の両立方法
  • Node.js(Express)・Hono・Nginx・Next.js App Routerでの実装例
  • CDNキャッシュ・Varyヘッダー・Cache-Busting戦略の実践的な設計
生徒

サイトを更新したのにキャッシュが残っていてユーザーに古いページが表示されたり、逆に毎回全部読み込んで遅かったりします……ブラウザキャッシュって難しいですね

ペン博士

ブラウザキャッシュの仕組みを正しく理解すれば、「変更があれば最新版を取得、なければキャッシュを使う」という理想的な動作を実現できるんじゃ!ETagとCache-Controlを使い分けて、パフォーマンスとコンテンツの鮮度を両立させよう!さらにCDNキャッシュ・stale-while-revalidate・Nginx/Next.jsの実装まで徹底解説するぞ!

ブラウザキャッシュの最適解は「静的アセットは1年間immutableキャッシュ・HTMLはETagで毎回確認・機密データはno-store」という3原則です。これを押さえると、2回目以降のアクセスで転送量を99%以上削減しながら常に最新コンテンツを届けられます。

本記事では、ブラウザキャッシュの4つのHTTPヘッダー(ETag・Last-Modified・Cache-Control・Expires)の仕組みと使い分け・Cache-Control全ディレクティブ詳解・stale-while-revalidate・条件付きリクエストの動作フロー・CDNキャッシュとVaryヘッダー・Cache-Busting戦略・Node.js/Express/Hono/Nginx/Next.js/Vercelでの実装例・DevToolsデバッグ方法を完全解説します。


目次

ブラウザキャッシュの4つのHTTPヘッダー

ヘッダー種類動作特徴推奨用途
Expires強制キャッシュ期限内はHTTPリクエストを送らない絶対日付指定・サーバー時刻依存(旧式)後方互換性のみ
Cache-Control強制キャッシュmax-age秒の間はHTTPリクエストを送らない複数ディレクティブで柔軟に設定全リソース
Last-Modified条件付きキャッシュIf-Modified-Since で変更確認後キャッシュ or 取得秒単位の精度・ファイルシステムの更新日時シンプルな静的サーバー
ETag条件付きキャッシュIf-None-Match でハッシュ照合後キャッシュ or 取得高精度(コンテンツハッシュベース)動的コンテンツ・API
>【強制キャッシュ vs 条件付きキャッシュの違い】

強制キャッシュ(Cache-Control: max-age=3600):
1. ブラウザがキャッシュの有効期限を確認
2. 有効期限内 → サーバーに一切通信しない(ネットワーク0リクエスト)
3. 有効期限切れ → 条件付きリクエスト or 通常リクエスト

条件付きキャッシュ(ETag + Cache-Control: no-cache):
1. 毎回サーバーにリクエスト(但し If-None-Match ヘッダー付き)
2. サーバーがETagを比較
3. 変更なし → 304(ボディなし、数十バイトのレスポンス)
4. 変更あり → 200 + 新しいコンテンツ

→ 静的アセット:強制キャッシュ(max-age=1年)が最高パフォーマンス
→ HTML・API:条件付きキャッシュ(no-cache + ETag)で鮮度を保つ

ETagの仕組みを詳しく理解する

ETagの動作フロー(初回〜変更検知まで)

>【ETagの動作フロー詳細】

━━━ 初回アクセス ━━━
ブラウザ  →  GET /api/products HTTP/1.1
              Host: example.com
              Accept: application/json

サーバー  →  HTTP/1.1 200 OK
              Content-Type: application/json
              ETag: "d41d8cd98f00b204e9800998ecf8427e"
              Cache-Control: no-cache
              Content-Length: 1234

              {"products": [...]}

ブラウザ:レスポンスとETag値をキャッシュに保存

━━━ 2回目のアクセス(条件付きGET)━━━
ブラウザ  →  GET /api/products HTTP/1.1
              Host: example.com
              If-None-Match: "d41d8cd98f00b204e9800998ecf8427e"

サーバー(変更なし)  →  HTTP/1.1 304 Not Modified
                          ETag: "d41d8cd98f00b204e9800998ecf8427e"
                          (ボディなし → 数十バイトのみ転送)

ブラウザ:キャッシュから以前のレスポンスを使用

━━━ 3回目のアクセス(コンテンツ変更後)━━━
ブラウザ  →  GET /api/products HTTP/1.1
              If-None-Match: "d41d8cd98f00b204e9800998ecf8427e"  ← 古いETag

サーバー(変更あり)  →  HTTP/1.1 200 OK
                          ETag: "new_hash_value_xyz"  ← 新しいETag
                          Content-Length: 2345

                          {"products": [...updated...]}

ブラウザ:新しいコンテンツと新しいETagでキャッシュを更新

強いETag(Strong ETag)と弱いETag(Weak ETag)

>【Strong ETag vs Weak ETag】

Strong ETag(強いETag):
ETag: "abc123def456"
→ バイト単位で完全一致の場合のみ同一とみなす
→ Range リクエスト(部分取得)にも使用可能
→ 推奨:精度が高く信頼性が高い

Weak ETag(弱いETag):
ETag: W/"abc123def456"
→ セマンティックに同等であれば同一とみなす
→ 例:HTTP vs HTTPS、gzip圧縮有無の違いを無視
→ 用途:コンテンツは同じだが表現が異なる場合

【ETagの生成方法例】

// ファイルの内容ハッシュ(推奨)
const etag = crypto.createHash('sha256').update(fileContent).digest('hex')
// → "e3b0c44298fc1c149afbf4c8996fb924..."

// ファイルサイズ + 最終更新時刻(Nginx/Apacheのデフォルト)
const etag = `"${fileSize.toString(16)}-${mtime.toString(16)}"`
// → "1a2b-5d3c4e8f9a"

// バージョン番号(APIレスポンス向け)
const etag = `"v${version}-${lastModifiedTimestamp}"`
// → "v42-1710000000000"

Cache-Controlディレクティブ完全リファレンス

ディレクティブ説明対象
max-age秒数指定秒数の間は再リクエストしない(Expiresより優先)ブラウザ・CDN
s-maxage秒数共有キャッシュ(CDN)の有効期限(max-ageより優先)CDNのみ
no-cacheキャッシュは保存するが毎回サーバーに確認(ETagと組み合わせ)ブラウザ・CDN
no-storeキャッシュを一切保存しない(最も厳格)ブラウザ・CDN
publicブラウザ・CDN両方にキャッシュを許可CDN
privateブラウザのみキャッシュ可(CDNはキャッシュ不可)CDN除外
immutablemax-age内でも再確認リクエストを送らない(Firefox/Chrome対応)ブラウザ
stale-while-revalidate秒数max-age後もこの秒数間はキャッシュを返しながらバックグラウンドで更新ブラウザ・CDN
stale-if-error秒数サーバーエラー時にこの秒数間はキャッシュを返すCDN
must-revalidate期限切れキャッシュはサーバー確認が必須ブラウザ

stale-while-revalidate|ユーザー体験を損なわないキャッシュ戦略

stale-while-revalidate は、キャッシュが期限切れでもユーザーに即座に古いデータを返しながら、バックグラウンドで新しいデータを取得するディレクティブです。「速さ」と「鮮度」を両立できる強力な戦略です。

>【stale-while-revalidate の動作フロー】

Cache-Control: max-age=60, stale-while-revalidate=3600

→ 最初の60秒:キャッシュから即座に返す(リクエストなし)
→ 61秒〜3660秒:キャッシュから古いデータを返す + バックグラウンドで更新
→ 3661秒以降:通常のキャッシュミス(サーバーに同期リクエスト)

【使用例(1時間ごとに変わるニュースサイト)】
Cache-Control: public, max-age=3600, stale-while-revalidate=86400

→ 1時間以内:キャッシュから即座に返す
→ 1時間〜24時間:古いキャッシュを返しながらバックグラウンドで最新取得
→ ユーザーは常に高速なレスポンスを体験できる
>// Hono(Node.js/Edge Runtime対応)でのstale-while-revalidate実装
import { Hono } from 'hono'
import { cache } from 'hono/cache'

const app = new Hono()

// APIレスポンスにキャッシュヘッダーを設定
app.get('/api/news', async (c) => {
  const data = await fetchLatestNews()

  return c.json(data, 200, {
    'Cache-Control': 'public, max-age=3600, stale-while-revalidate=86400',
    'ETag': generateEtag(data),
    'Last-Modified': new Date().toUTCString(),
    'Vary': 'Accept-Language',  // 言語別にキャッシュを分ける
  })
})

// 条件付きリクエストの処理
app.get('/api/products', async (c) => {
  const data = await fetchProducts()
  const json = JSON.stringify(data)
  const etag = `"${await generateHash(json)}"`

  // If-None-Match チェック
  const clientEtag = c.req.header('if-none-match')
  if (clientEtag === etag) {
    return new Response(null, {
      status: 304,
      headers: { 'ETag': etag }
    })
  }

  // If-Modified-Since チェック(フォールバック)
  const lastModified = new Date('2026-03-14').toUTCString()
  const ifModifiedSince = c.req.header('if-modified-since')
  if (ifModifiedSince && new Date(ifModifiedSince) >= new Date(lastModified)) {
    return new Response(null, { status: 304 })
  }

  return c.json(data, 200, {
    'ETag': etag,
    'Last-Modified': lastModified,
    'Cache-Control': 'no-cache',
  })
})

export default app

目的別のキャッシュ設定パターン集

>【リソース種別ごとの推奨キャッシュ設定パターン】

━━━ パターン1:静的アセット(ハッシュ付きファイル名)━━━
対象:main.a1b2c3d4.js, styles.e5f6g7h8.css, image.webp
設定:Cache-Control: public, max-age=31536000, immutable
理由:URLにハッシュが含まれるため、コンテンツ変更時はURLが変わる
     → 同じURLは永遠に同じコンテンツ(immutableで確認リクエストも省略)

━━━ パターン2:HTMLドキュメント ━━━
対象:index.html, /about, /blog/post-1
設定:Cache-Control: no-cache
     ETag: "abc123"
理由:ユーザーが常に最新のHTMLを見る必要がある
     → 毎回確認するが変更がなければ304(高速)

━━━ パターン3:APIレスポンス(頻繁に変わるデータ)━━━
対象:/api/cart, /api/user, /api/notifications
設定:Cache-Control: no-store
理由:ユーザー固有・リアルタイムデータは絶対にキャッシュしてはいけない

━━━ パターン4:APIレスポンス(変化が少ないデータ)━━━
対象:/api/products, /api/categories
設定:Cache-Control: public, max-age=300, stale-while-revalidate=3600
     ETag: "xyz789"
理由:5分のキャッシュで多くのリクエストをオフロード
     SWRで期限切れ後もUIはサクサク動作

━━━ パターン5:認証済みページ ━━━
対象:/dashboard, /profile, /orders
設定:Cache-Control: private, no-cache
     ETag: "user123-content-hash"
理由:private でCDNにキャッシュさせない
     no-cache + ETag で毎回確認(personalized content)

━━━ パターン6:画像・動画(CDN配信)━━━
対象:/images/hero.jpg, /uploads/video.mp4
設定:Cache-Control: public, max-age=604800, stale-while-revalidate=86400
理由:画像は1週間キャッシュ。変更時はURLを変更してキャッシュバスト

サーバー実装例|Node.js(Express)・Hono・Nginx・Next.js

Node.js(Express)でのETag実装

>// Express での ETag + Cache-Control 実装
import express from 'express'
import { createHash } from 'crypto'
import { readFileSync, statSync } from 'fs'

const app = express()

// Expressはデフォルトで etag: 'weak' が有効
// Strong ETAGに変更
app.set('etag', 'strong')

// 静的ファイルに最適なキャッシュヘッダーを付与
app.use('/static', express.static('public', {
  etag: true,
  lastModified: true,
  // ハッシュ付きファイル名の場合は1年キャッシュ
  setHeaders: (res, path) => {
    if (path.match(/\.[a-f0-9]{8,}\.(js|css|png|jpg|webp)$/)) {
      res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
    } else {
      res.setHeader('Cache-Control', 'public, max-age=86400')
    }
  },
}))

// 動的APIエンドポイントでETagを手動設定
app.get('/api/products', async (req, res) => {
  const products = await db.getProducts()
  const json = JSON.stringify(products)

  // コンテンツのSHA-256ハッシュからETagを生成
  const hash = createHash('sha256').update(json).digest('hex')
  const etag = `"${hash}"`

  // If-None-Match ヘッダーと照合
  const clientEtag = req.get('if-none-match')
  if (clientEtag === etag) {
    return res.status(304).set({
      'ETag': etag,
      'Cache-Control': 'no-cache',
    }).end()
  }

  // Last-Modified の確認
  const lastModified = new Date(products[0]?.updatedAt || Date.now()).toUTCString()
  const ifModifiedSince = req.get('if-modified-since')
  if (ifModifiedSince && new Date(ifModifiedSince) >= new Date(lastModified)) {
    return res.status(304).end()
  }

  res
    .set('ETag', etag)
    .set('Last-Modified', lastModified)
    .set('Cache-Control', 'no-cache')
    .set('Vary', 'Accept-Encoding')
    .json(products)
})

Next.js App Router でのキャッシュヘッダー設定

>// app/api/products/route.ts - Next.js App Router

import { NextRequest, NextResponse } from 'next/server'
import { createHash } from 'crypto'

export async function GET(request: NextRequest) {
  const products = await fetchProducts()
  const json = JSON.stringify(products)
  const etag = `"${createHash('sha256').update(json).digest('hex')}"`

  // 条件付きリクエストの処理
  const ifNoneMatch = request.headers.get('if-none-match')
  if (ifNoneMatch === etag) {
    return new NextResponse(null, {
      status: 304,
      headers: { 'ETag': etag }
    })
  }

  return NextResponse.json(products, {
    headers: {
      'ETag': etag,
      'Cache-Control': 'public, max-age=60, stale-while-revalidate=300',
    }
  })
}

// next.config.js - 静的ファイルのキャッシュヘッダー設定
/** @type {import('next').NextConfig} */
const nextConfig = {
  async headers() {
    return [
      {
        // ハッシュ付き静的ファイルは永久キャッシュ
        source: '/_next/static/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, max-age=31536000, immutable',
          },
        ],
      },
      {
        // HTMLページはno-cache(ETagで確認)
        source: '/((?!_next).*)',
        headers: [
          {
            key: 'Cache-Control',
            value: 'no-cache',
          },
        ],
      },
    ]
  },
}

module.exports = nextConfig

Nginx での詳細設定

># /etc/nginx/conf.d/app.conf
# Nginxの完全なキャッシュ設定例

server {
    listen 443 ssl http2;
    server_name example.com;

    # ETag を有効化(Nginxはデフォルトで有効)
    etag on;

    # ハッシュ付き静的アセット(JS・CSS・フォント)は1年キャッシュ
    location ~* \.(js|css|woff2?|ttf|otf)$ {
        # ハッシュが含まれているURLパターン(例: main.a1b2c3.js)
        if ($uri ~* "\.[a-f0-9]{8,}\.") {
            add_header Cache-Control "public, max-age=31536000, immutable";
            break;
        }
        # ハッシュがない場合は1日キャッシュ
        add_header Cache-Control "public, max-age=86400";
    }

    # 画像は1週間キャッシュ
    location ~* \.(png|jpg|jpeg|gif|webp|avif|svg|ico)$ {
        expires 7d;
        add_header Cache-Control "public, max-age=604800, stale-while-revalidate=86400";
    }

    # HTMLは no-cache(ETagで毎回確認)
    location ~* \.html$ {
        add_header Cache-Control "no-cache";
        # ETagはNginxが自動で付与する
    }

    # ルートと動的ルートはno-cache
    location / {
        add_header Cache-Control "no-cache";
        try_files $uri $uri/ /index.html;
    }

    # APIはキャッシュしない
    location /api/ {
        add_header Cache-Control "no-store";
        add_header Pragma "no-cache";
        proxy_pass http://backend:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # バックエンドのETagをそのままパススルー
        proxy_pass_header ETag;
        proxy_pass_header Last-Modified;
    }
}

# キャッシュの圧縮設定(gzip/brotli)
gzip on;
gzip_vary on;  # Vary: Accept-Encoding ヘッダーを追加(CDN対応)
gzip_types text/plain application/json application/javascript text/css;

CDNキャッシュとVaryヘッダー

CDN(Cloudflare, CloudFront, Fastly等)は独自のキャッシュレイヤーを持っています。VaryヘッダーはCDNがキャッシュを分けるための判断基準として機能します。

Vary値用途注意点
Accept-Encodinggzip/brotli圧縮有無でキャッシュ分離ほぼ必須。設定漏れで圧縮済みコンテンツを非対応ブラウザに返す可能性
Accept-Language言語別キャッシュ分離多言語サイトで同一URLが言語によって内容が変わる場合
Cookieユーザー別キャッシュ分離CDNには非推奨。全Cookieパターンをキャッシュしてヒット率が激減する
Authorization認証トークン別キャッシュCDNには非推奨。private設定と組み合わせて使う
>【CDNキャッシュの設定例(Cloudflare)】

# Cloudflare Page Rule または Cache Rules
# *.js, *.css → Cache Everything, Edge TTL: 1 year
# /api/* → Bypass Cache

# Cache-Control の s-maxage でCDNのみの有効期限を設定
Cache-Control: public, max-age=60, s-maxage=3600, stale-while-revalidate=300

→ ブラウザ:60秒キャッシュ
→ CDN:1時間キャッシュ
→ CDNキャッシュ期限後:古いデータを返しつつ5分間バックグラウンド更新

Cache-Busting(キャッシュ無効化)戦略

方法メリットデメリット
ファイル名にハッシュ(推奨)main.a1b2c3.jsmain.e5f6g7.jsURLが変わるので確実。CDNも自動対応ビルドツールが必要(Vite/webpack)
クエリパラメーターstyle.css?v=1.2.3実装が簡単CDNがクエリを無視する場合がある
CDNキャッシュパージAPICloudflare/CloudFront APIでパージ即時反映。URLを変えずに使えるデプロイパイプラインの構築が必要
># Cloudflare Cache Purge(デプロイスクリプト例)
curl -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/purge_cache" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" \
  -H "Content-Type: application/json" \
  --data '{"purge_everything": true}'

Chrome DevToolsでキャッシュをデバッグする

>【DevToolsでのキャッシュデバッグ手順】

1. Chrome DevToolsを開く(F12 or Ctrl+Shift+I)

2. Networkタブを選択

3. キャッシュの確認方法:
   → Status列で確認
   → 200(disk cache): ブラウザのディスクキャッシュから返された
   → 200(memory cache): ブラウザのメモリキャッシュから返された
   → 304: 条件付きリクエストで変更なし(ETag照合成功)
   → 200: 通常のリクエスト(キャッシュなし)

4. ETagの確認:
   → リクエストをクリック
   → Response Headers タブで ETag の値を確認
   → Request Headers タブで If-None-Match の値を確認

5. キャッシュを無効にしてテスト:
   → "Disable cache"チェックボックスをON(DevToolsが開いている間のみ)
   → または Ctrl+Shift+R でハードリロード

6. Lighthouseでキャッシュ効率を測定:
   → Audits タブ → "Uses efficient cache policy on static assets"
   → 長期キャッシュが設定されていないリソースの一覧が表示される

パフォーマンスへの影響測定

>【キャッシュ設定前後のパフォーマンス比較例】

設定前(キャッシュなし):
初回アクセス:
  GET /app.js → 200 (320KB, 850ms)
  GET /styles.css → 200 (45KB, 320ms)
  GET /hero.webp → 200 (180KB, 1200ms)
  合計転送量:545KB、合計時間:2370ms

2回目アクセス:
  GET /app.js → 200 (320KB, 800ms) ← 全部再取得
  GET /styles.css → 200 (45KB, 310ms)
  合計:変わらず重い

━━━━━━━━━━━━━━━━━━━━━

設定後(適切なキャッシュ):
初回アクセス:
  GET /app.a1b2c3.js → 200 (320KB, 850ms)
  GET /styles.d4e5f6.css → 200 (45KB, 320ms)
  GET /hero.webp → 200 (180KB, 1200ms)
  合計転送量:545KB(初回は変わらない)

2回目アクセス:
  GET /app.a1b2c3.js → 200 (from disk cache, 0ms, 0bytes)
  GET /styles.d4e5f6.css → 200 (from disk cache, 0ms, 0bytes)
  GET /hero.webp → 200 (from disk cache, 0ms, 0bytes)
  GET /index.html → 304 (Not Modified, 18ms, 200bytes)
  合計転送量:200bytes(99.9%削減!)、合計時間:18ms

よくある質問(FAQ)

Q1. Vite/Next.jsでビルドすると自動でキャッシュ設定されますか?

A. Viteはビルドで assets/main-abc123.js のようにファイル名にハッシュを付与します。このため、コンテンツが変わればURL自体が変わるので「同一URLは永久にキャッシュして良い(immutable)」という設定が可能です。サーバー側で Cache-Control: public, max-age=31536000, immutable を設定するのがベストプラクティスです。ただし、ビルドツールはファイルを生成するだけで、HTTPヘッダーの設定はサーバー(Nginx/Cloudflare/Vercel)側で行う必要があります。

Q2. 強いETag(Strong ETag)と弱いETag(Weak ETag)はどちらを使うべきですか?

A. 通常は強いETag"abc123")を推奨します。バイト単位で同一の場合のみ一致するため、精度が高く信頼性があります。弱いETag(W/"abc123")はセマンティックに同等であれば一致するため、HTTPとHTTPS間やgzip圧縮の有無など「内容は同じだが表現が異なる」ケースに使います。NginxはデフォルトでファイルサイズとMtimeから弱いETagを生成します。コンテンツハッシュを使って独自生成する場合は強いETagにしてください。

Q3. Cache-Control: no-cache と no-store の違いは何ですか?

A. これはよく混同されるポイントです。no-cacheは「キャッシュを保存するが毎回サーバーに確認する」という意味です(名前に反して、キャッシュを使わないわけではありません)。no-storeは「キャッシュを一切保存しない」という意味で、毎回サーバーからフルダウンロードします。HTMLやAPIレスポンスで「常に最新を確認したいが帯域は節約したい」場合は no-cache + ETag を使いましょう。個人情報や機密データは no-store を使います。

Q4. stale-while-revalidate を使うと古いデータがいつまでも表示されませんか?

A. stale-while-revalidate の第二値で「古いデータを返す最大期間」を指定します。例えば max-age=60, stale-while-revalidate=300 なら、最大5分間は古いデータを返しますが、バックグラウンドで新しいデータを取得するので次のリクエストからは最新データが返ります。SEOや公式発表など「即時最新を表示すべき」コンテンツには向きませんが、一般的なリスト表示や参照データには最適です。

Q5. CDNを使っている場合にETagは機能しますか?

A. CDNがレスポンスをキャッシュしている場合、CDNがETagをブラウザに転送するかどうかは設定によります。Cloudflareは通常ETagをそのままブラウザに転送します。CDNキャッシュがある場合、ブラウザは If-None-Match をCDNに送り、CDNがオリジンサーバーに転送するかどうかはCDNの設定次第です。CDNは Vary: Accept-Encoding の設定が重要で、これがないとgzip圧縮済みのキャッシュを非対応ブラウザに返してしまうことがあります。


まとめ

  • ETag:リソースのコンテンツハッシュ。If-None-Match で変更確認し、変更なしは304を返して帯域を節約する。
  • Cache-Control:max-ageで「何秒間リクエストを送らないか」を制御。ディレクティブの組み合わせで細かく制御可能。
  • stale-while-revalidate:速度と鮮度を両立する強力なディレクティブ。期限切れ後もキャッシュを返しながらバックグラウンドで更新。
  • 用途別の使い分け:静的ファイルはハッシュ+1年immutable、HTMLはno-cache+ETag、センシティブデータはno-store。
  • CDNキャッシュ:s-maxageでCDN専用の有効期限を設定。Varyヘッダーでキャッシュの分岐条件を指定する。
  • Cache-Busting:ファイル名にコンテンツハッシュを含める方法が最も確実。
  • Nginx実装:etag on が基本設定。URLパターンによってCache-Controlを出し分ける。
  • デバッグ:DevToolsのNetworkタブでステータス・ETagヘッダーを確認。Lighthouseでキャッシュ効率を測定。

ブラウザキャッシュはパフォーマンスに直結する重要な設定です。「変えないものは長期キャッシュ(immutable)・変わりうるものはETagで確認(no-cache)・ユーザー固有データはキャッシュしない(no-store)」という3つの原則で設計すると、帯域幅の節約とコンテンツの鮮度を両立できます。


関連記事


参考リンク(公式・一次情報)


WithCodeを体験できる初級コース公開中!

WithCodeを体験できる初級コース公開中!

初級コース(¥49,800)が完全無料に!

  • 期間:1週間
  • 学習内容:
    ロードマップ/基礎知識/環境構築/HTML/CSS/LP・ポートフォリオ作成
    正しい学習方法で「確かな成長」を実感できるカリキュラム。

副業・フリーランスが主流になっている今こそ、自らのスキルで稼げる人材を目指してみませんか?

未経験でも心配することはありません。初級コースを受講される方の大多数はプログラミング未経験です。まずは無料カウンセリングで、悩みや不安をお聞かせください!

この記事を書いた人

WithCode(ウィズコード)は「目指すなら稼げる人材」をビジョンに、累計400名以上のフリーランスを輩出してきた超実践型プログラミングスクールです。150社以上の実案件支援を特徴にWeb制作・Webデザインなどの役立つ情報を現場のノウハウに基づいて発信していきます。

– service –WithGroupの運営サービス

  • WithCode
    - ウィズコード -

    スクール

    「未経験」から
    現場で通用する
    スキルを身に付けよう!

    詳細はこちら
  • WithFree
    - ウィズフリ -

    実案件サポート

    制作会社のサポート下で
    実務経験を積んでいこう!

    詳細はこちら

公式サイト より
今すぐ
無料カウンセリング
予約!

目次