01 / 本文
サイトマップとcanonicalが食い違っていた
検索条件ごとのページを /search?keyword=... の形で持ち、その一覧をサイトマップに入れていた。
ところが各ページの canonical を調べると、どれも /search を指していた。クエリが消えている。
サイトマップは「このURLを登録してほしい」という依頼、canonical は「このURLは登録しないでほしい」という宣言。 同じサイトが、同じURLについて正反対のことを言っていた。
02 / 本文
原因はヘルパの仕様
Bladeのレイアウトはこう書いてあった。
<link rel="canonical" href="{{ url()->current() }}">03 / 本文
current() にクエリは含まれない
url()->current() が返すのはクエリ文字列を除いたURLで、これは仕様どおりの動き。
/search?keyword=異世界 も /search?keyword=恋愛 も、canonical は /search になる。13ジャンルのページが全部、同じ1枚を正規URLとして申告していた。
url()->full() ならクエリを含むが、今度は並び替えやページ送りのような内容が変わらないパラメータまで拾ってしまう。
04 / 本文
内容が変わる条件だけ残す
必要なのは「そのパラメータで中身が変わるか」の線引きなので、残すキーを明示する。
@php
// url()->current() はクエリを落とすため、ジャンル別のページがすべて
// /search を正規URLとして申告してしまう。内容が変わる条件だけ残す。
$canonicalQuery = array_filter(
request()->only(['keyword']),
fn ($value) => $value !== null && $value !== ''
);
$canonicalUrl = url()->current()
. ($canonicalQuery ? '?' . http_build_query($canonicalQuery) : '');
@endphp
<link rel="canonical" href="{{ $canonicalUrl }}">05 / 本文
og:url も同じ変数にする
og:url を別に書いていると、片方だけ直して食い違う。同じ変数を使う。
片側だけ直した状態は、見た目では気づけない。
06 / 本文
デプロイ後にBladeのキャッシュを消す
コンパイル済みのBladeが残っていると、修正が本番に出ない。デプロイの最後に消す。
php artisan view:clear
php artisan config:clear
php artisan route:clear07 / 本文
直ったことを本番で確かめる
ローカルで直っても、本番に届いていなければ意味がない。デプロイの最後に本番を叩いて確認するステップを足した。
U='https://example.jp/search?keyword=%E7%95%B0%E4%B8%96%E7%95%8C'
CANON=$(curl -sS "$U" | grep -o 'rel="canonical" href="[^"]*"')
case "$CANON" in *keyword=*) echo 'OK';; *) echo 'NG' >&2; exit 1;; esac08 / 本文
同じ原因が2つのサイトで起きていた
1つ目のサイトを直したあと、管理している全ドメインで同じ点検をした。同じ雛形から作った別のサイトでも、まったく同じ状態になっていた。
片方は47ページ、もう片方は13ページ。前者は46ページが「発見済み - 未登録」のまま1か月半放置されていた。
1つ見つけたら、同じ雛形を使った他も見る。 1サイトずつ目で見ると必ず漏れる。
09 / 本文
まとめ
- url()->current() はクエリを落とす。canonical に素で使わない
- full() ではなく、内容が変わるキーだけを残す
- og:url も同じ変数にする
- デプロイ後に view:clear
- 本番のcanonicalをcurlで確かめるステップを入れる
コメント
この記事へのコメント
お名前だけで投稿できます。アカウント登録やログインは不要です。 いただいたコメントは確認のうえ公開します。