0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

IndexNowをNodeスクリプト1本で実装する ― 記事公開を検索エンジンに即時通知(ChatGPT検索対策にも)

0
Posted at

IndexNowは、URLの追加・更新を検索エンジンに即時通知できるプロトコルです。BingやYandexなどが対応しています。新規ドメインだとクロール頻度が低く、記事を公開してもなかなか見つけてもらえないので、公開フローに組み込む価値があります。

もう1つの動機はChatGPT検索です。ChatGPTの検索はBingのインデックスを使うため、Bingに早く正しく載ることはAI検索経由の流入(いわゆるLLMO)対策になります。ここでは、依存パッケージなしのNodeスクリプト1本で実装した構成を紹介します。

仕組みは「公開キーファイル+POST」だけ

IndexNowの実装は驚くほど小さいです。

  1. APIキーを決める(ランダムな16進文字列。openssl rand -hex 16 などで生成)
  2. キーと同名のテキストファイルをサイトルートに公開するhttps://example.com/<key>.txt、中身はキーそのもの)。これが所有権の証明で、キーは秘密情報ではありません
  3. 公開・更新したURLを https://api.indexnow.org/indexnow にPOSTする

静的サイトなら、キーファイルは public/<key>.txt に置くだけです。

スクリプト本体

Node 18+なら標準の fetch だけで書けます。

#!/usr/bin/env node
const HOST = 'www.example.com';
const KEY = 'あなたのキー';
const KEY_LOCATION = `https://${HOST}/${KEY}.txt`;

const urlList = process.argv.slice(2);
if (urlList.length === 0) {
  console.error('使い方: node indexnow.mjs <URL...>');
  process.exit(1);
}

// 自ホスト以外のURLを弾く(他人のURLを送っても無効なだけだが事故防止)
const invalid = urlList.filter((u) => !u.startsWith(`https://${HOST}/`));
if (invalid.length > 0) {
  console.error(`https://${HOST}/ 配下のURLのみ送信できます`);
  process.exit(1);
}

const res = await fetch('https://api.indexnow.org/indexnow', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json; charset=utf-8' },
  body: JSON.stringify({ host: HOST, key: KEY, keyLocation: KEY_LOCATION, urlList }),
});

if (res.status === 200 || res.status === 202) {
  console.log(`IndexNow送信OK (${res.status}): ${urlList.length}件`);
} else {
  console.error(`IndexNow送信エラー (${res.status}): ${await res.text()}`);
  if (res.status === 403) {
    console.error(`→ キーファイルが公開されているか確認: ${KEY_LOCATION}`);
  }
  process.exit(1);
}

サイト全体を送る --all オプション

初回やサイト全面更新時は、サイトマップから全URLを拾って送ると楽です。サイトマップインデックス→子サイトマップ→<loc> の2段階で抽出します。

function extractLocs(xml) {
  return [...xml.matchAll(/<loc>([^<]+)<\/loc>/g)].map((m) => m[1].trim());
}

async function fetchSitemapUrls(indexUrl) {
  const sitemaps = extractLocs(await (await fetch(indexUrl)).text());
  const urls = [];
  for (const sm of sitemaps) {
    urls.push(...extractLocs(await (await fetch(sm)).text()));
  }
  return urls;
}

XMLパーサーを入れるほどの構造ではないので、正規表現で十分です。

ハマりどころ・注意点

  • 403が返る → キーファイルがまだ公開されていないのが定番。デプロイ前に送信すると起きます。「デプロイ完了→送信」の順序をフローで保証する
  • 202は「受理(キー検証は後で実施)」。200と同じく成功扱いでOK
  • 送信=即インデックスではない。「見つけてもらう」のが速くなるだけで、インデックスするかは検索エンジン側の判断
  • Googleは(2026年7月時点で)IndexNow非対応。Google側はサイトマップ+Search Consoleでの登録が別途必要
  • 送りすぎに神経質になる必要はないが、変更のないURLを機械的に毎日送るような使い方は推奨されていない

運用: 「公開フロー」に組み込んで人間の記憶から消す

このスクリプトは、ブログの公開手順(ビルド確認→コミット→push→IndexNow通知→インデックス確認)の1ステップとしてClaude Codeの手順書に組み込んであり、記事を公開すると必ず実行されます。通知の打ち忘れという概念自体をなくすのが一番の効きどころです。

ブログ運用の全体像はこちらに書いています: ブログのネタ出しを自動化する仕組み|Discordの独り言37件が記事になるまで

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?