2020.3.18

Cloud FunctionsでCORSエラー「Access to XMLHttpRequest ~ has been blocked by CORS policy」が発生した時の対応

Cloud Functionsで作成したHTTP関数をAxiosを使ったリクエストで呼び出そうとした時、ドメインが異なるので、CORSエラーが発生してリクエストに失敗することがあります。

以下、エラーが発生した時のCORSの対応について記します。

目次

  • エラーの詳細
  • 関数のCORS対応
  • 関数のCORS対応(expressのcorsパッケージ利用)
  • 参考リンク

エラーの詳細

CORSエラーとして出力されるメッセージは以下の通りです。

Access to XMLHttpRequest at 'http://localhost:5001/firebase-project-id/asia-northeast1/makeProducts' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

上記はローカルの動作確認で発生した時のエラーで「http://localhost:5001/~/saveScore」が呼び出しているFunctionsの関数、「http://localhost:3000」が呼び出し元のドメインになります。

関数のCORS対応

Functionsに作成した関数をCORSに対応させる場合、以下のようにします。


const functions = require('firebase-functions');

exports.makeProducts = functions
  .region('asia-northeast1')
  .https.onRequest((req, res) => {

    res.set('Access-Control-Allow-Origin', '*');

    if (req.method === 'OPTIONS') {
        res.set('Access-Control-Allow-Methods', 'GET,POST');
        res.set('Access-Control-Allow-Headers', 'Content-Type');
        res.set('Access-Control-Max-Age', '-1');
        res.status(204).send('');

    } else if (req.method === 'POST') {
        res.send('success');

    }else{
        res.status(405).send('');
    }
});

流れとしては、最初にプリフライトリクエスト(OPTIONSメソッド)が送られてくるので、許可するドメインやメソッドの情報を追加して、ステータスコード「204」でレスポンスを返します。

アクセス可能な場合、続けてメインリクエスト(GETやPOSTのメソッド)が送られてくるので、該当するメソッドの処理を実行するようにします。

プリフライトリクエスト(OPTIONSメソッド)のレスポンスで返している情報については以下の通り。

  • Access-Control-Allow-Origin

    アクセスを許可する呼び出し元のドメインを指定( 「*」 はすべて許可、URLで指定する場合は「https://oar.st40.xyz」といったように記述)

  • Access-Control-Allow-Methods

    アクセスを許可するメソッドを指定(ただし、ここで指定したメソッドの種類でなくてもメインリクエストは送られてくる)

  • Access-Control-Allow-Headers

    アクセスを許可するヘッダーを指定

  • Access-Control-Max-Age

    プリフライトリクエストのキャッシュ時間(秒で指定、-1 はキャッシュなし)

Access-Control-Max-Ageにキャッシュの時間を指定すると、キャッシュ中は前回のプリフライトリクエストの結果が参照されるので、動作確認などで頻繁に設定を変えている場合は注意が必要です。

言葉だけでは伝わりづらいので、実際に自身で確認してみると動作イメージがつかみやすいと思います。(Cloud Functionsのエミュレータを利用すると、わざわざサーバに配置したりする必要はなく、ローカル環境で確認可能)

また、上記のCORSに対応したリクエストはexpressのcorsパッケージを使ってもう少し分かりやすく記述することが可能です。

関数のCORS対応(expressのcorsパッケージ利用)

expressのcorsパッケージを使用した関数のCORS対応は以下のようにします。


const functions = require('firebase-functions');

const cors = require('cors')({
    origin: true, 
    methods: 'GET,POST', 
    allowedHeaders: 'Content-Type', 
    maxAge: '-1'
});

exports.makeProducts = functions
  .region('asia-northeast1')
  .https.onRequest((req, res) => {

    cors(req, res, () => {
      if (req.method === 'OPTIONS') {
          res.status(204).send('');
      } else if (req.method === 'POST') {
          res.send('success');  
      }else{
          res.status(405).send('');
      }
    }
});

最初のcorsの設定で origintrue を設定するとすべてのドメインを許可したことになります。

以下のようにURLを指定することも可能。

origin: 'https://oar.st40.xyz'

その他のオプションや設定の仕方を見てみると、expressのcorsパッケージを使った方が簡単に実装できそうです。

参考リンク

Firebase】関連記事