WordPressから静的サイトへ移行してもアプリストアのURLを壊さない方法

24件のストア掲載がWordPressのページをプライバシーポリシーURLにしていた。LiteSpeed上で登録URLをすべて200のまま保ち、5分で切り替えた記録。

WordPressから静的サイトへの移行記事は多いですが、ほとんど触れられていない問題があります。そのURLがアプリストアに登録されている場合です。私たちのサイトでは、App Store と Google Play の掲載24件が WordPress の固定ページをプライバシーポリシーURLとして使い、さらに8件がWordPressの「サポート」ページを指していました。どれか1つでも404になれば、審査で差し戻される可能性があり、ストアの「サポート」をタップしたユーザーにはエラー画面が出ます。

この記事は、Hostinger の共有サーバー(Webサーバーは LiteSpeed、前段に Hostinger CDN)で tlaboratory.net を WordPress から Python 製の静的サイトに移し、登録URLを1つも止めずに切り替えた記録です。数値はすべて 2026年11月8日に本番サーバーで計測したものです。

移行対象

項目 件数 条件
WordPress投稿(2021年の講座記事、韓国語スラッグ) 67 新URLへ301
WordPress固定ページ 19 うち13件がアプリのサポートページ
このドメインのストア登録URL 16種類 200を返し続けること
WordPressのプライバシーページを使う掲載 24 URLはそのまま、内容だけ更新
同じドキュメントルートにあるアプリ用JSON API 8フォルダ 一切影響させない

慎重にならざるを得なかった理由は最後の行です。同じ public_html には、インストール済みの16本のアプリが起動時に叩く広告設定APIもありました。ルートの .htaccess に構文エラーがあれば、ブログだけでなくドメイン配下すべてが500になります。

方針:リダイレクトせず「その場で返す」

旧URLごとに2種類の扱いを決めました。

旧URLの種類 挙動 理由
App Store Connect / Play Console に登録済み 内部リライトで200 URL文字列が永久に有効。審査ボットがリダイレクトでつまずく余地がない
WordPressの投稿・カテゴリ・フィード 新URLへ301 通常のSEO移行
どこにも登録されていない固定ページ 該当アプリの新サポートページへ301 検索流入のみ

内部リライトはリクエストのURLを変えずに、別のファイルを返します。

# ストア登録URL → 同じURLのまま新ページを200で返す
RewriteRule ^개인정보\-처리방침/?$ privacy/index.html [L]
RewriteRule ^technical\-support\-of\-the\-「宝くじ当選情報」/?$ app/takarakuji/support/index.html [L,NC]
RewriteRule ^HorseRace/support\.html/?$ app/horserace/support/index.html [L]

新しく登録するアプリには /app/<id>/support/ と /app/<id>/privacy/ という整理されたURLを使い、既存の登録は各アプリの次のアップデート時に少しずつ書き換えれば済みます。全アプリを一斉に更新する必要はありません。

LiteSpeedは日本語URLをどう照合するか

旧URLの半分には日本語や韓国語が入っていて、しかもストアごとに保存されたエンコードが違いました。%E3%80%8C(大文字)の掲載もあれば %e3%80%8c(小文字)もあり、ある掲載は US%20Lottery%20Infomation とスペース・大文字で登録されていたのに、WordPress自身のスラッグは us-lottery-infomation でした。

推測で書かず、本番サーバーの隔離フォルダで実測しました。スクリプトが /_rwtest_20261108/ を作り、そのフォルダ内だけに効く .htaccess を置いて15件のリクエストを送り、finally で必ずフォルダごと削除します。ルートの .htaccess には触れません。

OK   200 GET  韓国語・小文字エンコード
OK   200 GET  韓国語・大文字エンコード
OK   200 GET  韓国語・末尾スラッシュなし
OK   200 GET  US 大文字 + %20(ストア登録の形)
OK   200 GET  US 小文字 + ハイフン(WordPressのスラッグ)
OK   200 GET  日本語 「宝くじ当選情報」
OK   301 GET  韓国語の投稿URL → 新スラッグ
...
結果: 15/15

わかったこと:

  • パターンはデコード後のパスと比較される。 .htaccess にUTF-8で直接書いた文字は、どのエンコードのリクエストにも一致しました。比較用にパーセントエンコードで書いたルール(^%EB%A7%81...)は一度も一致しませんでした。
  • [NC] で大文字小文字は吸収できるが、スペースとハイフンの違いは吸収できない。 登録URLにスペースがある箇所は [\s-] を生成するようにしました。
  • ルールを生成するときはエスケープの重なりに注意する。 LiteSpeed 自体は \、\s、スペースを含むパターンを引用符で囲んだ書き方のどれも受け付けます(後日3つとも200を実測)。問題は生成スクリプトが \\(二重エスケープ)を出力していたことで、そのままならiOSのサポートURLが黙って壊れていました。エスケープが何重にかかっても崩れない \s に切り替えました。

さらに、生成したルート .htaccess 全体(ルール207本)を隔離フォルダ用に書き換えてもう一度アップロードし、構文エラーがないことを本番ルートに置く前に確認しました。

切り替え前に踏みやすい罠

1. ファイルを置いた瞬間にURLの中身が変わる。 WordPressは /privacy/ をフロントコントローラー経由で返していました。そこへ静的ビルドの privacy/index.html をアップロードした瞬間、.htaccess を替える前でも、そのURLは新しいページを返し始めます。つまりプライバシーポリシーの文面は「切り替え前」ではなく「最初のアップロード前」に確定させる必要があります。

2. 自分で作っていないファイル。 デプロイスクリプトは自分がアップロードしたファイルの一覧(マニフェスト)を持っています。ドライランで、サーバーに既にある robots.txt が自分のものではないと検出しました。中には Daum のサイト確認用の行があり、上書きすれば認証が外れるところでした。スクリプトはこの場合に停止し、明示的に許可したときだけ、元ファイルを backup/ にダウンロードしてから上書きします。

切り替えのタイムライン

時刻(JST) 作業 結果
23:01 事前チェック:アプリ用URL、APIスナップショット95件、ビルド検査 8/8、差分0、エラー0
23:02 静的ファイル240件をアップロード robots.txt をバックアップ
23:06 ルート .htaccess を差し替え(旧版はローカルとサーバーに保存) —
23:06 アプリに直結する8URLの簡易チェック 8/8
23:07 全URLマトリクス:ストアURL17件、301の抜き取り、新ページ、404 53/53
23:09 APIの回帰比較 実質差分0

WordPressのファイルはサーバーに残したままです。新しい .htaccess が index.php へ何も渡さなくなっただけなので、ロールバックは旧 .htaccess を戻すコマンド1つで済みます。

API比較では差分が4件出ましたが、いずれも移行が原因ではありませんでした。2件は意図して差し替えたサポートページ、1件は一時的なタイムアウト、残り1件は競馬の配当ランキングで、同額の行の並び順がリクエストごとに変わるものでした(898行の中身は同一。ORDER BY に同順位の決め手がないため)。

切り替え後:CDNのキャッシュ

サイトは Hostinger のCDNの後ろにあります。hPanelでキャッシュを削除するまで、一部のURLは x-hcdn-cache-status: HIT で古いWordPressの内容を返していました。チェックスクリプトはキャッシュ回避のクエリ付きで送っていたので通っていたのです。検証はクエリ付きとなしの両方で行ってください。なしの結果はサーバーではなくCDNを見ています。

チェックリスト

  • 作業前に、ストアに登録されたURLをすべて洗い出し、URLごとに「その場で返す/301」を決める
  • リライトの挙動は本番サーバーの使い捨てサブフォルダで実測する。生成したファイル全体も試す
  • 変更前にすべてのAPIレスポンスを保存し、変更後に差分を取る
  • デプロイスクリプトには「自分が作っていないファイルは上書きしない」制約を入れる
  • 旧システムのファイルはしばらく残し、ロールバックをコマンド1つにしておく
  • CDNを削除してから、キャッシュ回避ありとなしの両方で再確認する

次の記事では、同じ夜に行った後半の作業、つまりインストール済みのどのバージョンのアプリも止めずに、レガシーなPHPのAPIフォルダ8つを /api/v1/ に移した手順を書きます。

よくある質問

ストアに登録したサポートURLは301でリダイレクトしてよい?

今回は避けました。旧URLは内部リライトで新しいページを返すため、登録したURLそのものが200で応答します。審査や外部リンクからはリダイレクトが見えず、ストア側の登録変更は各アプリの次回アップデート時に回せます。

LiteSpeedのRewriteRuleはエンコード前とデコード後、どちらのURLと比較する?

デコード後のパスです。日本語・韓国語をUTF-8のまま書いたルールが、小文字・大文字のパーセントエンコードやスラッシュなしのリクエストすべてに一致しました。パーセントエンコードで書いたルールは一度も一致しませんでした。

実際の切り替えにかかった時間は?

約5分です。静的ファイルのアップロード、ルート.htaccessの差し替え、アプリ用URLの簡易チェック、全URLとAPIの回帰チェックの順に進めました。

  • #WordPress
  • #静的サイト
  • #htaccess
  • #LiteSpeed
  • #App Store
  • #移行

t.laboratory

実際に作って運用しているアプリ・サーバー・サービスで起きた問題と、その解決に使ったコードを記録する技術ブログです。

運営者情報 · アプリ

↑