WordPress4.5.1から特定のパーマリンクでシングルページが見れなくなる問題と対策

WordPress4.5.1で当ブログに障害が発生してしまったので、対応の流れを記載。

現象

シングルページを開こうとすると、その記事だけが含まれるアーカイブページが出てくる。

こんな感じになって、記事にたどり着けない。4.5.1はマイナーアップデートとして自動適用されたので面食らった。

原因

WordPress4.5.1から「秒単位までの投稿時間で構成したパーマリンク」が無効になり、投稿IDかスラッグを含めることが必須になったが、含めていなかったため。

当ブログはパーマリンクを「/%year%/%monthnum%/%day%/%hour%%minute%%second%/」つまり「/年/月/日/時分秒/」としていた。
採用理由は…一応記事と関連する数字が使われて、かつ桁が不変だからだったかな。結局そこにこだわる意味は無かったのだが。

この形式は、パーマリンクに秒までの完全な投稿時間を入れると、投稿IDやスラッグが無くてもシングルページと判定される仕様を利用したものだった。
が、どうやらお情け的に存在していた仕様だったらしく、4.5.1でフッと廃止された模様(正確な経緯は知らない)。
その結果、シングルページではなくアーカイブの判定に変わってしまい、本文が表示できなくなった。

応急処理

とにかくすぐ復旧する場合は、WPを4.5.1以前の、4.5などにダウングレードさせればよかった。
手段は何でもいいが、筆者は以下の記事を参考に「WP Downgrade Specific Core Version」プラグインで下げた。
これなら簡単にダウングレードさせつつ、プラグインが有効な間はアプデを止められるようだ。

【WordPress】バージョン5.4.1においてパーマリンクの設定により投稿ページが表示されない不具合が発生|HTMLてくてく道

もちろんこれは急場しのぎであり、このままでは今後永遠にWPのアプデができない。根本解決を行う必要がある。

解決策

パーマリンクを変えてリダイレクトをかけるか、従来のURLでシングルページを呼び出す処理を入れるかの二択になる。

mode-n | パーマリンク判定仕様変更による対処方法 (暫定)

上の記事だとシングルページを呼ぶ場合のコードが載っている。URL変えずに済むので、総合的には一番楽な解決法だと思う。筆者は試してないけど。

当ブログではパーマリンクを「/%year%/%monthnum%/%post_id%/」に変えてリダイレクトすることにした。
理由はこうすりゃよかったなあと前から思っていたのと、非推奨から禁止になったような構造を続けるのもあんま気分がよくないため。気分がすべて。

リダイレクトする場合の処理は見つからなかったので自力で組むことに。functions.phpに以下のように書いた。

add_action('init', 'changePermalink');
function changePermalink()
{
    $url = $_SERVER['REQUEST_URI']; // URL取得
    if (preg_match('!/([0-9]{4})/([0-9]{2})/([0-9]{2})/([0-9]{2})([0-9]{2})([0-9]{2})/!', $url, $postTime) !== 1) {
        return; // 「/年/月/日/時分秒/」を含むURLじゃなかったら中止
    }
    $query = new WP_Query(array(  // クエリー設定
        'post_type' => 'post', // 投稿タイプ
        'post_status' => 'publish', // 公開済みの記事
        'date_query' => array(
            'year' => $postTime[1],
            'month' => $postTime[2],
            'day' => $postTime[3],
            'hour' => $postTime[4],
            'minute' => $postTime[5],
            'second' => $postTime[6],
        ),
    ));
    if ($query -> have_posts()) {
        $query -> the_post();
        header('HTTP/1.1 301 Moved Permanently');
        header('Location:' . get_the_permalink()); // 検出した記事へ301リダイレクト
        exit;
    }
    wp_reset_postdata();
}

changePermalinkの矛盾感。

上の参考記事のコードに乗っかって$wp_query->query_varsに入ってる値で解決しちゃおうと思ったが、なんか値が年月までしか取れなかったので断念。旧パーマリンクでは時分秒を/で区切ってなかったせいだろうか。
そのためpreg_matchでURLを自力解析することに。できれば取っ掛かりのIF文で正規表現なんてやりたくなかったが…。

あとはWP_Queryのdate_queryで完全な投稿時間を指定、記事を特定してリダイレクトさせるという流れ。
もうちょっと洗練できそうな気はするが、本当にユニークなURLであったのならばとりあえずこれで飛べるはず。

パーマリンクを変えることによってどうしても残るデメリットとして、SEO面の弱体化と、アクセス解析等の計測リセットがある。
SEOは301リダイレクトを信じる。趣味ブログだから多少落ちぶれてもまあ構わない。
計測は気にするかどうかによる。シェアボタンにつけてたシェア数が0になるのは気になったが…ぶっちゃけシェア数なんてどうでもいいのでカウント処理自体消した。