HTTP のリダイレクト

URL リダイレクトは、 URL 転送とも呼ばれ、ページ、フォーム、ウェブサイト全体、ウェブアプリケーションなどに 2 つ以上の URL のアドレスを与える技術です。 HTTP ではこの操作のために、特別な種類のレスポンスである HTTP リダイレクトを提供しています。

リダイレクトには多くの目的があります。

  • サイトをメンテナンスしている間の一時的なリダイレクト
  • サイトの構成を変更した後も外部のリンクを機能させるための恒久的なリダイレクト、ファイルをアップロードしているときの進捗を示すページなど

原理

HTTP では、リダイレクトはリクエストに対して、サーバーが特別なリダイレクトレスポンスを送信することによって、サーバーがリダイレクトを発生させます。リダイレクトレスポンスはステータスコード3 で始まり、 Location ヘッダーがリダイレクト先の URL を保持しています。

ブラウザーがリダイレクトを受け取ると、 Location ヘッダーで提供された新たな URL を使用して直ちに読み込みを行います。追加の往復によるパフォーマンスの低下の他に、ユーザーがリダイレクトに気づくことはほとんどありません。

最初のリクエストはクライアントからサーバーに送られます。サーバーはリダイレクト先のURLとともに 301:moved permanently で応答します。クライアントは新しい URL への GET リクエストを行い、サーバーは 200 OK レスポンスでこれを返します。

リダイレクトには何種類かがありますが、3 つのカテゴリーに分類することができます。

  1. 恒久的リダイレクト
  2. 一時的リダイレクト
  3. 特殊リダイレクト

恒久的リダイレクト

これらのリダイレクトは永遠に続くことを意味します。これらのリダイレクトは、元の URL はもう使用されず、新しいものに置き換えるべきであることを示しています。検索エンジンのロボット、 RSS リーダー、および他のクローラーは、リソースの元の URL を更新します。

コード テキスト メソッドの扱い 主な使用例
301 Moved Permanently GET メソッドは変更しません。 他のメソッドは GET に変更されるかもしれません。[1] ウェブサイトの再編。
308 Permanent Redirect メソッドや本文は変更しません。 GET 以外のリンクや操作を含むウェブサイトの再編。

[1] 仕様書ではメソッドの変更を意図していませんが、メソッドを変更するユーザーエージェントが存在します。 308 が定義されたのは、 GET 以外のメソッドを使用するときの動作のあいまいさをなくすためです。

一時的リダイレクト

正規の場所でレスポンスしたリソースにアクセスすることはできませんが、別の場所でアクセスできる場合があります。このような場合に、一時的なリダイレクトを使用できます。

検索エンジンのロボットは、新たな一時的 URL を記録しません。一時的リダイレクトは、リソースを作成、更新、削除しているときに一時的な進捗ページを提供するためにも利用されます。

コード テキスト メソッドの扱い 主な使用例
302 Found GET メソッドは変更しません。 他のメソッドは GET に変更されるかもしれません。[2] ウェブページは不測の理由により、一時的に利用できない状態です。
303 See Other GET メソッドは変更しません。 他のメソッドは GET変更します (本文は失われます)。 ページの再読み込みによって操作が再度実施されることを防ぐために、PUTPOST の後のリダイレクトで使用します。
307 Temporary Redirect メソッドと本文は変更しません。 ウェブページは不測の理由により、一時的に使用できない状態です。検索エンジンは自身のリンクを更新しません。 302 と比較して、サイトで GET 以外の操作を使用できる場合に推奨されます。

[2] 仕様書ではメソッドの変更を意図していませんが、実際はメソッドを変更するユーザーエージェントが存在します。GET 以外のメソッドを使用するときの動作のあいまいさをなくすために、 307 が定義されました。

特殊リダイレクト

304 (Not Modified) は、ページをローカルにキャッシュした(陳腐化した)複製へリダイレクトします。また 300 (Multiple Choice) は、手動リダイレクトです。ブラウザーがウェブページとして表示する本文には使用可能なリダイレクトのリストがあり、ユーザーはひとつ選択してクリックします。

コード テキスト 主な使用例
300 Multiple Choice 多くはありません。本文内の HTML ページに選択肢の一覧があります。 Link ヘッダーに rel=alternate がある場合は、機械可読な選択肢を提供することが推奨されます。
304 Not Modified 再検証された条件付きリクエストのために送信されます。キャッシュされたレスポンスがまだ新鮮で使用可能であることを示します。

リダイレクトを指定する代替手段

HTTP リダイレクトは、リダイレクトを定義する唯一の手段ではありません。他にも 2 つの方法があります。

  1. <meta> 要素を使用する HTML リダイレクト
  2. DOM による JavaScript リダイレクト

HTML リダイレクト

HTTP リダイレクトはリダイレクトを作成するための最良の方法ですが、サーバーの制御権を持っていない場合もあります。このような場合は、そのページの <head> 内に <meta> 要素を記述し、その http-equiv 属性に Refresh を設定してみてください。ページを表示すると、ブラウザーは示されたページへ移動します。

html
<head>
  <meta http-equiv="Refresh" content="0; URL=https://example.com/" />
</head>

content 属性は、指定した URL へリダイレクトする前にブラウザーが何秒待つべきかを示す値から始まります。アクセシビリティを高めるため、常に 0 を設定しましょう。

当然ながらこの方法は HTML でしか動作せず、画像などのコンテンツでは使用できません。

JavaScript リダイレクト

JavaScript のリダイレクトは window.location プロパティに URL 文字列を設定することで実行され、新たなページが読み込まれます。

js
window.location = "https://example.com/";

HTML リダイレクトと同様にすべてのリソースでは動作できず、また JavaScript を実行するクライアントでしか動作しないことは明らかです。一方、例えば特定の条件に一致した場合にのみリダイレクトを行うなど、さまざまな可能性があります。

優先順位

リダイレクトを起動する方法が 3 種類あり、同時に複数の方法を指定できますが、どのリダイレクトが最初に適用されるのでしょうか?

  1. HTTP リダイレクトが常に最初に動作します。 — 転送されるページがない場合でも存在するからです。
  2. HTTP リダイレクトが存在しなければ、HTML リダイレクト (<meta>) が動作します。
  3. JavaScript リダイレクトが最後に、 JavaScript が有効であれば実行されます。

可能であれば常に HTTP リダイレクトを使用して、<meta> は追加しないようにしましょう。誰かが HTTP リダイレクトを変更して HTML リダイレクトを変更し忘れると、リダイレクトが同一ではなくなったり、無限ループになったり、その他の問題が発生したりします。

使用例

リダイレクトの使用例は多数ありますが、どのリダイレクトもパフォーマンスへの影響がありますので、使用は最小限にとどめるべきです。

ドメインの別名

理想的なのは、ひとつのリソースに対してひとつの場所、そしてひとつの URL が存在することです。しかし、リソースに別名を設定する理由はいくつかあります。

サイトへの到達方法を拡大するため

よくある事例は、サイトが www.example.com ドメイン配下に存在しているとき、example.com からもアクセスできるようにしたい場合です。この場合は、example.com から www.example.com へのリダイレクトを設定します。また、一般的に使用される同義語や、ドメイン名の打ち間違いで頻度が多いものを提供してもよいでしょう。

別のドメインに移動するため

例えば、会社名が変わっても、既存のリンクやブックマークからも新しい会社名を見つけられるようにしたいでしょう。

HTTPS を強制するため

http:// 版のサイトへのリクエストを https:// 版のサイトにリダイレクトします。

リンクの存続

ウェブサイトを再構築すると URL が変わります。サイトのリンクを新しい URL に合うよう更新しても、外部リソースからのリンクで使用されている URL を制御することはできません。

外部のリンクは貴重なユーザーを連れてきてくれるし、 SEO にも役立つため、リンクを壊したくはありません。よって、古い URL から新しい URL へのリダイレクトを設定します。

メモ: この手法は内部のリンクにも有効ですが、内部のリダイレクトは避けるようにしてください。リダイレクトは(追加の HTTP リクエストを行うため)性能の負担がかなりあります。内部のリンクを修正することでこれを避けられるのであれば、リンクを修正してください。

安全でないリクエストへの一時的なレスポンス

安全でないリクエストはサーバーの状態を変更するものであり、ユーザーがうっかり再実行するべきではありません。

一般的に、ユーザーは PUTPOSTDELETE のリクエストを再送信することは望みません。リクエストの結果としてレスポンスを返すだけである場合は、単に再読み込みボタンを押すことで (おそらく確認メッセージの後に)、リクエストを再送信します。

この場合、サーバーは正しい情報を持つ 303 (See Other) レスポンスを返すことができます。一方、再読み込みボタンが押された場合はページを再表示するだけであり、安全でないリクエストを再実行しません。

長いリクエストに対する一時的なレスポンス

DELETE リクエストを後で処理するように予定するなど、リクエストによってはサーバー側で長い時間が必要になる場合があります。この場合、レスポンスを 303 (See Other) として操作が実行予定に追加されたことを示すページにリンクし、最終的に進捗を確認したり、キャンセルできるようにしたりするようにします。

一般的なサーバーにおけるリダイレクトの設定

Apache

リダイレクトはサーバーの設定ファイルか、各ディレクトリの .htaccess で設定できます。

mod_alias モジュールには Redirect および RedirectMatch ディレクティブがあり、これらは既定で 302 レスポンスを設定します。

xml
<VirtualHost *:443>
  ServerName example.com
  Redirect / https://www.example.com
</VirtualHost>

URL https://example.com/https://www.example.com/ にリダイレクトされ、その下のファイルやディレクトリも同様です (https://example.com/some-pagehttps://www.example.com/some-page にリダイレクトされます)。

RedirectMatch も同じですが、対象の URL の集合を定義するために正規表現を使用します。

RedirectMatch ^/images/(.*)$ https://images.example.com/$1

images/ フォルダー内のすべての文書が、別のドメインにリダイレクトされます。

一時的なリダイレクトを設定したくない場合は、別の種類のリダイレクトを設定するために追加引数 (使用する HTTP ステータスコードまたは permanent キーワード) を使用できます。

Redirect permanent / https://www.example.com
# …acts the same as:
Redirect 301 / https://www.example.com

mod_rewrite モジュールでリダイレクトを作成することもできます。こちらはさらに柔軟性がありますが、若干複雑です。

Nginx

Nginx では、リダイレクトしたいコンテンツ用の server ブロックを作成します。

server {
  listen 80;
  server_name example.com;
  return 301 $scheme://www.example.com$request_uri;
}

ディレクトリまたは特定のページにのみリダイレクトを適用するには、rewrite ディレクティブを使用します。

rewrite ^/images/(.*)$ https://images.example.com/$1 redirect;
rewrite ^/images/(.*)$ https://images.example.com/$1 permanent;

IIS

IIS では、<httpRedirect> 要素を使用してリダイレクトを設定します。

リダイレクトループ

追加のリダイレクトが、すでに通っている経路をたどるとリダイレクトループが発生します。言い換えると終わらないループが存在しており、最終的に見つかるページはありません。

ほとんどの場合はサーバーの問題であり、サーバーで検出できる場合は 500 Internal Server Error を返すでしょう。サーバーの設定を変更した直後にこのようなエラーが発生した場合は、リダイレクトループが発生しているかもしれません。

時々、サーバーがリダイレクトループを検出しないことがあります。それぞれのサーバーでは全貌を把握できない、複数のサーバーにわたるリダイレクトループがあり得ます。この場合はブラウザーがループを検出して、エラーメッセージを表示するでしょう。Firefox では以下のメッセージを表示します。

Firefox は、サーバーがこのアドレスへのリクエストを決して終了しない方法でリダイレクトしていることを検出しました。

Chrome では以下のように表示されます。

このウェブページにはリダイレクト ループが含まれています

どちらの場合も、ユーザーができることはほとんどありません (キャッシュや Cookie の不一致など、ユーザー側で問題が発生している場合を除きます)。

リダイレクトループは使い勝手をを完全に損ないますので、避けることが重要です。