hreflangの整合性と相互参照を確認
hreflangクラスターを正しく機能させるには、参加する全ページが、自己参照を含む同一の完全な代替ページ集合を宣言する必要があります。このチェッカーは複数ページから収集したマッピングを比較し、URLへアクセスせずに不完全なクラスターを検出します。すべての参照元URLと参照先URLから期待されるページ集合を作り、各参照元を照合して、ページごとに不足するリンクを正確に返します。結果は決定的で、公開前確認、移行、継続的なSEO品質管理に利用できます。
無料で実行
完全な相互参照が重要な理由
hreflang注釈は、あるページから別のページへの一方向の推奨ではありません。言語または地域ごとに用意された同等ページのグループへの所属を表すものであり、各ページは自分自身を含む同じ参照先集合を公開する必要があります。英語ページがフランス語版とドイツ語版を示していても、フランス語ページが英語版しか示していなければ、個々のタグが正しく見えてもクラスターは不完全です。検索エンジンは裏付けのない関係を無視し、意図しない言語版を表示したり、地域シグナルを弱めたりする可能性があります。ページ数が増えるほど関係数も急増するため、目視確認は不確実になります。4ページなら各ページが4件を宣言し、合計16件の参照元・参照先関係が必要です。このチェッカーは目視比較を厳密な集合検査に置き換えます。送信された各ページがクラスター内の全ページを宣言した場合だけ整合していると判定し、欠落と自己参照不足を明示します。
マッピングの準備方法と結果の読み方
確認したhreflang宣言ごとに1件のレコードを送信してください。page_urlはタグを含むページ、languageはタグの値、target_urlは参照先を表します。クラスターに含める予定の全ページからレコードを収集してください。期待されるクラスターは、参照元URLと参照先URLの和集合から導出されます。そのため、兄弟ページから参照されているのに自身の宣言を1件も提供しないページも期待集合に残り、すべての参照先が不足していると報告されます。URLは前後の空白を除去した後、文字列として厳密に比較されます。収集時にはページが出力する正規の絶対URLを使用してください。出力にはページ別の要約と競合一覧があります。不完全なページではmissing_urlsに追加すべき参照先が示されます。consistentが真になるのは競合一覧が空の場合だけです。参照先は集合として比較されるため、重複宣言で不足を埋めることはできません。
公開、移行、定期監査での活用
相互参照を確認する最適な時点は、多言語版の公開内容をクローラーが取得する前です。レンダリング済みページからhreflang宣言を抽出し、マッピングレコードへ変換して、consistentが偽なら公開処理を停止してください。現在ページを省くテンプレート分岐、新市場だけを更新した地域展開、旧URLを出力し続けるロケールなどを検出できます。言語の追加や廃止後に行う定期監査にも適しています。このアルゴリズムはネットワーク要求を行わないため、参照先の応答、canonical指定、内容の同等性までは証明しません。それらには別のクロールと編集確認が必要です。本機能は、提示された各ページが他の全ページと自分自身を列挙しているかを厳密に判定します。APIリクエストには公開基本料金の$0.002が適用され、ブラウザーでは同じ純粋な比較をローカル実行できます。競合結果をデプロイ証跡と共に保存すると、修正対象を具体的な参照元と参照先に結び付けられます。
活用例
多言語公開の検証
公開前に、新しい各ロケールと既存の兄弟ページが完全な代替ページ集合を宣言しているか確認します。
プラットフォーム移行の監査
新テンプレートのマッピングを比較し、移行中に自己参照や戻りリンクを失ったページを特定します。
SEOデプロイ工程の保護
不整合クラスターを、不足する参照元・参照先関係を示す決定的な失敗として扱います。
よくある質問
hreflangクラスターが整合する条件は何ですか?
クラスターに現れる各URLが、自分自身を含む全URLを宣言する必要があります。全関係が存在する場合だけ真になります。
チェッカーはページを取得しますか?
いいえ。指定されたマッピングだけを比較し、HTTP状態、canonicalタグ、ページ内容は検証しません。
宣言のない参照元ページをどう検出しますか?
参照元と参照先の両URLから期待集合を作るため、参照先だけに現れるURLも宣言不足のページとして報告されます。
重複した宣言は競合になりますか?
完全性には影響しません。参照先は集合として比較しますが、マッピング件数には送信された全レコードが含まれます。
URLの表記揺れは正規化されますか?
いいえ。前後の空白を除いて厳密に比較します。送信前にスキーム、ホスト、パス、末尾スラッシュを統一してください。
APIリクエストの料金はいくらですか?
各リクエストには公開基本料金の$0.002が適用されます。ネットワークやモデルは使用しません。
開発者向け — APIアクセス
このページの機能はすべてAPIからも利用できます。自社システムに組み込みたいチーム向けのセクションです。それ以外の方は上のツールをそのままお使いください。
エンドポイント
Bearerトークンで認証し、POST1回でタスクをキューに登録します。結果はWebhookまたは署名付きリンクで受け取れます。
お使いのスタックから呼び出す
curl -X POST https://api.kit.forhosting.com/seo/hreflang-conflict-check \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"mappings":[{"page_url":"https://example.com/en","language":"en","target_url":"https://example.com/en"},{"page_url":"https://example.com/en","language":"fr","target_url":"https://example.com/fr"},{"page_url":"https://example.com/fr","language":"en","target_url":"https://example.com/en"},{"page_url":"https://example.com/fr","language":"fr","target_url":"https://example.com/fr"}]}'const res = await fetch("https://api.kit.forhosting.com/seo/hreflang-conflict-check", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"mappings": [
{
"page_url": "https://example.com/en",
"language": "en",
"target_url": "https://example.com/en"
},
{
"page_url": "https://example.com/en",
"language": "fr",
"target_url": "https://example.com/fr"
},
{
"page_url": "https://example.com/fr",
"language": "en",
"target_url": "https://example.com/en"
},
{
"page_url": "https://example.com/fr",
"language": "fr",
"target_url": "https://example.com/fr"
}
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/seo/hreflang-conflict-check",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"mappings": [
{
"page_url": "https://example.com/en",
"language": "en",
"target_url": "https://example.com/en"
},
{
"page_url": "https://example.com/en",
"language": "fr",
"target_url": "https://example.com/fr"
},
{
"page_url": "https://example.com/fr",
"language": "en",
"target_url": "https://example.com/en"
},
{
"page_url": "https://example.com/fr",
"language": "fr",
"target_url": "https://example.com/fr"
}
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/seo/hreflang-conflict-check", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"mappings":[{"page_url":"https://example.com/en","language":"en","target_url":"https://example.com/en"},{"page_url":"https://example.com/en","language":"fr","target_url":"https://example.com/fr"},{"page_url":"https://example.com/fr","language":"en","target_url":"https://example.com/en"},{"page_url":"https://example.com/fr","language":"fr","target_url":"https://example.com/fr"}]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"mappings":[{"page_url":"https://example.com/en","language":"en","target_url":"https://example.com/en"},{"page_url":"https://example.com/en","language":"fr","target_url":"https://example.com/fr"},{"page_url":"https://example.com/fr","language":"en","target_url":"https://example.com/en"},{"page_url":"https://example.com/fr","language":"fr","target_url":"https://example.com/fr"}]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/seo/hreflang-conflict-check", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)リクエスト例
{
"mappings": [
{
"page_url": "https://example.com/en",
"language": "en",
"target_url": "https://example.com/en"
},
{
"page_url": "https://example.com/en",
"language": "fr",
"target_url": "https://example.com/fr"
},
{
"page_url": "https://example.com/fr",
"language": "en",
"target_url": "https://example.com/en"
},
{
"page_url": "https://example.com/fr",
"language": "fr",
"target_url": "https://example.com/fr"
}
]
}レスポンス例
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "seo.hreflang_conflict_check",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}非同期APIです。task_idは即時に返ります。ポーリングは1秒あたり1リクエストまでです。
料金
単価はすべて公開しています。トークン換算や独自クレジットはありません。失敗したタスクは課金されません。
エラー
| HTTP | コード | 意味 |
|---|---|---|
401 | unauthorized | APIキーが無効か、指定されていません。Authorizationヘッダーを確認してください。 |
402 | insufficient_balance | 残高が不足しています。チャージ後に再度お試しください。 |
404 | unknown_type | 指定されたタスクタイプは存在しません。タイプ名を確認してください。 |
429 | rate_limited | リクエストが多すぎます。しばらく待ってから再度お試しください。 |