% cd ..

IndexNow で Bing 系検索エンジンに URL を自動プッシュする

IndexNow で Bing 系検索エンジンに URL を自動プッシュする

IndexNow — 検索エンジンに自ら push 通知する仕組み

とりまサイトマップ sitemap.xml を検索サイト2つ(Google Search Console, Bing Webmaster Tools)に登録して、「これでクロール対応は OK」と思っていたのですが、マイナーサイトの悲しさよ、クロールされるまでの時間ってめちゃ長いんですね。

そんな時に(もしかしたら)効きそうなのが、IndexNow
IndexNow は、Microsoft と Yandex が2021年に共同で発表した、新規/更新ページを検索エンジンに能動的に通知するためのプロトコルです。

  • 対応検索エンジン: Bing、Yandex、Naver(韓国)、Seznam(チェコ)
  • Google は未対応(Google はあくまで自前のクローラーで発見したいスタンス)
  • 仕組み: シンプルに HTTP POST で URL を送るだけ

良さそうな上に、もう Bing Webmaster Tools のメニューに組み込まれちゃってるので、今更ながら始めてみることにしました。 「サイトマップを置いておくからクローラーが来た時に拾ってね」という受けの姿勢から、「新記事できたから今すぐ来て」という攻めの姿勢への転換です。

公開することでドメイン所有の証明になる

普通 Web API なら認証は秘密の API key で行いますが、IndexNow はもっとシンプルな方式を採用しています:

  1. ランダムな文字列(= key)を適当に作成(8〜128 文字の半角英数字とハイフン)
  2. https://yourdomain/<key>.txt にその key 文字列を中身に持つテキストファイルを配置
  3. API リクエストには keykeyLocation を含める
  4. 検索エンジンは keyLocation を実際に GET して中身を検証する

一見「鍵を公開場所に置くなんて危ない」とも感じますが、IndexNow の認証は key 自体を秘密にする方式ではありません。 もちろんバレないに越したことはないのですが、key も keyLocation もAPIリクエストに平文(生テキスト)で含まれていて、検索エンジン側の処理ログにも残る前提です。
実際に安全性を担保しているのは「この URL にコンテンツをホストできる = ドメインの所有者」という proof-of-control の仕組み。 HTTPS 証明書発行(ACME challenge)や Google Search Console の HTML ファイル認証と同じ発想です。

ぶっちゃけ、もし悪意ある他人にAPIキーがバレてしまったとしても、その人ができることといえば、このサイトのURLを Bing に『インデックスして』とリクエストすることだけです。 ドメイン縛りがあるからバレても実質問題がないという、地味ながら賢い設計です。

セットアップ、所要 5 分

Bing Webmaster Tools の IndexNow API Key 生成画面
  1. Bing Webmaster Tools でAPIキー取得
    先にも書いたように、APIキーは適当に自分で作っちゃってもルール内なら問題ありません。 ただ、せっかく Bing のダッシュボード左メニューに「IndexNow」が組み込まれていて、[Generate] ボタンで自動的に key 生成してくれますので、これを利用します。
    生成される文字列は、UUID 風の16進数32文字。

  2. キーファイルをルートに配置
    このブログは Next.js + Vercel 構成なので、public/<key>.txt に置くだけ。中身は key 文字列そのまま。 push してデプロイ後、https://diary.dazydayz.com/<key>.txt でアクセスできれば準備完了。

  3. API 動作確認
    IndexNow は URL を渡す方法が 2 通りあります。
    GET: ?url=X&key=Y&keyLocation=Z で 1 URL ずつ送信
    POST: urlList 配列で最大 10,000 URL を一括送信
    このブログでは日本語版 + 英語版がペアで公開される前提です(将来的にもっと言語を増やす可能性もある)。 1 記事公開ごとに 2 URL 以上をまとめて 1 リクエストで送れる POST 方式の方が効率的なので、POST urlList を採用します。

curl で叩いてみる:

curl -X POST "https://api.indexnow.org/indexnow" \
  -H "Content-Type: application/json" \
  -d '{
    "host": "diary.dazydayz.com",
    "key": "<KEY>",
    "keyLocation": "https://diary.dazydayz.com/<KEY>.txt",
    "urlList": [
      "https://diary.dazydayz.com/ja/posts/2026/05/tokuryu",
      "https://diary.dazydayz.com/en/posts/2026/05/tokuryu"
    ]
  }'

試しに トクリュウ記事 のURLを投げてみたら、202 が返ってきました。

202 Accepted: URL received. IndexNow key validation pending.

キー検証待ちのステータスで、初回API呼び出し時に IndexNow 側がまだ keyLocation の中身を取りに行って verify する前の状態を表す模様。
しばらく経ってから同じ内容を再投したところ、今度は 200 OK が返ってきました。

GitHub Actions で自動化

毎回 curl を手動で叩くのは面倒ですし、自動化してナンボだと思いますので、posts/ への push 検出 → IndexNow ping を自動化しました。

スクリプト(scripts/indexnow-ping.ts

引数で渡された .md ファイルのうち status: published のものだけを抽出し、対応する公開 URL を IndexNow API に POST:

const HOST = "diary.dazydayz.com";
const KEY = process.env.INDEXNOW_KEY!;

// posts/ja/2026/05/indexnow.md → /ja/posts/2026/05/indexnow
const m = file.match(/^posts\/(ja|en)\/(.+)\.md$/);
urls.push(`https://${HOST}/${m[1]}/posts/${m[2]}`);

await fetch("https://api.indexnow.org/indexnow", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    host: HOST,
    key: KEY,
    keyLocation: `https://${HOST}/${KEY}.txt`,
    urlList: urls,
  }),
});

--dry-run フラグも対応して、ローカルで通知対象のURLを事前に確認可能にしました。

$ npx tsx scripts/indexnow-ping.ts posts/ja/2026/05/indexnow.md --dry-run
IndexNow に 1 件の URL を通知:
   - https://diary.dazydayz.com/ja/posts/2026/05/indexnow

ワークフロー(.github/workflows/indexnow.yml

on:
  push:
    paths:
      - 'posts/ja/**.md'
      - 'posts/en/**.md'
    branches:
      - main

jobs:
  ping:
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 2

      - name: Detect changed posts
        id: changes
        run: |
          FILES=$(git diff --name-only --diff-filter=AM HEAD~1 HEAD \
            -- 'posts/ja/**.md' 'posts/en/**.md' | tr '\n' ' ')
          echo "files=$FILES" >> $GITHUB_OUTPUT

      - name: IndexNow ping
        if: steps.changes.outputs.files != ''
        env:
          INDEXNOW_KEY: ${{ secrets.INDEXNOW_KEY }}
        run: npx tsx scripts/indexnow-ping.ts ${{ steps.changes.outputs.files }}

ポイント:

  • --diff-filter=AM で Added / Modified のみ拾う(Deleted は除外)
  • status: published フィルタはスクリプト側でやる(draft 記事は ping しない)
  • secrets から APIキーを読む(スクリプト / ワークフローには key 値そのものをハードコードしない)

まとめ

この記事を push することが workflow の初回テストになります。
無事に動いていれば、push 後数分以内に Bing Webmaster Tools の URL Inspection でこの URL の最近のクロール記録が見える(はず)。

これで、push したら数分以内に複数の検索エンジンに通知が飛ぶ仕組みが完成しました。
残念ながら Google はこのアライアンスには入っていないので、別途 GSC のクロールサイクル待ちで対応することになります。