AIデジマ研究所

重複記事を301で統合する手順|退避・転送・ゴミ箱・検証を自動化する

2026-10-17 ・ 大規模修正

同じテーマの記事が2本あって、どちらも中途半端に順位を取っている。削除していいのか、残すならどちらか、迷って手が止まる。これは大規模修正のうちでも、最初に片づけたい構造の問題です。

結論から書きます。重複記事は、残す側を1本に決め、消す側は「本文の退避 → 301リダイレクト → ゴミ箱に送る → 転送が効いているか検証」の4手順で統合します。 この4つを一つのスクリプトにまとめ、少ない件数で試し実行してから本番で動かします。削除ではなくゴミ箱に送るのは、間違えたときに戻せる状態を残すためです。

この記事は、散らかったサイトをAIで作り変えるマニュアルの「大規模修正」の作業です。1記事ずつのリライトに入る前に、ここで消える記事を先に消しておきます。


この作業で決まること

この作業が終わると、重複した2本以上の記事が、1本の正しいURLに集まり、消した側のURLに来た読者と検索エンジンのクロールが残した側へ届く状態 になります。

転送より先に消す側をゴミ箱へ送ると、短い時間ですが消す側のURLが404を返す窓ができます。順番は「退避 → 転送を有効にする → 消す側をゴミ箱に送る」の順で固定し、スクリプトの中でも入れ替えません。


前提:この記事の前に必要なもの

Redirection プラグインの公式サイトは https://redirection.me/ です。REST API の形はプラグインのバージョンで変わるので、本番の前に自分のサイトで1件だけ投げて、返ってくるJSONを目で確かめます。


AIに渡すもの

  1. 統合ペアの一覧:(消す側の記事ID・消す側のスラッグ・残す側の記事ID・残す側のスラッグ)の組を、プログラムの中に配列として持つ
  2. WordPress の認証情報:REST API のユーザーとアプリケーションパスワード
  3. 退避先のフォルダのパス
  4. 実行モードの切り替え:引数で「試し実行」と「本番実行」を分ける

ペアの一覧はコードの中に書き、AIにペアを毎回自分で判断させません。重複の判定は、同じテーマで複数URLが存在するという構造の問題で、ここは人が決めてから渡します。自分のサイトで作ったスクリプトでは、ペアを MERGES という配列として (trash_id, trash_slug, keep_id, keep_slug) の形で並べ、--apply を付けたときだけ本番の操作が走る作りにしています。


手順

  1. ペアを決める。残す側は、公開日が古い・被リンクがある・順位が高い・本文が厚い、のどれかで決めます。迷うときは、残す側に本文を寄せる作業を先に済ませてから統合します。
  2. 試し実行する。スクリプトを --apply 無しで実行し、ペアの件数・消す側の記事タイトル・ステータスが期待どおりかを画面で確認します。この段階ではサイト側は何も変わりません。
  3. 1件だけ本番で動かす。配列から1ペアだけ残して --apply を付け、退避ファイルが保存されたか、Redirection プラグインに301が1件増えたか、消す側の記事のステータスが trash になったかを確認します。
  4. 残りを本番で動かす。1件が通ったら残りのペアを配列に戻し、もう一度 --apply で実行します。1回に流す件数は3〜5件までに抑えます。
  5. 転送の検証。消す側のURLに allow_redirects=False でアクセスし、ステータスが 301、Location ヘッダが残す側のURLになっていることを確認します。この検証はスクリプトの最後にまとめて走らせます。
  6. Search Console でURL検査。数日たってから、消す側のURLをURL検査にかけ、「正規URL」が残す側に切り替わっているかを見ます。

最初の本番実行で1件に絞ると、問題が起きたときに原因の切り分けが短くて済みます。1回に数十件を流すと、Redirection プラグイン側の内部整合やキャッシュがずれたときに、どのペアで止まったのか追うのに時間がかかります。


完了条件


実際に起きた失敗

このスクリプトは、本番サイトを機械で触るときに取り返しのつかない状態を作らないよう、設計の段階で3つの決まりを入れています。守らないと戻せなくなるという理由で入れた決まりなので、ここが崩れていないかを公開前に見ます。

  1. 完全削除ではなく、ゴミ箱に送る。WordPress の REST API では、DELETE /posts/{id} に force=False を渡すとステータスが trash になり、管理画面から復元できます。自分のスクリプトでは params={"force": False} を固定にして、force=True を使わない作りにしています。間違えたペアを統合しても、ゴミ箱から戻せます。
  2. 本文を先に退避する。ゴミ箱からの復元は WordPress 側の仕様に依存するので、復元できなかった場合に備えて、本文のHTMLをファイルで持ちます。退避ファイル名は 記事ID_日付.raw.html の形にして、同じ記事を別の日に触っても上書きされないようにしています。
  3. 転送と削除の順序を固定する。コードの中で「退避 → 301登録 → ゴミ箱」の順を変えません。301を登録する前にゴミ箱に送ると、短い時間ですが404が返る窓ができるので、ここは入れ替えない構造にしています。

試し実行の仕組みも、同じ理由で入れています。デフォルトは何もしない動作で、--apply を明示したときだけ本番の操作が走ります。本番サイトを変更するスクリプトは、明示しないと動かない ことを前提にすると、別の作業中に誤って走らせても被害が出ません。


よくある質問

Q. 301ではなく、残す側に本文を追記して、消す側は手動で削除してはいけませんか?
A. 本文を寄せる作業自体は必要なことがあります。ただし、消す側のURLに来る被リンクや、検索結果に残っているキャッシュを捨てることになるので、転送は別に入れます。削除と転送はセットです。

Q. 残す側のスラッグが読みにくいです。残す側のURLを変えてもいいですか?
A. 残す側のURLを変えると、残す側にも301を足すことになり、301が2段になります。1段で済むように、残す側のURLは触らず、「どちらがより良いスラッグか」ではなく「どちらが実力でクリックを取っているか」で残す側を決めます。

Q. Redirection プラグインを使わず、サーバーの設定ファイルで301を書いてはいけませんか?
A. 書けますが、WordPress の記事ID・スラッグと転送ルールが別の場所に分かれると、台帳との整合を機械で確かめにくくなります。REST API で登録・取得できる Redirection プラグインを使うと、転送の一覧も台帳と同じ方法で読めます。


関連記事

次の作業