まさか
<link rel="canonical">の原因がテーマ(Cocoon)だったなんて…という方向けの、**再現→原因→安全な直し方(可逆・少変更)**をまとめました。WordPress 標準や GSC(Google Search Console)の挙動にも馴染む形で、“ページに1本だけ、自己参照の canonical” を安定して出すのがゴールです。
テーマ名:Cocoon バージョン:2.8.8 が今回のテーマ
要約(最短ルート)
- Cocoon は
wp_headで 自前の canonical を出力します。 - これが フィード/AMP/404 でも出てしまう・重複して2本以上出るなどで GSC の評価が不安定になることがあります。
- MU プラグインで Cocoon の canonical を止め、自前の安全版 canonical を 1 本だけ出せば安定します(テーマ更新にも強い・可逆)。
症状の例
- GSC で「代替ページ(適切な canonical タグあり)」「ページにリダイレクトがあります」が増える
- フィード (
/feed/)、AMP、404 にも canonical が出ている - 1ページ内に canonical が 2本以上 出ている
curlで見るとカテゴリを指す 誤った canonical が混ざる
原因の概要(Cocoon の canonical 出力)
Cocoon テーマでは lib/seo.php 内で以下のように canonical を出力しています:
// 例:テーマ内(概念図)
add_action( 'wp_head', 'generate_canonical_tag' );
この実装は便利ですが、条件分岐がサイト運用の方針とズレると、
- フィード/AMP/404にも出てしまう
- ほかのプラグイン/自作コードと 重複 する
…などが起きえます。
解決策:MU プラグインで“止めて、出す”
mu-plugins はテーマや通常プラグインより早く読み込まれるため、テーマ更新の影響を受けにくいのが利点です。
1) Cocoon の canonical を無効化(remove_action)
wp-content/mu-plugins/disable-cocoon-canonical.php を作成:
<?php
/*
Plugin Name: Disable Cocoon Canonical (MU)
Description: Cocoon の canonical 出力を安全に停止する。
*/ // テーマが初期化された後に、Cocoon のフックを外す
add_action('after_setup_theme', function(){ // Cocoon が wp_head にぶら下げている関数名 remove_action('wp_head', 'generate_canonical_tag');
}, 100);
ポイント
after_setup_themeの**遅め(100)**で実行すると、確実にフックを外せます。- テーマ側の関数名が変わると効かないので、アップデート後に再確認を。
2) 安全版 canonical を“1本だけ”出力
wp-content/mu-plugins/safe-canonical.php を作成:
<?php
/*
Plugin Name: Safe Canonical (MU)
Description: 404/プレビュー/フィード/REST/AMP などには出さず、フロントで自己参照 canonical を1本だけ出力。
*/ // AMP 判定(AMPプラグイン未使用なら false)
if (!function_exists('hd0_is_amp')) { function hd0_is_amp(): bool { return function_exists('is_amp_endpoint') && is_amp_endpoint(); }
} // sitemap など“HTML以外”は除外(必要に応じて調整)
if (!function_exists('hd0_is_sitemap_uri')) { function hd0_is_sitemap_uri(string $uri): bool { return (bool) preg_match( '#^/(?:sitemap(?:_index)?\.xml|[A-Za-z0-9_-]+-sitemap(?:\.xml)?|wp-sitemap(?:-.*)?\.xml)$#', $uri ?? '' ); }
} add_action('wp_head', function(){ // 管理/プレビュー/ログインユーザー/404/フィード/AMP/REST は出さない if (is_admin() || is_user_logged_in() || (function_exists('is_preview') && is_preview())) return; if (function_exists('is_404') && is_404()) return; if (function_exists('is_feed') && is_feed()) return; if (hd0_is_amp()) return; $uri = $_SERVER['REQUEST_URI'] ?? ''; if (hd0_is_sitemap_uri($uri)) return; // フロントのみ(必要なら is_singular() などで更に絞る) if (!is_singular() && !is_home() && !is_front_page() && !is_category() && !is_tag() && !is_tax() && !is_author()) { // ここではアーカイブも許容。絞るなら is_singular() に限定。 } // WordPress の正規URLを取得(末尾スラ/URLエンコードは WP に任せる) if (function_exists('wp_get_canonical_url')) { $canonical = wp_get_canonical_url(); } if (empty($canonical)) { // フォールバック:現在URLを自力で $scheme = is_ssl() ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST'] ?? ''; $request = $_SERVER['REQUEST_URI'] ?? '/'; $canonical = $scheme.'://'.$host.$request; } $canonical = esc_url($canonical); if ($canonical) { echo '<link rel="canonical" href="'.$canonical.'" />' . "\n"; }
}, 50);
運用Tips
- もし「投稿/固定ページだけに限定したい」なら、
if (!is_singular()) return;にしてアーカイブで出さない運用もOK。- AMP を使っていないなら
hd0_is_amp()は常に false で OK。
動作確認(curl だけでOK)
# 1) 記事本体:canonical が “1行だけ & 自分自身”
curl -s 'https://example.com/2025/post-slug/' | grep -i 'rel="canonical"' # 2) フィード/AMP/404:canonical が “出ない”
curl -s 'https://example.com/2025/post-slug/feed/' | grep -i canonical
curl -s 'https://example.com/2022/09/post-xxx/amp/' | grep -i canonical
curl -s 'https://example.com/404/' | grep -i canonical # 3) サイト全体の重複排除(ざっくり)
for u in \ https://example.com/2025/a/ \ https://example.com/2025/b/ \
; do echo -n "$u : "; curl -s "$u" | grep -ci 'rel="canonical"'; done
# → すべて "1" になっていればOK
キャッシュ/HTTPヘッダの注意
- もし Apache の
mod_cache_disk等で HTMLをキャッシュしている場合、テーマ修正前の HTML が出続けることがあります。 - MU を入れた直後は サーバ側でキャッシュを無効/削除 →
curl -H 'Cache-Control: no-cache'で確認がおすすめ。
例:mod_cache_disk
# 例: ディスクキャッシュの確認と手動クリーン(環境依存)
sudo htcacheclean -p /var/cache/httpd/mod_cache_disk -l 2G -t -v
# ※ 実運用では適切な -l (容量) 設定が必要
GSC(Google Search Console)での回復手順
- サイトマップは 最終URL(https/エンコード済み/200直) のみが載る構成にしておく。
- 問題が出ていた URL は、旧URLではなく“最終URL”で
- URL 検査 → 公開URLをテスト → インデックス登録をリクエスト
- 404 や feed/amp の URL は 検証しない(サイトマップにも含めない)。
よくある質問(FAQ)
Q. Cocoon の更新で元に戻りませんか?
A. MU で remove_action() しているため 基本的に維持されます。テーマ側の関数名が変わった場合は MU を再調整。
Q. canonical は投稿だけに出したい
A. 上記 MU の is_singular() 条件にし、アーカイブでは出さない運用にしてください。
Q. rel=”prev”/”next” は要りますか?
A. Google は rel="next/prev" をランキングシグナルとしては使っていません。ページネーションはサイト内導線として適切に(サイトマップ/内部リンクを丁寧に)。
Q. 代替ページ(適切な canonical タグあり)が消えません
A. クロール更新で順次解消されます。旧URLを送らず、最終URLのみ を URL 検査してください。
まとめ
- Cocoon の canonical は便利な反面、条件が合わないと重複/誤出力の原因に。
- MU プラグインでテーマに手を入れず、
- 止める(remove_action)
- 出す(自己参照 canonical を1本だけ)
を徹底すると、GSC 側の評価が安定しやすくなります。
付録:より厳格なバリアント(任意)
canonical を 投稿/固定ページのみに限定し、アーカイブには出さないバージョン:
add_action('wp_head', function(){ if (!is_singular()) return; // 投稿/固定ページのみ if (is_admin() || is_user_logged_in()) return; if (function_exists('is_preview') && is_preview()) return; if (function_exists('is_404') && is_404()) return; if (function_exists('is_feed') && is_feed()) return; if (function_exists('is_amp_endpoint') && is_amp_endpoint()) return; $canonical = function_exists('wp_get_canonical_url') ? wp_get_canonical_url() : ''; if (!$canonical) { $scheme = is_ssl() ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST'] ?? ''; $request = $_SERVER['REQUEST_URI'] ?? '/'; $canonical = $scheme.'://'.$host.$request; } echo '<link rel="canonical" href="'.esc_url($canonical).'" />' . "\n";
}, 50);
誤出力の温床になりがちな feed/amp/404 には出さない・1本だけ自己参照――この2点を守るだけでクローラビリティは劇的に安定します。テーマやプラグインに大手術は不要。MUで“軽くかわす”のがコツです。

