2026年8月14日 金曜日
AI時短ラボ
活用· 約32

Wayback CDX APIは「該当なし」を200・0バイトで返す──既定が完全一致なのを知らずに『誰も保存していない』と結論した話

Wayback Machine の CDX Server API は、該当が無いときエラーもJSONも返さず HTTP 200・本文0バイト・curl終了コード0で終わる。さらに url= の既定マッチは完全一致なので、2026年8月14日に url=higgsfield.ai を投げると439行返るが439行すべてがルート(パス /)で、matchType=domain&collapse=urlkey に変えると同じ日に322,517行になった。0行を「アーカイブされていない」と読まないための書き方を実測ログ付きでまとめる。

執筆・編集:
目次

Wayback Machine の CDX Server API は、条件に合う記録が1件も無いとき、エラーもJSONも返さない。HTTP 200・本文0バイト・curlの終了コード0で終わる。加えて url= の既定のマッチ方式は完全一致(matchType=exact)なので、ドメイン配下を調べるつもりで url=example.com とだけ書くと、返るのはルートURLの記録だけになる。この2つが重なると「このサイトはアーカイブされていない」という誤った結論が立つ。対処は3つで、matchType= を必ず明示するoutput=json を付けて0バイトではなく [] を受け取るHTTPステータスと行数を常に併記して記録するである。0バイトの本文は「該当なし」とも「パラメータ不正」とも「サーバ過負荷」とも読めるため、本文の長さだけで判断してはいけない。

  1. 該当が無いとき CDX API は HTTP 200・0バイトを返し、curlは終了コード0で正常終了する。エラー文字列が出ないので grep でも気づけない(2026年8月14日実測)
  2. 既定は完全一致。 url=higgsfield.ai は439行返したが、439行すべてがパス / だった。同じURLに matchType=domain&collapse=urlkey を付けると322,517行、matchType=prefix&collapse=urlkey で178,932行になった
  3. output=json を付けると、該当なしの応答は0バイトではなく 3バイトの [] になる。パーサに渡せる形になるので、0行の見落としが起きにくい

症状:エラーが出ないので、検索する文字列すら存在しない

2026年8月14日、Higgsfield の利用規約ページがいつ保存されたかを調べようとして、次を実行した。

$ curl -sS -o out.txt \
  -w "http_code=%{http_code} size_download=%{size_download}\n" \
  "https://web.archive.org/cdx/search/cdx?url=higgsfield.ai/terms"
http_code=200 size_download=0

$ echo $?
0

$ wc -c < out.txt
0

200が返り、curlは終了コード0で成功し、本文は0バイト。 例外も警告もスタックトレースも無い。この記事のような「エラーメッセージで検索する」記事が普段成立するのは、貼り付けられる文字列があるからだが、ここには貼り付けるものが何も無い。それがこの罠の厄介なところである。

このとき私は「Higgsfield は利用規約ページをアーカイブされていない」と結論しかけた。それは誤りだった。

同じ日、パスを変えて投げ直すと記録は存在した。

$ curl -sS -o out2.txt \
  -w "http_code=%{http_code} size_download=%{size_download}\n" \
  "https://web.archive.org/cdx/search/cdx?url=higgsfield.ai/terms-of-use-agreement&fl=timestamp"
http_code=200 size_download=660

$ wc -l < out2.txt
      44

実際のパスは /terms ではなく /terms-of-use-agreement で、44件の記録が残っていた。最も古い記録は 20240413223203、最も新しい記録は 20260807225857 である。0行が意味していたのは「保存されていない」ではなく「私が指定した文字列と完全一致するURLは無い」だった。

紛らわしい応答ヘッダがある(これは該当なしの印ではない)

該当なしの応答のヘッダを見ると、nomatch という語が2か所に出てくる。

HTTP/2 200
content-type: text/plain
set-cookie: wb-cdx-nomatch-SERVER=wwwb-app240; path=/
x-location: cdx
x-be: wb_cdx_pygwb_nomatch

x-be: wb_cdx_pygwb_nomatch は「該当なしを示すフラグ」に見える。しかしこれは該当なしの印ではない。 2026年8月14日16時33分〜16時38分(日本時間)に、該当ありのクエリ2件と該当なしのクエリ2件を交互に投げてヘッダを比べたところ、4件すべてが同じ x-be: wb_cdx_pygwb_nomatch を返した(wwwb-app217 / wwwb-app251 / wwwb-app23 / wwwb-app240)。差が出たのは本文のバイト数だけで、30バイト(該当あり)と0バイト(該当なし)だった。バックエンドのプール名がたまたま nomatch を含んでいるだけと考えられる。このヘッダを判定に使うと、該当ありの応答まで「該当なし」と誤判定する。

原因1:matchType の既定は完全一致

公式のドキュメントは Internet Archive の GitHub リポジトリ internetarchive/waybackwayback-cdx-server/README.md にある。「Url Match Scope」の節が該当する(2026年8月14日取得)。

The default behavior is to return matches for an exact url. However, the cdx server can also return results matching a certain prefix, a certain host or all subdomains by using the matchType= param.

(既定の動作は、完全一致するURLの記録を返すことである。ただしCDXサーバは matchType= パラメータを使うことで、特定のプレフィックス、特定のホスト、または全サブドメインに一致する結果を返すこともできる)

同節は archive.org/about/ を例に4つのモードを並べている。

  • matchType=exact (default if omitted) will return results matching exactly archive.org/about/

  • matchType=prefix will return results for all results under the path archive.org/about/

  • matchType=host will return results from host archive.org

  • matchType=domain will return results from host archive.org and all subhosts *.archive.org

matchType=exact(省略時の既定)は archive.org/about/ に完全一致する結果を返す/matchType=prefix はパス archive.org/about/ 配下のすべての結果を返す/matchType=host はホスト archive.org の結果を返す/matchType=domain はホスト archive.org と全サブホスト *.archive.org の結果を返す)

ワイルドカードによる暗黙指定も定義されている。

  • If url is ends in '/*', eg url=archive.org/* the query is equivalent to url=archive.org/&matchType=prefix
  • if url starts with '*.', eg url=*.archive.org/ the query is equivalent to url=archive.org/&matchType=domain

(URLが /* で終わる場合、たとえば url=archive.org/*url=archive.org/&matchType=prefix と等価である/URLが *. で始まる場合、たとえば url=*.archive.org/url=archive.org/&matchType=domain と等価である)

さらに domain モードには条件が付いている。

(Note: The domain mode is only available if the CDX is in SURT-order format.)

(注:domain モードは、CDXがSURT順の形式である場合にのみ利用できる)

原因2:本文の長さは「該当なし」と「パラメータ不正」を区別しない

READMEには、該当が無いときに何が返るかの記述が無い。 「Url Match Scope」「Filtering」「Query Result Limits」いずれの節にも、空応答・0行・該当なしについての記載は見つからなかった(2026年8月14日時点のREADME全文18,292バイトを対象に確認)。つまりこの挙動は、ドキュメントを読んでも分からず、実際に投げて初めて分かる。

そして厄介なことに、パラメータが不正なときも本文は0バイトになる。 違うのはステータスコードである。

$ curl -sS -D - -o /dev/null \
  "https://web.archive.org/cdx/search/cdx?url=higgsfield.ai&matchType=bogus&limit=5"
HTTP/2 400
content-type: text/plain; charset=utf-8
content-length: 0
x-archive-wayback-runtime-error: unsupported matchtype bogus

matchType に定義外の値を渡すと HTTP 400・content-length: 0 で、理由は x-archive-wayback-runtime-error: unsupported matchtype bogus というヘッダにだけ書かれる。本文は空なので、curl -s で本文だけ受け取っているスクリプトからは、該当なし(200)と綴り間違い(400)が完全に同じ「0バイト」に見える。

3つ目の空パターンもある。連続してクエリを投げていた2026年8月14日16時06分〜16時41分の間、同じエンドポイントが断続的に 503(107バイトのHTML)504(160バイトのHTML) を返した。バイト数は固定ではなく、同日の再測で受け取った504は、末尾にパディングのコメント行が付いた562バイトだった。

<html><body><h1>503 Service Unavailable</h1>
No server is available to handle this request.
</body></html>

こちらは0バイトではないが、CDXの行として読むと1行目が <html><body><h1>503 Service Unavailable</h1> になる。行数だけを数える実装だと「3行返ってきた」と誤読する。

状況 HTTPステータス 本文 見分け方
該当なし 200 0バイト ステータス200かつ0バイト
パラメータ不正 400 0バイト x-archive-wayback-runtime-error ヘッダ
サーバ過負荷 503 / 504 HTML(107 / 160 / 562バイトを観測) 本文がCDX形式でない
該当あり 200 1行以上

実測:同じURLでも matchType で桁が変わる

2026年8月14日16時06分〜16時31分(日本時間)に higgsfield.ai に対して投げた結果である。行数は保存したファイルに対する wc -l の値で、いずれも末尾が改行(0a)で終わっていることを確認済みなので、行数は切り捨てられていない。

クエリ ステータス 行数
url=higgsfield.aimatchType省略=exact) 200 439
url=higgsfield.ai&matchType=prefix&collapse=urlkey 200 178,932
url=higgsfield.ai&matchType=domain&collapse=urlkey 200 322,517
url=higgsfield.ai/terms(exact) 200 0
url=higgsfield.ai/terms-of-use-agreement(exact) 200 44
url=higgsfield.ai/terms&matchType=prefix&collapse=urlkey 200 1,236
url=higgsfield.ai&matchType=bogus 400 0

完全一致の439行が「そこそこ返ってきた」ように見えるのが、この罠の入口である。 439行という数字を見ると、ドメイン全体を見ている気になる。しかし439行すべてのパスを取り出して集計すると、/ が439件で、それ以外は0件だった。

$ awk '{print $3}' exact.txt | sed -E 's#^https?://[^/]+##' | sort | uniq -c
 439 /

既定のフィールド順は urlkey timestamp original mimetype statuscode digest length なので、URLが入っているのは3番目である($2 はタイムスタンプで、ここを取り違えるとパスではなく日時の一覧が出る)。

original フィールドの値としては7種類(http://https://www. の有無、Higgsfield.AI のような大文字表記)が混ざっていたが、パスはすべてルートだった。CDXの照合は正規化済みキー(urlkey)で行われるため、スキームや大文字小文字、www. の有無は吸収される。吸収されないのはパスである。

同じ理由で、末尾スラッシュの有無は結果を変えなかった。url=higgsfield.ai/pricing&fl=timestampurl=higgsfield.ai/pricing/&fl=timestamp の応答は、どちらも113行・1,695バイトでMD5まで一致した(d9875866560122406e5c81710fca5782)。このバイト数とMD5は fl=timestamp を付けた場合の値で、フィールドを省略しない既定の応答は同じ113行で13,669バイトになる。「末尾スラッシュを疑う」という定番の対処は、この API では的外れである。 疑うべきはパスそのものと matchType の方だった。

比較として Runway でも同じ形を確認した。url=runwayml.com/terms(exact)は0行だが、matchType=prefix&collapse=urlkey を付けると141行返る(collapse=urlkey を外すと605行。同一URLの複数回の保存が畳まれないためである)。完全一致で0行になるのは Higgsfield 固有の事情ではなく、/terms という短いパスをそのまま持っているサイトが少ないというだけである。

なお pricing の記録数は、url=higgsfield.ai/pricing(exact)が113行、url=runwayml.com/pricing(exact)が272行で、Runway の方が多かった(いずれも2026年8月14日16時19分〜16時21分取得)。

output=json を付けると0バイトが [] になる

同じ「該当なし」のクエリに output=json を足すと、応答は0バイトではなくなる。

$ curl -sS -o j.txt -w "code=%{http_code} bytes=%{size_download}\n" \
  "https://web.archive.org/cdx/search/cdx?url=higgsfield.ai/terms&output=json"
code=200 bytes=3

$ cat j.txt
[]

3バイトの [] が返る。該当ありの場合は先頭にフィールド名の行が入った配列になる。

$ curl -sS "https://web.archive.org/cdx/search/cdx?url=higgsfield.ai/terms-of-use-agreement&output=json&limit=2"
[["urlkey","timestamp","original","mimetype","statuscode","digest","length"],
["ai,higgsfield)/terms-of-use-agreement","20240413223203","https://higgsfield.ai/Terms-of-Use-Agreement","text/html","200","CUUYTYVBE3MRTYBADJOPX2R5ZIVR34HV","40709"],
["ai,higgsfield)/terms-of-use-agreement","20240725003243","https://higgsfield.ai/Terms-of-Use-Agreement","text/html","200","VVTUNIBCBD3YZYGWUOVXKJFA5AORZJA5","69058"]]

urlkey が小文字の ai,higgsfield)/terms-of-use-agreement なのに対し、original は大文字混じりの https://higgsfield.ai/Terms-of-Use-Agreement である。照合に使われるのは前者なので、大文字で保存されたページも小文字のクエリで引ける。

JSONにすると、該当ありは要素数3以上(フィールド名の行を含む)、該当なしは要素数0の配列になり、パーサでそのまま分岐できる。ただし400や503のときは本文が空またはHTMLなので、JSONパースは例外で落ちる。 ステータスコードの確認を省略してよいわけではない。

from=2024 のような4桁指定は黙って0行を返す」は誤り

社内の在庫メモに「from= に4桁の年を渡すとエラーを出さず0行になる」という記述があったが、2026年8月14日の実測でこれは否定された。

url=higgsfield.ai&from=2024&to=2024&limit=10&fl=timestamp,originalurl=higgsfield.ai&from=20240101&to=20241231&limit=10&fl=timestamp,original の応答は、どちらも200・379バイト・10行で、MD5が一致した909efc9f55dbb4474f05bdf22af90a50)。バイト数とMD5は fl=timestamp,original を付けた場合の値である(フィールドを省略しない既定の応答は同じ10行で1,058バイト、こちらも両者一致する)。

READMEも4桁指定を正規の書き方として定義している。

Date Range: Results may be filtered by timestamp using from= and to= params. The ranges are inclusive and are specified in the same 1 to 14 digit format used for wayback captures: yyyyMMddhhmmss

Ex: http://web.archive.org/cdx/search/cdx?url=archive.org&from=2010&to=2011

(日付範囲:from=to= パラメータを使い、タイムスタンプで結果を絞り込める。範囲は両端を含み、wayback のキャプチャで使われるのと同じ1〜14桁の形式 yyyyMMddhhmmss で指定する)

「1〜14桁」と明記されており、README自身の例が4桁の from=2010&to=2011 である。 0行になった経験があるなら、原因は桁数ではなく matchType かパスの側を疑うほうがよい。

対処

  1. matchType= を必ず書く。 省略時の既定が完全一致であることを、コードを読む人が思い出せる形にしておく。ドメイン配下を見たいなら matchType=domain、パス配下なら matchType=prefix
  2. output=json を付ける。 該当なしが0バイトではなく [] になり、「空ファイル」と「取得失敗」を取り違えにくくなる
  3. HTTPステータス・バイト数・行数の3つを常に記録する。 どれか1つでは3種類の空応答(200/400/5xx)を区別できない
  4. 0行を「存在しない」と書かない。 少なくとも matchType=prefix で投げ直してから結論する。/terms が0行でも /terms-of-use-agreement が44件ある、という例が実際に起きる
  5. 正しいパスを先に調べる。 matchType=prefixcollapse=urlkey でパス一覧を取り、そこから完全一致で絞り込む
  6. 5xxはリトライする。 大きいクエリを連投すると503/504が返る。間隔を空けて投げ直す

判定を1か所にまとめるなら、たとえばこう書ける。

cdx() {
  local url="$1" mt="${2:-exact}" out code
  out=$(mktemp)
  code=$(curl -sS -o "$out" -w '%{http_code}' \
    "https://web.archive.org/cdx/search/cdx?url=${url}&matchType=${mt}&output=json")
  case "$code" in
    200) ;;
    4*)  echo "パラメータを確認: HTTP $code" >&2; rm -f "$out"; return 2 ;;
    *)   echo "サーバ側の一時エラー、時間を空けて再試行: HTTP $code" >&2; rm -f "$out"; return 3 ;;
  esac
  local rows
  rows=$(python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); print(max(len(d)-1,0))' "$out")
  echo "url=${url} matchType=${mt} http=${code} rows=${rows}"
  rm -f "$out"
}

rows=0 は「該当なし」を意味するが、それは指定した matchType の下での該当なしであって、「アーカイブされていない」ではない。ここを取り違えないために、出力に matchType を必ず含めている。

観測条件

  • 実施日:2026年8月14日16時06分〜16時41分(日本時間)
  • 環境:macOS 15.1.1(ビルド24B91)、curl 8.7.1(x86_64-apple-darwin24.0、libcurl/8.7.1、nghttp2/1.62.0)
  • エンドポイント:https://web.archive.org/cdx/search/cdx
  • 対象:higgsfield.ai(主)、runwayml.com(比較)
  • 該当なしの200・0バイトは、4種類のクエリで再現した(higgsfield.ai/this-path-does-not-exist-20260814higgsfield.ai/termshiggsfield.ai/definitely-not-here-20260814、および存在しないドメイン this-domain-surely-does-not-exist-20260814.examplematchType=domain を付けたもの)
  • ヘッダの比較は、該当あり2件・該当なし2件を交互に投げた計4リクエストで実施
  • 503/504は、322,517行・178,932行を返す大きいクエリ(既定フィールドで82,432,443バイト/47,285,573バイト)を連投した直後に発生した。間隔を空けたリトライで回復している
  • 本文の数値は同日16時41分以降にもう一度同じクエリを投げて再測している。行数(439/44/113/272/1,236/141/178,932/322,517)、MD5、matchType=bogus の400とヘッダ、output=json の3バイト [] は再測でも同じ値だった

正直な但し書き

サーバのソースコードは読んでいない。 本記事の記述はすべて、外部からリクエストを投げて応答を観察した結果である。「なぜ200を返すのか」「x-be の値が何を指すのか」は、Internet Archive の実装を確認したわけではなく、応答の一致から推測した範囲を出ない。

READMEは現在稼働中のサーバと一致しない箇所がある。 このREADMEは冒頭が「Wayback CDX Server API - BETA」で、変更履歴の最新項目は2013年8月7日である。実際、READMEは「absolute maximum length returned from a single query (currently set to 150000 by default)」(1クエリで返る絶対上限は既定で150,000)と書いているが、2026年8月14日の実測では matchType=domain のクエリが322,517行を返した。稼働中の設定はREADMEの記述と異なる。 引用した matchType の定義については実測と矛盾しなかったが、README全体を現行仕様として信頼してよいとは言えない。

捕捉数は時間とともに変わる。 439行・322,517行・44件といった数字は、2026年8月14日のその時刻に返ってきた値である。Wayback Machine は継続してクロールしているため、同じクエリを後日投げれば増える。記事の数字を「現在の値」として引用しないでほしい。

collapse=urlkey を付けた行数は「異なるURLの数」であって「保存回数」でも「ページ数」でもない。 322,517と178,932はいずれも collapse=urlkey を付けて取得した値で、同一URLの複数回の保存は1行に畳まれている。一方でクエリ文字列(?gclid=... など)が違えば別URLとして数えられる。実際、322,517件の urlkey のうち238,386件(73.9%)はクエリ文字列を含んでいた。ルートページだけを見ても、クエリ無しの ai,higgsfield)/ は1件なのに対し、ai,higgsfield)/?... の形が1,891件あり、合計1,892件が同じ1ページを指している。この数字をページ数として引用しないでほしい。

matchType=host は試していない。 READMEに定義がある4モードのうち、実測したのは exactprefixdomain の3つである。host の挙動は確認していない。

在庫メモの from= に関する記述を否定した根拠は、1組の比較である。 from=2024from=20240101 がMD5一致したこと、およびREADMEの記述と例を根拠にしている。すべての桁数(1桁・3桁・13桁など)を網羅的に試したわけではない。

この記事のきっかけになった調査自体は、まだ結論が出ていない。 「AI動画生成サービスの利用規約がいつ変わったか」を調べる目的で CDX を叩き始めたが、本記事で扱ったのはその過程で踏んだAPIの挙動までである。規約の変更履歴そのものについては何も確認していない。

出典

シェア: ポスト はてブ

出典・参照資料

AIニュースの解説を動画でも

YouTubeでは注目ニュースの背景を解説し、Xでは新着記事をお知らせしています。

コメント

まだコメントはありません。最初のコメントを書いてみませんか?

AIについて聞きたいことはありますか?

質問箱で無料で受け付けています。回答は公開され、他の方の参考にもなります。

質問箱を見る →

新しい記事をメールで受け取る

AIの新しい発表を、出典付きで整理して届けます。

関連記事

AI時短ラボ
活用08.14読了21

curlに-Lを付けずに308を叩くと15バイトの「Redirecting...」が返る──sitemapが0件・サイトが壊れたと誤診する罠

出典 ─ 308 Permanent Redirect(HTTP response status codes)
AI時短ラボ
活用08.14読了32

HTTP 200・79,525バイトなのに可視テキストは416文字──「取得できた」を200で数えてはいけない

出典 ─ RFC 9110: HTTP Semanti
AI時短ラボ
活用08.14読了42

IndexNowにGoogleは参加していない──418URL送ってもGoogleには渡らない仕組みと、鍵が無効なときに返る403 UserForbiddedToAccessSite

出典 ─ IndexNow - Home(indexn
AI時短ラボ
活用08.14読了26

出典URLの死活チェックで404だけ見てはいけない──1,517件を全数検査したら、HEADに404を返す公式ドキュメントと403の一次資料が出てきた

出典 ─ RFC 9110: HTTP Semanti
AI時短ラボ
活用08.14読了28

Vercel CLIはgitではなく作業ツリーを直接デプロイする──git statusが本番の中身を表していない状態の見分け方

出典 ─ Exclude Files from Dep
AI時短ラボ
活用08.14読了36

Vercelの自動生成ドメインがGSCの参照元に117本入っていた──*.vercel.appと「Duplicate, Google chose different canonical than user」を全351記事で実測

出典 ─ Accessing Deployments
Vercelデプロイのエラーを減らす個人開発チェックリスト──ローカルビルドから本番200確認までの記事画像
活用07.16読了7

Vercelデプロイのエラーを減らす個人開発チェックリスト──ローカルビルドから本番200確認まで

出典 ─ Vercel Docs: Troublesh
「AIっぽい見た目」を卒業する──デザイントークンをAIに渡す方法の記事画像
活用06.01読了11

「AIっぽい見た目」を卒業する──デザイントークンをAIに渡す方法

出典 ─ Design Tokens Communit