HTTP / WebSpec

リダイレクトとは:サーバーがブラウザに「次はここを開いて」と言う HTTP の仕組み

コールバックとは を書いたあと、ログインの流れを追っていて、「ログイン画面へ飛ぶのも、ログインのあとで元のアプリに戻るのも、どちらもリダイレクトだ」と気づきました。では、コールバックはリダイレクトでできているのか。この記事では、リダイレクトの仕組みと種類、コールバックとの関係、気をつけることをまとめます。

ひとことで

リダイレクトは、サーバーがブラウザへの返事として、「ここではなく、次はこの URL を開いて」と言う仕組みです。ブラウザは、その URL を自分で、自動的に開き直します。

返事の中身は、3xx の番号と、Location ヘッダーの行き先だけです。

$ curl -I http://example.com/
HTTP/1.1 301 Moved Permanently
Location: https://example.com/

ブラウザは Location を見て、https://example.com/ を開き直します。利用者からは、URL が変わって別のページが出るだけに見えます。curl は、そのままでは移動せず、-L を付けると移動先までたどります。

番号の種類

番号名前恒久か一時かPOST で来たとき、移動先を開く方法
301Moved Permanently恒久(引っ越した)多くのブラウザは GET に変える(昔からの動き)
302Found一時多くのブラウザは GET に変える(昔からの動き)
303See Other一時必ず GET
307Temporary Redirect一時同じ方法・同じ中身のまま(POST なら POST)
308Permanent Redirect恒久同じ方法・同じ中身のまま
  • 恒久(301・308):ブラウザや検索エンジンが「もう新しい URL に変わった」と覚える。ブラウザが強く覚えるので、間違えると取り消しにくい。試している間は 302 を使う
  • 一時(302・303・307):今回だけの案内。次も元の URL に来る
  • POST を受けた後の移動で、方法をはっきりさせたいときは、303(GET にする)か 307・308(POST のまま)を使う

よく使われる場面

場面例よく使う番号
HTTP から HTTPS へhttp://… に来たら https://… へ301・308
URL の引っ越しページの場所や、ドメインを変えた301・308
フォームを送った後「送信完了」のページへ移す(下の PRG)303・302
ログインが要るページログインしていなければ、ログイン画面へ302
ログインの往復(OAuth・SSO)アプリ → 認証サービスのログイン画面 → アプリへ戻る302
短縮 URL短い URL から、本当の URL へ301・302

HTTP から HTTPS へ移すのは、nginx なら次の 1 行です。

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}
フォームを送った後に使う理由(PRG)

フォームを POST で送った返事に、そのまま「完了」のページを返すと、利用者がブラウザの「更新」を押したときに、同じ POST がもう一度送られます(注文が 2 回になる、など)。そこで、POST の返事ではリダイレクト(303)だけを返し、「完了」のページは GET で開き直させます。これを Post / Redirect / Get(PRG) と呼びます。更新しても、GET が繰り返されるだけになります。

コールバックとの関係

ログインの流れを、リダイレクトに注目して見ると、こうなります。

  1. 利用者がアプリでログインを始める
  2. アプリが返す リダイレクト①:「認証サービスのログイン画面へ行って」(送り出す)
  3. 利用者が、認証サービスでログイン・同意する
  4. 認証サービスが返す リダイレクト②:「アプリの /callback へ戻って」(送り返す。認可コードを付ける)
  5. ブラウザが /callback を開く

アプリと認証サービスは、ブラウザの画面を介しては直接話せません。そこで、ブラウザに運び役をさせて、2 つのサイトを行き来させます。行きと帰りの両方がリダイレクトです。

ただし、コールバックと呼べるのは帰り(リダイレクト②)だけです。コールバックは「前もって渡しておいた連絡先へ、戻ってくる」ことで、行き(リダイレクト①)は送り出しているだけだからです。

言葉何か
コールバック「終わったら、ここに戻して」という考え方
リダイレクトブラウザに「次はここを開いて」と言う、HTTP の仕組み

ブラウザを戻すコールバック(ログイン・決済)は、リダイレクトという仕組みを使って実現されています。一方、Webhook のコールバックは、相手のサーバーが直接 POST してくるもので、リダイレクトは使いません(Webhook とは)。リダイレクトがすべてコールバックというわけでも、コールバックがすべてリダイレクトというわけでもありません。

気をつけること

1. 行き先を、外から自由に決めさせない(オープンリダイレクト)

https://example.com/login?next=… のように、行き先を URL で受け取って、そのままリダイレクトする作りはよくあります。ここを確かめないと、next=https://悪いサイト/ を仕込んだ URL を作られ、信用されているドメインから、偽のサイトへ飛ばすのに使われます(フィッシング)。行き先は、自分のサイトの中か、前もって決めた一覧の中だけに限ります。

OAuth で、認証サービスに戻り先の URL(リダイレクト URI)を前もって登録するのは、まさにこのためです。登録していない URL には、認可コードを付けて戻しません。

2. URL に載せたものは、残ると思う

リダイレクトの行き先の URL に付けたもの(?code=… など)は、ブラウザの履歴・サーバーのログ・アドレスバーに残ります。だから、ログインの流れでも、URL に載せて運ぶのは、短い時間で 1 回しか使えない認可コード(引換券)です。トークンそのものは載せません。

3. 行ったり来たりの繰り返し(リダイレクトのループ)

A から B へ、B から A へとリダイレクトし合うと、ブラウザは「リダイレクトが多すぎます」(ERR_TOO_MANY_REDIRECTS)で止まります。HTTPS への移動を、アプリと前のリバースプロキシの両方で行っているときなどに起きやすいです。

まとめ

  • リダイレクトは、3xx と Location で、ブラウザに「次はここを開いて」と言う HTTP の仕組み。ブラウザが自動で開き直す
  • 恒久(301・308)と一時(302・303・307)、POST のときに方法を変えるか(301・302・303)変えないか(307・308)で番号を選ぶ
  • ログインの往復は、行きも帰りもリダイレクト。そのうち帰りがコールバック
  • 行き先を外から自由に決めさせない、URL に載せたものは残ると思う、ループに気をつける
スポンサーリンク
タイトルとURLをコピーしました