JavascriptのXMLHttpRequestを実装した時に以下のようなエラーに出くわしたことはないでしょうか。

Access to XMLHttpRequest at 'https://... ' from origin 'http://... ' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource

このエラーはドメインが違うサイトにリクエストを送った時に、「あなたのサイトからのリクエストは許可していません。」とリクエストが跳ね返された時に起こります。

このエラーの原因はCORSというものなのですが、「理由はいいから、早く解決したい!」という方のために、なぜ違うサイト同士の通信ができないのかはまた後でお話します。

じゃあどうしたら解決できるのかについてですが、一番最適な方法はリクエストを送りたいサイトに自分のドメインからのリクエストを許可してもらうことです。
が、相手のサイトに許可をもらうことができない場合もありますよね😱
(例えば、Googleの検索結果をスクレイピングしたい時とか。 ) そこで今回は相手のサイトから許可してもらえない時の解決方法をいくつかご紹介します。

CORSによるエラーの解決策


1. Chromeの拡張機能を使う

Access Control-Allow-Origin - UnblockというクロスオリジンからのリクエストをブロックしないようにしてくれるChromeの拡張機能があります。 Access Control-Allow-Origin - Unblock

使い方は上記のページから拡張機能をChromeに追加し、Chromeのブラウザ上でXMLHttpRequestを実装したページを見るだけです。
データ分析など、ローカル環境などレスポンスが欲しいだけの時はChromeの拡張機能が一番おすすめです。

2. fetchのmodeを使用する

JavascriptにはXMLHttpRequestと似たようなfetchというAPIがあります。※以下参照
Fetch 概説 - Web API | MDN このfetch APIのオプションにはクロスオリジンに対応するため、modeというものがあります。

postData(http://example.com/answer, {answer: 42})
  .then(data => console.log(JSON.stringify(data))) // JSON-string from response.json() call
  .catch(error => console.error(error));
function postData(url = ``, data = {}) {
    return fetch(url, {
        method: "POST", 
        mode: "cors", // no-cors, cors, *same-origin
        cache: "no-cache",
        credentials: "same-origin",
        headers: {
            "Content-Type": "application/json; charset=utf-8",
        },
        redirect: "follow",
        referrer: "no-referrer",
        body: JSON.stringify(data), 
    })
    .then(response => response.json()); 
}

上のコードはMDNのfetchの実装例で、modeに'no-cors'を設定するとCORSの規定に違反してもエラーではなくnullの値が返ってくるような仕組みになっています。
この方法はあくまでエラーを回避するのが目的でリクエストはしっかり拒否されているので、レスポンスはありません。



3. サーバーサイドからレスポンスを投げる

CORSはJavascriptのXMLHttpRequestやfetchなどフロントエンド側で発生する問題でした。
そのため最終的な手段としてはpythonやnode.jsなどを使って、サーバーサイドでリクエストを送る方法があります。
そもそもブラウザ上で実装したいので、XMLHttpRequestを絶対に使わないといけないんだ!という場合にはスルーしてください。
※私がこのCORSのエラーに出くわした時はサーバーサイドでも実行できるような内容だったので、Node.jsを使って解決しました。

Node.jsとjQueryを使って任意のページをスクレイピングしてhtml要素を取得した時の記事も書いてますので、参考にしてください。
Node.jsとjQuery!スクレイピングしてhtml要素を取ってみた

CORSのエラーの解決策としては以上になります。
ここからは今回のエラーの原因について詳しくお話いたします。

CORSによるエラーの原因

冒頭でも書いていますが、下記のようなエラーはなぜ起こるのか?

Access to XMLHttpRequest at 'https://... ' from origin 'http://... ' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource
クロスドメイン間でデータをやり取りするCORSという規約があり、その規約に違反していることが原因です。

CORSとは?

CORSとはCross Origin Resourse Sharingの略で、一言で言えば、違うドメイン同士でjavascriptのXMLHttpRequestやfetchなどのAJAX通信をする際に、安全にやりとりができるよう定められた規約です。
そもそも違うドメイン同士はブラウザ上でAjax通信を通じてデータをやりとりすることができません!とsame-origin policyというルールが存在します。(正確には同じドメインでしかやりとりができませんというルールです。)
例えばsame-origin policyがない世界があったとします。その世界で自分の口座を登録している銀行のサイトにアクセスし、ログインアウトせずに悪意のあるサイトにアクセスしたとします。
自分の口座情報を格納したCookieが銀行のサイトで作成されていた場合、悪意のあるサイトはそのCookie情報を意図もたやすく取得できてしまいます。
もしそうなれば後は何が起こる想像がつきますよね。自分の銀行からお金が盗まれます。

same-origin policyは一見すると厄介に見えますが、実は私たちの情報を守ってくれている重要なルールなのです。しかし、全く悪意がない場合でも違うドメイン間でデータをやりとりしたい時ってありますよね。
そう行った場合に安全にデータをやりとりできるようにしてくれたものがCORSなのです。

Access-Control-Allow-Originとは?

ヘッダーとはページの言語、クッキーなどの必要な情報ほぼ全てを格納している箱のようなもので、ページがリクエスト(アクセス)された時に、そのヘッダーの中からリクエストに応じてページに反映する情報が決められます。
ヘッダー内容はChormeのディベロッパーツール>Network>Headersから確認することができます。
Access-Control-Allow-Originとはそのヘッダーの情報の一つで、そのページのデータのやりとりを許可するドメインを設定することができます。

例えば、 下はhttps://example.comからのリクエストはOKしますよ!ということです。

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

全てのドメインからリクエストを許可している場合は、 下のようにになっています。
Access-Control-Allow-Origin: 

Access-Control-Allow-Originに書かれているドメインのみがAJAX通信が可能ということです。

まとめ

異なるドメイン間でAJAX通信をする場合のCORSによるエラーはリクエストを送る先のドメインから許可されていないことが原因です。
解決策としてはもちろん、相手のドメインに許可してもらうことですが、それができない場合はChromeの拡張機能やfetch、サーバーサイドでの実装を試してみてください。

ここまで読んでくれてありがとうございます。
ご質問ありましたらSNSからご連絡ください。
またね✋