よくある原因
CORSエラーの最も多い原因は、APIサーバーがレスポンスに「Access-Control-Allow-Origin」ヘッダーを返していないことです。ブラウザはセキュリティ上の理由から、このヘッダーが存在しないクロスオリジンのレスポンスをJavaScriptから読めないようにブロックします。特に無料プランや古いAPIサービスではCORSを意図的に無効化しているものもあります。
もう一つよくあるケースは、POSTやPUTなどのリクエストでカスタムヘッダー(AuthorizationやContent-Type: application/json)を送ると、ブラウザが本番リクエスト前にOPTIONSメソッドでプリフライトを送る点です。このOPTIONSに対してサーバーが200を返さない、または「Access-Control-Allow-Headers」が不足していると、実際のリクエストが届く前にエラーになります。
- 相手がログインなしで開ける状態か確認する
- PCとスマホで最低1回ずつ表示を確認する
- 内部情報・個人情報・不要な外部送信が残っていないか見る
- レビュー期限と修正時の差し替え方を決めておく
自分側で確認すること
まずブラウザの開発者ツール(NetworkタブでOPTIONSリクエストを探す)でプリフライトが送られているかを確認します。送られている場合、サーバー側が「Access-Control-Allow-Methods」と「Access-Control-Allow-Headers」の両方を返しているかをレスポンスヘッダーで確認してください。自分でAPIサーバーを管理しているなら、Expressなら`cors`パッケージ、Nginxなら`add_header`ディレクティブで対応できます。
自分側でできる回避策として、サーバーサイドのプロキシ経由でAPIを呼ぶ方法があります。CloudflareWorkersやVercelのEdge Functionsを使えば、HTMLを置くドメインと同じオリジンからAPIを叩けるため、CORSの制約を受けなくなります。プロキシを挟む際はAPIキーをクライアント側のHTMLに直書きしなくて済む副次的なメリットもあります。
- HTML内の外部script・form action・iframeを確認する
- APIキーやトークンらしき文字列がないか検索する
- 画像・CSS・JSのパスが公開後も解決できるか見る
- noindexと認証を混同せず、必要なら両方設定する
相手側で確認すること
相手が提供するAPIであれば、ドキュメントの「CORS」または「クロスオリジン」の項目を確認し、許可オリジンの登録手順を探します。多くのAPIサービスはダッシュボードで「許可するドメイン」を登録する画面があり、ギガサイト便のプレビューURLや自社ドメインを追加するだけで解決します。
APIの管理者に連絡する際は、エラーが出ているURLとリクエストのHTTPメソッド、送っているヘッダーの一覧を共有するとスムーズです。「CORSが設定されていない」とだけ伝えるより、「OPTIONSリクエストへのAccess-Control-Allow-Originヘッダーが返っていない」と具体的に書くと、担当エンジニアがすぐ対処できます。
- HTML内の外部script・form action・iframeを確認する
- APIキーやトークンらしき文字列がないか検索する
- 画像・CSS・JSのパスが公開後も解決できるか見る
- noindexと認証を混同せず、必要なら両方設定する
再発防止の運用
CORSエラーは本番公開後に初めて気づくケースが多いです。開発中からHTTPSでホストされた環境(ローカルでも`localhost`ではなく`https://`)でテストする習慣をつけると、本番前に問題を発見できます。ギガサイト便を使ってプレビューURLをチームに配り、実際のドメインで動作確認するのが最も確実な方法です。
外部APIを使うHTMLを量産する場合は、APIキーを含むエンドポイントをHTMLに直書きするのをやめ、プロキシURL(例:`/api/xxx`)に統一するルールを決めておくと管理が楽になります。また、APIサービスの利用規約でクライアントサイドからの直接呼び出しが禁止されているケースもあるため、ドキュメントを一度確認することをすすめます。
- HTML内の外部script・form action・iframeを確認する
- APIキーやトークンらしき文字列がないか検索する
- 画像・CSS・JSのパスが公開後も解決できるか見る
- noindexと認証を混同せず、必要なら両方設定する
よくある質問
CORSエラーはローカルでは出ないのに本番URLで出るのはなぜですか?
file://プロトコルやlocalhostはブラウザがオリジンチェックを緩めている場合があります。本番ドメイン(https://)からアクセスすると厳格にチェックされるため、ローカルでは動いても本番で失敗します。
fetchにmode:'no-cors'を指定するとエラーが消えますが、これで解決しましたか?
いいえ。no-corsモードはレスポンスの中身をJavaScriptから読めない「opaque response」になります。APIのデータを取得して使う用途では意味がなく、根本解決にはなりません。
プロキシを使うとき、APIキーはどこに置けば安全ですか?
Cloudflare WorkersやVercel Edge FunctionsなどサーバーサイドのSecret(環境変数)に置きます。HTMLやフロントエンドのJSに直書きするとソースを見た人が全員APIキーを取得できてしまいます。