Last updated on

.htaccess リダイレクト実務メモ — 301 と 302 の使い分け

  • リダイレクト
  • Apache
  • WordPress
  • Web制作

Web 制作でリダイレクトを書くとき、よく出てくるのは 301 と 302 である。

301 は恒久的な転送、302 は一時的な転送。言葉だけなら簡単だが、実務では「旧 URL を恒久的に移す」「今だけトップに逃がしたい」「WordPress の URL 補正とどう分けるか」など、判断と実装が少しややこしくなる。

よしあきは最近、過去に残していた .htaccess とリダイレクトのメモを整理し直した。この記事では、301 と 302 の使い分けから .htaccess の書き方、周辺の仕組み、確認・運用までをまとめる。

301 と 302 — 使い分け

どちらも「別 URL へ転送する」HTTP ステータスコードである。違いは、その転送を恒久的とみなすか、一時的とみなすか だけに近い。

301(Moved Permanently)302(Found)
意味恒久的な転送一時的な転送
向く場面URL 移転が確定しているときあとで元に戻す・外す前提のとき
httphttpswww 統一、旧ページ→新ページ、ドメイン移転メンテ中だけ下層をトップへ、期間限定の逃がし、仮公開の導線

よしあきの実務メモでは、判断を次の一文に寄せている。

あとで外す・戻す予定があるなら 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 から httpswww 統一、固定的な旧 URL から新 URL への転送のような単純な処理は、.htaccess で先に寄せる方が扱いやすい。

投稿タイプ、タクソノミー、パーマリンク、WordPress のスラッグ解釈に依存する処理は、WordPress 側の設定やプラグインも含めて見る。.htaccess だけで無理に吸収しようとすると、アプリケーション側の正規 URL とズレることがある。

redirect_canonical() は、管理画面の「WordPress アドレス (URL)」「サイトアドレス (URL)」やパーマリンク設定を基準に、アクセス URL を補正してリダイレクトする処理である。

.htaccess の記述順

リダイレクトは、書いた順に処理される。実務では、基本的に次の順にする。

  1. URL 正規化(多くは 301)
  2. 個別リダイレクト(301 または 302)
  3. WordPress の rewrite

http から httpswww ありから 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行目 about2行目 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 になっているか
  • httpwww、末尾スラッシュで余計な多段リダイレクトが起きていないか
  • 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 は移転先の確定まで決めておく

関連記事