かえでBlogは2012年から続いているブログであり、長年WordPressで運営しています。
これまで、記事のパーマリンク(URL)を「/%year%/%monthnum%/%day%%hour%%minute%%second%.html」という日時ベースの構造で設定していました。
しかし、WordPress 5.4.1へのアップデートを行った途端、個別記事が正常に表示されなくなるというトラブルに見舞われました。
調査の結果、WordPress 5.4.1以降ではパーマリンクに「%post_id%」または「%postname%」を含めることが必須になったことが判明しました。
今回は、この事象の原因と、既存のURL(見た目)を極力維持したまま新仕様へ移行・リダイレクトさせた際の手順を備忘録としてまとめます。
突然個別記事が表示されなくなった原因
プレビュー画面までは問題なく、公開すると消える!?
WordPressを5.4.1にアップデートした後、新しく記事を書いていたのですが、編集画面やプレビュー画面では全く問題なく表示されていました。そのため、異常に気づくのが少し遅れてしまいました。
しかし、いざ「公開」ボタンを押した途端、今まで正常に表示されていた個別記事(single.php)の画面が一転して表示されなくなってしまったのです。
最初は「カスタムHTMLやTwitterの埋め込みタグが悪さをしているのかな?」と思ったのですが、テスト用に簡単なテキストだけの記事を投稿しても現象は同じ。テーマを変更しても直らなかったため、WordPress本体の仕様変更を疑い始めました。
原因:セキュリティ対策による仕様変更(日時のみはアーカイブ判定へ)
情報を集めていると、公式フォーラムの「wordpress 5.4.1 からの障害について」というトピックにたどり着きました。
Changeset 47641 の内容を確認すると、パーマリンクを「%day%%hour%%minute%%second%」で終わらせてはいけない(最後に %post_id% または %postname% が必要)という仕様変更が行われていました。

「Unpacking The 7 Vulnerabilities…」などのセキュリティ記事を読むと、どうやら情報漏洩を防ぐための措置のようです。
時・分・秒だけのURL設定だと、全く同じ日時に複数の記事(公開記事と非公開記事など)が存在した場合、システムが「どちらを表示すべきか」正しく判別できず、誤って非公開の記事を露出させてしまう脆弱性があったとのこと。
そのため、アップデート以降は「日時指定のみのパーマリンクは、個別記事ではなくアーカイブ(一覧)として判定する」ように処理が変更されたようです。
公式の「パーマリンクの使い方」にも、現在ではっきりと「構造は必ず %post_id% あるいは %postname% で終了し、各パーマリンクが個々の投稿を指すようにしてください」と記載されています。日時のみのパーマリンクは邪道になってしまったのですね……。
パーマリンク設定の変更とDB一括置換の実施
WordPressを今後も安全に運用していくためには、パーマリンクの設定変更が必須です。
しかし、当ブログにはすでに400記事ほどの公開済み記事があり、今から手作業で全て修正するのは骨が折れます。
そこで、これまでのURLの見た目(数字の羅列)を極力維持しつつ、新仕様に適合させる作戦をとることにしました。
具体的には、従来の「日時の数字部分」をそのまま新しい %postname%(スラッグ)として一括で登録し直します。
STEP 1:現状のURL一覧を抽出する
まずは、現在割り当たっているURLとPost IDの情報を抽出します。DBから直接SQLで抜いても良いですが、今回は「Export All URLs」という便利なプラグインを使用しました。

インストール後、以下の設定で抽出を行います。
- Select a Post Type to Extract Data: Posts
- Additional Data: Post IDs, Titles, URLs にチェック
- Post Status: Published
- Export Type: CSV

「Export now」をクリックし、ダイアログの「Click here」からCSVをダウンロードします。


STEP 2:Excelで置換用のSQL文を作成する
URLの「一番右側にある数字部分(日時)」を新しい post_name(スラッグ)として使うため、Excelの関数を利用して文字列を抽出します。
URLが https://kaede.jp/2020/05/02014155.html の場合、「02014155」の部分だけを取り出します。
(※C2セルにURLが入っている場合、以下の関数で最後の / 以降を抽出できます)
=MID(C2,FIND("●",SUBSTITUTE(C2,"/","●",LEN(C2)-LEN(SUBSTITUTE(C2,"/",""))))+1,LEN(C2))【重要】 生成したSQL文をphpMyAdmin等で実行する前に、必ずデータベースのバックアップを取得してください!
WordPressの post_name(スラッグ)には「.(ドット)」を使用できず、自動的に「-(ハイフン)」へ変換される仕様があります。そのため、抽出した文字列に .html などが含まれている場合は、このExcelでの作業時点で .html を削除(置換)しておくことを強くお勧めします。
STEP 3:パーマリンク設定の変更と「.html」の罠
SQLの実行が終わったら、WordPressの管理画面からパーマリンク設定を以下のように変更します。
- 旧設定:
/%year%/%monthnum%/%day%%hour%%minute%%second%.html - 新設定:
/%year%/%monthnum%/%postname%/
これで無事に表示される……と思いきや、問題が発生しました。

元のURL通りに .html を付けたままアクセスすると、404エラーになってしまいます。 実際の記事編集画面でスラッグを確認してみると……

先述した通り、WordPressの仕様で「ドット」が「ハイフン」に自動置換されてしまっていました。 いちいち400記事を手動で開き直して調整するのは非現実的なので、新URLからは .html 自体を削除し、古いURLからのアクセスはプラグインを使って一括リダイレクト(転送)させる手法をとることにしました。
Redirectionプラグインで正規表現リダイレクト
プラグインの導入と設定
URLの転送管理ができる定番プラグイン「Redirection」をインストールします。

Redirectionの機能を使って、「末尾が .html になっている古いURL」のアクセスを、「.html が無い新しいURL」へ転送する正規表現(Regex)ルールを1つだけ作成します。
- ソース URL:
^/([0-9]{4})/([0-9]{2})/([0-9]{8})\.html - URL 正規表現: チェックを入れる
- 一致したとき: URL へ転送
- HTTP コード: 301(恒久的な移動)
- ターゲット URL:
/$1/$2/$3/

これで、検索エンジンや過去のリンクから古い .html 付きのURLへアクセスされても、自動的に新しい正しい記事ページへ「301リダイレクト」されるようになります。Googleのクローラーも追従してくれるので安心です。
最後に
今回のように一部DBを直接操作する手順を踏むことで、数百記事に及ぶURLスラッグの手動設定作業を大幅にショートカットでき、数時間程度で新仕様への移行を終えることができました。 (※非公開記事も一括で変更したい場合は、wp_posts テーブルの post_status の条件を調整すれば対応可能です)
本当はSEOの観点を踏まえると、URLスラッグには記事内容を表す「英語名」を一つひとつ持たせた方が良いのですが……これまで数字のみで運用してきてしまったので、急いで無理に変更せず、今後の新しい記事から少しずつ意識していけば良いかなと思っています。
WordPressの大型アップデートでパーマリンクが壊れてしまった方の参考になれば幸いです!