Skip to content
uupaa edited this page Aug 10, 2017 · 28 revisions

ローカル環境( https://localhost/app )で開発を進めていると、
サーバ側( https://api.example.com/xxx )のAPIを呼び出せないCanvas.toBlob() が実行できない という問題が発生します。

これはブラウザのセキュリティ機構の一つで、CORS ( Cross-Origin Resource Sharing ) と呼ばれており、ドメイン間をまたいだ通信をしようとする時は、予めサーバ側で許可をしておかないと通信がブロックされてしまいます。

一般的には、以下のいずれかの方法で回避が可能です。

  • 方法1. 適切なHTTPレスポンスヘッダを付与する
  • 方法2. Chrome の拡張機能で制限を突破する
  • 方法3. Chrome Canary をオプション付きで起動し突破する
  • 方法4. Nginx の proxy で突破する

適切なHTTPレスポンスヘッダを付与する

クロスオリジンの通信を成立させるためには、サーバ側が返すレスポンスに以下のHTTPヘッダを付与する必要があります。

まとめると、全オリジンからのAPIの呼び出しを許可する場合は、サーバ側のレスポンスに以下のヘッダを含めるように設定してください。

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Origin, Authorization, Accept, Cache-Control, Content-Type, DNT, If-Modified-Since, Keep-Alive, User-Agent, X-Requested-With

Cookie が必要な場合は Access-Control-Allow-Origin にFQDN を指定しドメインを制限することになります。また Access-Control-Allow-Credentials: true が必要になります。

Access-Control-Allow-Origin: https://api.example.com         # [!] * を指定できなくなります
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Origin, Authorization, Accept, Cache-Control, Content-Type, DNT, If-Modified-Since, Keep-Alive, User-Agent, X-Requested-With
Access-Control-Allow-Credentials: true                       # [!] これが必要になります

上記のように、Access-Control-Allow-Origin: https://api.example.com を設定してしまうと、 https://localhost から https://api.exmple.com を呼べなくなります(ブラウザ側でエラーにされてしまいます)。

このようなケースでは後述の Chrome Canary をオプション付きで起動し突破する を使用するか、 後述するSubdomain CORSを使い nginx で Access-Control-Allow-Origin を動的に生成してください。

Subdomain CORS

復数のサブドメインや無関係なドメインからのアクセスを許可する場合は Access-Control-Allow-Origin ヘッダを動的に書き換える必要があります。

以下の例では https://api.example.com に対し https://www.example.comhttps://ope.example.com そして https://localhost からのアクセスを許可しています。

server {
  listen      443;
  server_name api.example.com;

  location / {
    if ($http_origin ~* (https://(www\.example\.com|ope\.example\.com|localhost))) { #
      set $cors "true";
    }

    if ($cors = "true") {
      add_header Access-Control-Allow-Origin "$http_origin" always;
      add_header Access-Control-Allow-Methods "POST, GET, OPTIONS";
      add_header Access-Control-Allow-Headers "Access-Control-Allow-Headers: Origin, Authorization, Accept, Cache-Control, Content-Type, DNT, If-Modified-Since, Keep-Alive, User-Agent, X-Requested-With";
      add_header Access-Control-Allow-Credentials true;
    }
  }
}

always は 200 以外(404や500) の場合にも CORS ヘッダを付与するパラメタです。 always を付けないと 404 でも CORS のエラーとしてブラウザがステータスコードを書き換えてしまい、404 かどうかが分からなくなります(エラー原因の調査が難航します)。

Chrome の拡張機能で制限を突破する

Chrome の拡張機能をインストールし、Allow-Control-Allow-Origin ヘッダ等を付与することで、ブラウザの制限突破を試みます。

最新のChrome は Access-Control-Allow-Origin に復数の要素を指定するとエラーになります(例1, 例2)。

Chrome の拡張機能を使って、レスポンスヘッダを書き換える(追加する)場合に、サーバからのレスポンスヘッダに既に Access-Control-Allow-Origin: ... が含まれていると、この症状が発生しエラーになってしまいます。 その場合は、サーバ側の設定を変更するか、この方法を諦めて後述の Chrome Canary をオプション付きで起動し突破する を使用してください。

例1 (Access-Control-Allow-Origin の指定にカンマが許されなくなった)

Access-Control-Allow-Origin: https://example.com, *

例2 (Access-Control-Allow-Origin を何度か呼ぶことでも同様に発生する)

Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Origin: *

Chrome をオプション付きで起動し突破する

Chrome Canary を全て終了させ、以下のコマンドを実行すると、 CORS が OFF になっている状態で起動します。

open -a Google\ Chrome\ Canary --args \
  --allow-cross-origin-auth-prompt \
  --disable-web-security \
  --user-data-dir ~/ \
  --flag-switches-begin \
    --enable-experimental-web-platform-features \
  --flag-switches-end

一行: open -a Google\ Chrome\ Canary --args --allow-cross-origin-auth-prompt --disable-web-security --user-data-dir ~/ --flag-switches-begin --enable-experimental-web-platform-features --flag-switches-end

この方法で起動すると素の Chrome Canary が起動します。
Chrome Canary を普段使いしている方は注意してください。
ブックマークバーが非表示になっていたり、開いていたタブが全滅したりします。

Chrome Canary の生き残りがいると設定が反映されません。
Dock に常駐している Chrome Canary も終了させてください。

Nginx の proxy で突破する

Nginx の proxy で頑張る方法です。