コールバックとは を書いたあと、ログインの流れを追っていて、「ログイン画面へ飛ぶのも、ログインのあとで元のアプリに戻るのも、どちらもリダイレクトだ」と気づきました。では、コールバックはリダイレクトでできているのか。この記事では、リダイレクトの仕組みと種類、コールバックとの関係、気をつけることをまとめます。
ひとことで
リダイレクトは、サーバーがブラウザへの返事として、「ここではなく、次はこの 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 で来たとき、移動先を開く方法 |
|---|---|---|---|
301 | Moved Permanently | 恒久(引っ越した) | 多くのブラウザは GET に変える(昔からの動き) |
302 | Found | 一時 | 多くのブラウザは GET に変える(昔からの動き) |
303 | See Other | 一時 | 必ず GET |
307 | Temporary Redirect | 一時 | 同じ方法・同じ中身のまま(POST なら POST) |
308 | Permanent 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 が繰り返されるだけになります。
コールバックとの関係
ログインの流れを、リダイレクトに注目して見ると、こうなります。
- 利用者がアプリでログインを始める
- アプリが返す リダイレクト①:「認証サービスのログイン画面へ行って」(送り出す)
- 利用者が、認証サービスでログイン・同意する
- 認証サービスが返す リダイレクト②:「アプリの
/callbackへ戻って」(送り返す。認可コードを付ける) - ブラウザが
/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 に載せたものは残ると思う、ループに気をつける
