Web 制作でリダイレクトを書くとき、よく出てくるのは 301 と 302 である。
301 は恒久的な転送、302 は一時的な転送。言葉だけなら簡単だが、実務では「旧 URL を恒久的に移す」「今だけトップに逃がしたい」「WordPress の URL 補正とどう分けるか」など、判断と実装が少しややこしくなる。
よしあきは最近、過去に残していた .htaccess とリダイレクトのメモを整理し直した。この記事では、301 と 302 の使い分けから .htaccess の書き方、周辺の仕組み、確認・運用までをまとめる。
301 と 302 — 使い分け
どちらも「別 URL へ転送する」HTTP ステータスコードである。違いは、その転送を恒久的とみなすか、一時的とみなすか だけに近い。
| 301(Moved Permanently) | 302(Found) | |
|---|---|---|
| 意味 | 恒久的な転送 | 一時的な転送 |
| 向く場面 | URL 移転が確定しているとき | あとで元に戻す・外す前提のとき |
| 例 | http→https、www 統一、旧ページ→新ページ、ドメイン移転 | メンテ中だけ下層をトップへ、期間限定の逃がし、仮公開の導線 |
よしあきの実務メモでは、判断を次の一文に寄せている。
あとで外す・戻す予定があるなら 302。外さないなら 301。
302 を「とりあえず飛ばす」ためではなく、戻す前提の退避として使う。301 は URL 設計の一部 として残す、と割り切ると、あとから設定を読み返しやすい。
リダイレクトまわりの分担
.htaccess だけで完結すると思いがちだが、実務では次のように層が分かれている。
| 種類 | 役割 |
|---|---|
301 / 302 リダイレクト(.htaccess など) | サーバーが応答時点で別 URL へ転送する(本記事の主役) |
WordPress の redirect_canonical() | WordPress が認識する正規 URL へ転送する |
<meta http-equiv="refresh"> | HTML を返したあと、ブラウザが一定時間後などに遷移する |
JavaScript(location.replace など) | ページ読み込み後、ブラウザ上で条件付き・即時に遷移する |
<link rel="canonical"> | 検索エンジンに正規 URL を伝える(ブラウザは動かさない) |
「ユーザーを実際に移動させたい」のか、「検索エンジンに正規 URL を伝えたい」のかで、使うものは変わる。URL の恒久移転や正規化は 301/302 を優先し、meta refresh や JavaScript は「○秒後にトップへ」など画面付きの案内や、ログイン後の分岐などに使われることが多い。いずれも curl -I では 301/302 にならず、多くは 200 OK の HTML のままである。
<meta http-equiv="refresh" content="5; url=https://example.com/">
location.replace('https://example.com/');
Apache と WordPress の層
.htaccess のリダイレクトは Apache の層で動く。PHP や WordPress が起動する前に処理できる。
.htaccess -> Apache が先に判断する
WordPress -> PHP 起動後、WordPress の URL 解釈で判断する
http から https、www 統一、固定的な旧 URL から新 URL への転送のような単純な処理は、.htaccess で先に寄せる方が扱いやすい。
投稿タイプ、タクソノミー、パーマリンク、WordPress のスラッグ解釈に依存する処理は、WordPress 側の設定やプラグインも含めて見る。.htaccess だけで無理に吸収しようとすると、アプリケーション側の正規 URL とズレることがある。
redirect_canonical() は、管理画面の「WordPress アドレス (URL)」「サイトアドレス (URL)」やパーマリンク設定を基準に、アクセス URL を補正してリダイレクトする処理である。
.htaccess の記述順
リダイレクトは、書いた順に処理される。実務では、基本的に次の順にする。
- URL 正規化(多くは 301)
- 個別リダイレクト(301 または 302)
- WordPress の rewrite
http から https、www ありから www なし、といった正規化は先に処理する。そのあとに個別ページのリダイレクトを書く。最後に WordPress の rewrite を置く。
順番を雑にすると、次のような多段転送が起きる。
http://www.example.com/about/
-> http://www.example.com/company/
-> https://www.example.com/company/
-> https://example.com/company/
本来は、最初から最終 URL に飛ばしたい。
RewriteRule ^about/?$ https://example.com/company/ [R=301,L]
301 でも 302 でも、正規化と個別転送が混ざる場合は、ブラウザが何回も転送されないように、できるだけ短い経路にする。
.htaccess の書き方 — 共通
RewriteRule の1行は、「どの URL か」→「どこへ飛ばすか」→「どう飛ばすか」 の3つに分けて読む。
- 一致条件 — 例:
^about/?$(^先頭、$末尾、/?はスラッシュ任意) - 転送先 — 例:
/やhttps://example.com/company/ - フラグ —
[R=301,L]または[R=302,L]。R=でステータス、L(Last)はその行にマッチしたときだけ以降のRewriteRuleを見ない
L がないと、マッチしたあとも下の RewriteRule を続けて評価する。301/302 のリダイレクトでは意図せず後続ルールと組み合わさりやすいので、[R=301,L] / [R=302,L] とするのが定番である。意図的に L を外すのは、ブラウザを飛ばさない内部書き換え(R なし)を次の行に渡すような上級の構成に限られる。
RewriteRule は上から順に評価される。1行目に当たらなければ次の行へ進む。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^about/?$ / [R=302,L]
RewriteRule ^contact/?$ / [R=302,L]
</IfModule>
| リクエスト | 1行目 about | 2行目 contact | 結果 |
|---|---|---|---|
/about/ | マッチ → 転送、L で終了 | (見ない) | トップへ |
/contact/ | マッチしない → 次へ | マッチ → 転送 | トップへ |
/news/ | マッチしない | マッチしない | リダイレクトしない |
末尾の $ があると、about の直後に別の文字が続くパスは対象外になる。
| パス | 上記ルールにマッチするか |
|---|---|
/about / /about/ | する |
/about-us | しない |
/about/team | しない |
例: 旧 URL を恒久的に移す(301)
/about/ を /company/ へ恒久に移すときは 301 である。
RewriteRule ^about/?$ https://example.com/company/ [R=301,L]
転送先を絶対 URL にすると、スキームやホストの正規化と組み合わせやすい。記述順の節で触れた「最初から最終 URL へ」は、このパターンに当たる。
例: 一時的にトップへ逃がす(302)
リニューアル中などで、会社概要だけ一時的に見せずトップへ逃がすときは 302 である。書き方の骨格は 301 と同じで、フラグが [R=302,L] になる点だけが違う。
RewriteRule ^about/?$ / [R=302,L]
/about-us も同様に逃がすなら、ルールを1行足す。
RewriteRule ^about-us/?$ / [R=302,L]
「トップ以外の下層をまとめてトップへ」という一括指定もあるが、静的ファイルまで巻き込みやすい。まずは逃がしたいパスを1本ずつ指定する方が、意図が読み返しやすい。
入れたら確認する
リダイレクトを入れたら、ブラウザだけでなくヘッダーも確認する。
curl -I https://example.com/about/
見るポイントは次の通り。
HTTP/2 301またはHTTP/2 302など、意図したステータスになっているかLocationが想定した URL になっているかhttp、www、末尾スラッシュで余計な多段リダイレクトが起きていないか- CSS、画像、JavaScript など必要な静的ファイルまで転送していないか
- WordPress の管理画面やログイン URL まで巻き込んでいないか
ブラウザでは最終画面だけ見えるため、途中で 301 と 302 が混ざっていても気づきにくい。curl -I でヘッダーを見ると、転送の意図が確認しやすい。
運用メモ
302 を入れるとき
一時的な転送なので、外すタイミングも一緒に決めておく。
- 何のための 302 か
- どの URL を対象にしているか
- いつ外す予定か
- 外したあと 301 へ置き換える必要があるか
- WordPress 側の設定やプラグインにも対応が必要か
302 のまま長く放置すると、「一時的なはずだった転送」が実質的な恒久運用になる。そうなったら、301 に切り替えるか、URL 設計を見直す。
301 を入れるとき
恒久的な転送なので、移転先 URL が確定していることを前提にする。旧 URL へのアクセスが残る期間は長いので、多段転送や記述順のミスは SEO・UX の両方に効きやすい。設定メモには「旧→新」の対応と、正規化ルールとの関係を書いておくとよい。
まとめ
リダイレクトは、301/302 の選択、Apache と WordPress の分担、記述順、確認方法までセットで見る話である。
よしあきの実務メモとしては、次のように整理しておく。
- あとで外すなら 302、外さないなら 301
- 単純な正規化・固定的な旧→新は
.htaccessで先に処理する - WordPress の URL 解釈が絡むものは WordPress 側も見る
- meta refresh・JavaScript はブラウザ側の遷移。恒久移転の主手段にはしない
- canonical タグは転送ではない
RewriteRuleは上から評価され、Lはマッチした行だけで効く- 入れたら
curl -IでステータスとLocationを確認する - 302 は外す日まで、301 は移転先の確定まで決めておく