WordPressのSQLダンプからHugoのaliasesで旧URLリダイレクトを設定する

WordPressからHugoへブログを移行したものの、WordPress時代のURL /2023/02/12/9136/ のような形式で外部からリンクされているページが大量にある。Hugo側のURLは /p/{slug}/ という全く別の構造なので、そのままだと旧URLが全て404になってしまう。

Hugoの aliases 機能とWordPressのSQLダンプを組み合わせることで、nginx設定を触らずにリダイレクトできたのでメモしておく。

WordPressのURL構造

WordPress(デフォルトのパーマリンク設定)では投稿のURLはこの形式になっている。

/YYYY/MM/DD/{投稿ID}/

2023年2月12日に公開された投稿ID 9136の記事なら /2023/02/12/9136/ になる。この情報は全て wp_posts テーブルに入っている。

wp_postsから取り出すカラム

SQLダンプの wp_posts テーブルのカラム定義はこんな感じ。

CREATE TABLE `tblog_wp_posts` (
  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `post_author` bigint(20) unsigned NOT NULL DEFAULT '0',
  `post_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `post_date_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `post_content` longtext NOT NULL,
  `post_title` mediumtext NOT NULL,
  `post_excerpt` mediumtext NOT NULL,
  `post_status` varchar(20) NOT NULL DEFAULT 'publish',
  ...
  `post_name` varchar(200) NOT NULL DEFAULT '',
  ...
  `post_type` varchar(20) NOT NULL DEFAULT 'post',
  ...
);

旧URLの復元とHugo記事への紐付けに使うのはこの4つ。

カラム 役割
ID 投稿ID。旧URLのパスの一部 9136
post_date 投稿日時。旧URLの年月日部分 2023-02-12 22:11:10
post_name スラッグ。Hugo側の記事とマッチさせるキー xremapを利用して特殊なショートカットをマッピング
post_type 投稿タイプ。post のみ対象 post

post_statuspublish または draft のものだけフィルタする。

旧URLの組み立て

post_date からYYYY・MM・DDを抽出して ID と組み合わせるだけ。

year  = post_date[0:4]   # "2023"
month = post_date[5:7]   # "02"
day   = post_date[8:10]  # "12"
post_id = "9136"

old_url = f"/{year}/{month}/{day}/{post_id}/"
# → /2023/02/12/9136/

Hugo記事とのマッチング

Hugo側の記事ファイルには front matter に slug が設定されている。

---
title: "xremapを利用して特殊なショートカットをマッピングしてみる"
slug: "xremapを利用して特殊なショートカットをマッピング"
---

SQLダンプの post_name カラムがWordPress側のスラッグに相当する。WordPress→Hugo変換時に post_name をそのまま slug として使っているので、この値でマッチングできる。

post_name が空の投稿(下書きなど)は変換時に post-{ID} というスラッグを割り当てているので、同じルールでフォールバックする。

slug = post_name if post_name else f"post-{post_id}"

Hugoのaliases

Hugo には front matter に aliases を指定すると、そのパスにリダイレクト用のHTMLを自動生成してくれる機能がある。これが便利。

---
title: "xremapを利用して特殊なショートカットをマッピングしてみる"
date: 2023-02-12 22:11:10
slug: "xremapを利用して特殊なショートカットをマッピング"
aliases:
  - /2023/02/12/9136/
categories:
  - Tech
---

この設定だけでHugoのビルド時に /2023/02/12/9136/index.html が生成される。中身はmeta refreshによるリダイレクトHTMLで、アクセスすると /p/xremapを利用して特殊なショートカットをマッピング/ に自動遷移する。

nginxやApacheのrewrite設定は一切不要で、Hugo単体で完結する。GitHub PagesやCloudflare Pagesのような静的ホスティングでもそのまま動く。

スクリプトで一括処理

1,300件以上あるので手作業は無理。Pythonスクリプトで一括処理した。

処理の流れはシンプルで、

  • SQLダンプの INSERT INTO 文をパースして各投稿の ID, post_date, post_name を取得
  • post_name(スラッグ)をキーにして content/post/ 内のMarkdownファイルを特定
  • front matter に aliases: ["/YYYY/MM/DD/{ID}/"] を挿入

front matter の編集では、既存の aliases があればマージし、なければ slug: 行の直後に挿入するようにしている。

実行結果。

WordPress投稿数: 1327
Markdownファイル数: 1327
更新: 1326
マッチなし: 1

1,327件中1,326件で追加できた。マッチしなかった1件はスラッグの重複回避処理で名前が変わっていたケースなので、実質的には全件対応できている。

あとはHugoをビルドし直せば旧URLからのリダイレクトが有効になる。サーバー側の設定変更なしでここまでできるのはありがたい。

Hugo で構築されています。
テーマ StackJimmy によって設計されています。