← 一覧へ戻る

Claude が書いた記事

CDN の cache って結局なに見てるん? ── shield と cache key を解体する

Cloudflare をモデルに CDN の内部を解剖した学習ログ。shield / PoP の規模 / cache key / KV の関係を、図 3 枚で 30 秒で復元できるよう整理。

俺の最初の疑問

ニュースで Cloudflare の障害だの「大規模 DDoS を耐えた」だの、よく目にする。 ただ CDN って結局、内部で何をしているのかずっと曖昧やった。

CDN の中で何が起きてんねん? shield と cache って、何を見て何を判断しとるん?

公開情報の厚さで Cloudflare をモデルに、ここを解体する。

まず一言でいうと

CDN は 「世界中の PoP に置いた『でかい KV ストア (cache)』を、shield 役の中間層を挟んで漏斗状に重ねたもの」

入ってきたリクエストから cache key を計算して、その key で KV を引く。 HIT したらキャッシュを返す。MISS したら shield (上位層) に渡す。さらに MISS したら origin に取りに行く。 これだけ。

派手な印象に反して、中身は dict と漏斗 の合わせ技。

何と比べるとわかるか

普通の Web サーバーCDN
受け持つ場所1 拠点世界中の PoP (Cloudflare で約 330 ヶ所)
キャッシュ単一 (なくてもいい)多段 (Edge → Upper-tier → Cache Reserve → Origin)
経路選択クライアントが直接来るAnycast / DNS で最寄り PoP に振り分け
origin 保護自分で TCP 守るupper-tier (shield) が前段で吸収
識別の単位URL でファイルを探すcache key (URL + α) で KV を引く

要するに CDN = 多段キャッシュ + 動的ルーティング。Web サーバーを「重ね合わせて、間を漏斗で繋いだ」構造に近い。

何が問題なのか

CDN を生んだ動機は 2 つだけ。

1. 物理距離

光速の限界で、地球の裏側を往復すると 最低でも 150ms 級 のレイテンシが出る。 ユーザー近くの PoP に持ってこないと Web は遅すぎる。だから「世界中に PoP を置く」。

2. origin への殺到

Edge を世界中に置くと、新着コンテンツの初回アクセスで 数百ヶ所の Edge が同時に origin に殺到する (thundering herd)。 これを防ぐために、上位層 (shield / mid-tier) を間に挟んで漏斗状にする。 origin から見える「リクエスト元の数」が N から 1 まで絞られる ── これが「盾」の効果。

図で見る

Client から Origin までの経路と、shield (= upper-tier) がどこにいるか:

Edge → Origin の直接矢印は存在しない。MISS は必ず Upper-tier (shield) を通る漏斗形になる。Origin から見えるリクエスト元は、その時々の Upper-tier 1 ヶ所だけ ── これが「盾」の効果。

PoP の中身の規模感。Edge と Upper-tier の違いは「役割」ではなく「規模と立地」:

「専用キャッシュサーバー」は存在しない。全サーバーが同じスタック (cache / DNS / WAF / Workers / TLS 終端) を動かす flat な設計。Edge と Upper の違いは規模と立地。

1 リクエストの内部フロー。cache key を「作る」ところ、それを使って cache を「使う / 作る」分岐:

cache key を作るのも使うのも CDN edge software。クライアントは URI を送るだけで cache key の存在を知らない。HIT 経路で「使う」、MISS 経路で origin から取って「作る」。

混乱しやすいポイント

ここからが認知のズレを直すパート。順に潰す。

shield は固定の 1 ヶ所ではない

Fastly や AWS CloudFront は顧客が Origin Shield を 1 つ指定する固定運用。 一方 Cloudflare は Origin Shield という独立機能を持たない。代わりに Tiered Cache の upper-tier がその役を兼ねていて、しかも cache key ごとに upper-tier が動的に選ばれる (Smart Tiered Cache)。

つまり Cloudflare では「shield = 1 ヶ所の関所」ではなく、cache key 単位で別の関所が立つ。

PoP に「専用キャッシュサーバー」は存在しない

Cloudflare の設計哲学は “every server runs every service”。 1 つの PoP の中の全サーバーが同じスタック (cache / DNS / WAF / Workers / TLS 終端) を動かしていて、L4 LB (Unimog) で到着パケットを均等分散する。

cache key の hash で責任サーバーが決まる (PoP 内 sharding) ので、ある URL のキャッシュは PoP 内の 1〜数台にだけ存在する。PoP 内にも小さな漏斗構造がある、と思っておくと正確。

cache key の主語を整理

ここが一番ハマる。

主語役割
クライアントURI を送るだけ。cache key の存在も知らない
CDN edge softwarecache key を計算する。KV lookup にも使う。作るのも使うのも edge software
サイト運営者cache key の 計算ルール (Cache Rules / Custom Cache Key) を設定する。計算自体には関与しない

cache key は CDN の内部概念。クライアントには見えない。

URI と cache key は別物

cache key は URI を素材にして作るが、URI そのものではない。

  • 同じ URI でも Cookie やヘッダを cache key に含める設定 をすれば別キャッシュ (ユーザーごとに別)
  • 違う URI でも utm_source のような query を計算から除外する設定 をすれば同じキャッシュ

「URI ⊆ cache key の入力」。デフォルトでは URI ≒ cache key だが、設定でズレる。

cache key は文字列だが、内部は固定長 hash

論理表現: "https://example.com/article/123?ref=twitter" 物理表現: 0xa3f1e8b7... (例えば 32 byte の hash)

固定長 hash にする理由は 3 つ:

  1. 比較が一瞬で終わる (1〜2 命令で済む、可変長文字列だとループ)
  2. PoP 内 sharding が均等になる (hash は統計的に均等分布)
  3. メモリ・ディスク効率が良い (200 byte の URL → 32 byte の hash)

cache 本体は KV ストアそのもの

CDN の cache は概念的に でかい dict

cache = {
    "0xa3f1e8b7...": {
        "response": <HTTP response body + headers>,
        "metadata": {
            "stored_at":   "2026-05-26T12:34:56Z",
            "ttl_seconds": 3600,
            "etag":        "W/\"abc123\"",
        }
    },
    ...
}

実装上は LRU 退避 / TTL 切れ / SSD spillover / サーバー間分散があるが、抽象としては dict。 ちなみに Cloudflare の “Workers KV” は別物の顧客向け製品。文脈で判別する必要がある。

TTL は cache key には入っていない

TTL を key に混ぜると、時刻で key が変わって永遠に MISS。 TTL は value 側のメタ情報 として格納される。

lookup は 2 段階:

  • ステップ A: cache key で「どのエントリを引くか」を決定
  • ステップ B: 引いたエントリの TTL を見て「新鮮か」を判定

そして「そもそもキャッシュ対象か」は ステップ A 以前Cache-Control や Cache Rules によって判定される。3 層に分離している。

cf-cache-status で各層が透ける

意味
MISSステップ A で key が無かった
HITステップ A で key あり、ステップ B でも新鮮
EXPIREDA で key あり、B で TTL 切れ。origin に取り直した
REVALIDATEDEXPIRED 後、origin が 304 Not Modified を返したので value を再利用
BYPASSA 以前で「そもそもキャッシュしない」判定

cf-cache-status を読めば、どの層で何が起きたかが見える。デバッグはまずこのヘッダから。

たとえ話

  • shield = 城門。城の周りには無数の道があるが、外からの来訪者 (リクエスト) は 1 つの門 (upper-tier) を通ってしか城内 (origin) に届かない。
  • cache key = 倉庫の棚の住所。同じ住所の箱は同じ棚。同じ住所の箱が一個も無ければ外に取りに行って、その住所に置く。
  • cache = でかい dict。Python の dict にサイズ制限と TTL と分散を足したもの。だいたいそれ。
  • PoP = 同じ機械を大量に並べたデータセンター。Edge も Upper も「特別な装置」ではなく、ハードの数違い。

ニュースを読むための変換表

ニュースの言葉つまり何の話?
Cloudflare の障害edge software のバグ・設定ミス・ネットワーク経路問題が世界規模で同時発火する CDN 構造の代償
大規模 DDoS を耐えたedge で攻撃を吸収して origin に届かせなかった話。盾の効果が見えた瞬間
Cache hit rateorigin に届かず edge で返せた割合。CDN の費用対効果の主指標
Tiered Cacheshield 役の upper-tier を入れる Cloudflare の機能名
Cache ReserveLRU で消えない永続キャッシュ層 (R2 ベース)。長尾コンテンツ用
Argo Smart Routingorigin への経路を Cloudflare の網内で最適化する有料機能
Workersedge で動くカスタムコード。cache lookup の前後に割り込める
Anycast「同じ IP を世界中に配って、BGP で最寄りに届ける」ネット技術。CDN の入り口の仕組み
QuicksilverCloudflare 内製の KV 配布システム。purge を世界に数秒で伝播する裏方
次に答える、設計者の問い で、それがなんやねん?

shield と cache key の中身は割れた。次は現場の俺として「で、どう設計を選ぶんや?」。

  • 配信を設計するとき、shield (upper-tier) を効かせるか、ヒット率を捨てて origin に近づけるか。 判断軸は「origin の処理能力 × ヒット率 × cache key の切り方」。cache key に Cookie を含めた瞬間ヒット率が落ちる、まで込みで選べるか。
  • 責任境界:どこまでが CDN (edge) の責任で、どこからが origin の責任か。 障害が出たとき cf-cache-status のどの値なら CDN 側、どこから origin 側か、切り分けられるか。

これに答えられると強い。「CDN の中身を語れる」の一歩先 ── ヒット率と origin 保護を天秤にかけて配信を設計できる側に立てる。普段触る Front Door でも迫られる同じ判断や。

深掘りメニュー 次におすすめのトピック

この記事の続きとして、未来の自分が次に掘るといいトピック。

  • Quicksilver (設定の世界配信) ── purge やルールを数秒で全 PoP に配る KV 配布システム。CDN の”制御面”がなぜ速いかの核。
  • Pingora (Cloudflare の Rust 製 proxy) ── NGINX を置き換えた自社プロキシ。なぜ書き直したかを追うと CDN のボトルネックが見える。
  • PoP 内 sharding の hash (consistent hash / rendezvous) ── 1 拠点の中でどのサーバーがどの URL を持つか。キャッシュ分散の基礎アルゴリズム。
  • Workers (エッジで動くコード) ── cache lookup の前後どこに割り込むか。CDN が”実行環境”に化ける話。
  • Akamai との設計比較 ── Tiered Distribution との思想の違い。CDN 各社の設計判断を相対化できる。