XMLHttpRequestでクロスドメインリクエスト
つまり
XMLHttpRequestでクロスドメインのリクエストを送って、リクエスト先でCookie付けたりリクエスト元に値を返したりするときは、XMLHttpRequest・レスポンスヘッダに↓をつけること。
- XMLHttpRequest
withCredentials: true
- レスポンスヘッダ
Access-Control-Allow-Origin: (リクエスト元ドメイン)
Access-Control-Allow-Credentials: true
(リクエスト元ドメイン)のところに *
を使うと、もうひとつの Access-Control-Allow-Credentials
がきかずエラーになり、Cookieが付与できない。
サーバー側のプログラムでリファラを取得して動的に指定してあげると通る。
ただしセキュリティ的にそれでいいのか感はある。
くわしく
やりたいこと
fooドメインのjsからbarドメインのPHPにリクエスト飛ばして、PHPでCookie書き込んで、PHPからjsに値を返したい。
NG
こんな感じで送るとうまくいかない。
リクエスト元
// client.js var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if (this.readyState === 4 && this.status === 200) { var id = this.response.id; console.log(id); } }; xhr.open('GET', 'https://bar.jp/server.php', true); xhr.send();
リクエスト先
<?php // server.php header('HTTP/1.0 200 OK'); $id = 'hogehoge'; setcookie( 'id', $id, time() + 60 * 60 * 24 * 180, '/', 'bar.jp' ); echo json_encode(array('id' => $id));
↓
Chromeだとこういうエラー
XMLHttpRequest cannot load https://bar.jp/server.php. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://foo.jp' is therefore not allowed access. The response had HTTP status code 500.
さらにCookieの付与もできていない。Cookie読むのはできるけど書けない。
OK
- リクエスト元のjsで、XMLHttpRequestに↓を設定
withCredentials: true
- リクエスト先のサーバーで、レスポンスヘッダに↓を追記
Access-Control-Allow-Origin: (リクエスト元ドメイン)
Access-Control-Allow-Credentials: true
- ※ ↑のリクエスト元ドメインはワイルドカード以外を指定する
サーバー側で付けている Access-Control-Allow-Origin: (リクエスト元ドメイン)
はクロスドメインリクエストで値を返したりCookieを付けたりするために必要。
どうでもいい値を返すだけなら「どこからでも良いよ」の意で Access-Control-Allow-Origin: *
とワイルドカード指定しても良い。
ただし、ここでワイルドカード指定すると、Cookieなど大事な情報を扱うための Access-Control-Allow-Credentials: true
がきかなくなる。
リクエスト元が自分の運用する別ドメインなど限定できるなら、ちゃんと Access-Control-Allow-Origin: https://bar.jp
と指定する。
大人の事情で限定できず、事実上「どこからでも良いよ」な状況なのであれば、サーバー側でリファラを取得して指定すると通る。
これをjsとPHPで実装するとこうなる↓
// client.js var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if (this.readyState === 4 && this.status === 200) { var id = this.response.id; console.log(id); } }; xhr.open('GET', 'https://bar.jp/server.php', true); xhr.withCredentials = true; xhr.send();
<?php // server.php header('HTTP/1.0 200 OK'); header('Access-Control-Allow-Credentials: true'); $parsed_url = parse_url($_SERVER['HTTP_REFERER']); header('Access-Control-Allow-Origin: ' . $parsed_url['scheme'] . '://' . $parsed_url['host']); $id = 'hogehoge'; setcookie( 'id', $id, time() + 60 * 60 * 24 * 180, '/', 'bar.jp' ); echo json_encode(['id' => $id]);
実際は「サーバー側で事故ったら500」とかの処理もあったけど今回の話にかかわるところはこんな感じ。
おわり
値を返すところ、JSONPなるものをうまくつかうといいのかもしれない。
今回つかわなかったからまた機会があれば。