Supabase Edge FunctionがブラウザでCORSエラーになるときOPTIONSと本体応答を分けて確認する

Supabase Edge FunctionのCORSエラーを、OPTIONS preflightと本体応答に分けて確認する手順を解説します。

AIものさし編集部

ブラウザでSupabase Edge Functionを呼び出して「CORS error」や Failed to fetch が出ても、それだけでは原因を特定できません。まず開発者ツールのNetworkでOPTIONS(preflight)と本来のHTTP requestを別々に確認します。OPTIONSが失敗しているなら許可Origin・method・request headerやFunctionのpreflight処理を調べます。本来のrequestが404・401・500なら、CORS設定だけを変えても直りません。

Supabaseの公式ガイドでは、ブラウザからEdge Functionを呼ぶ場合にCORS preflightを処理すること、SDKのwrapperやCORS helperを利用する方法が説明されています。使っているfunctionの実装・SDK versionによって処理方法が異なるため、Access-Control-Allow-Origin: * をすべての場面へコピーするのではなく、実際のrequestと利用中の公式例を照合してください。CORSはブラウザによる応答読み取りの制御であり、Functionの認証・認可を置き換えるものではありません。

最初にNetworkでOPTIONSと本体を分ける

ブラウザはcross-origin requestの条件によって、本体requestの前にOPTIONS requestを送ることがあります。これをpreflightと呼びます。Networkパネルで対象Function endpointを絞り、method、status、request header、response header、redirect先を確認してください。OPTIONSが成功していなければ、ブラウザは続くrequestを送らない場合があります。逆にOPTIONSが成功していても、本体requestの処理が成功したとは限りません。

表は横にスクロールできます

Networkで見える状態次に確認すること判断の方向
OPTIONSがNetworkに現れないrequestがpreflightを必要とする条件か、DevToolsのfilter、Service Worker、呼出しコードを確認OPTIONS不在だけでCORS設定ミスとはいえない
OPTIONSが4xx/5xxまたはredirectOPTIONSのURL、status、Location、function/gateway log、必要method/headerを確認正しいendpointへ到達しているか、preflight handlerが応答するか
OPTIONSは成功、本体requestが401/403Authorization、API key、JWT validation、function側認証を確認CORSではなく認証・権限エラーの可能性
OPTIONSは成功、本体requestが404endpoint/function名、デプロイ状態、Platform logを確認ブラウザはPlatform 404をCORS風の一般エラーとして表示する場合がある
OPTIONSは成功、本体requestが500Functionの実行ログとresponse statusを確認Function内の例外・依存関係・処理エラーの可能性
本体requestは2xxだがJSから応答を読めない本体responseに必要なCORS headerがあるか確認OPTIONSだけでなく本体responseにもheaderが必要な場合がある

DevToolsのNetworkでは、OPTIONS行と本体request行を同じendpoint・時刻で対応づけます。OPTIONSのRequest Headersにある Origin、Access-Control-Request-Method、Access-Control-Request-Headers を見て、responseの Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers が実際の要求と合うか確認します。値はヘッダー名までで足りることが多く、Authorization tokenやAPI keyをコピーして共有しないでください。

CORS設定で照合するもの

CORSを手動処理する実装では、少なくともOPTIONSに適切な成功responseを返し、実際のrequestにもCORS response headerを含める必要があります。許可するmethodやheaderは、呼び出すクライアントが送るものを踏まえて設定します。Authorizationや apikey などを送っているのに許可headerへ含まれなければ、preflightで拒否されます。SDK versionの更新でクライアントが送るheaderが増えることもあるため、古いallow-listを固定したままにせず、使用中のversionと現在のSupabase公式資料を照合します。

Supabaseの現行CORSガイドでは、withSupabase wrapperがCORSとOPTIONS preflightを処理する例、手動処理が必要なfunctionでCORS headerを返す例が掲載されています。さらに supabase-js v2.95.0以降ではSDKの corsHeaders helperをimportする方式が案内されています。古いversionでは手動header設定が必要となる場合があります。本文作成時の公式例をそのまま貼る前に、自分のfunctionがwrapperを使うのか、SDK versionは何かを確認してください。

CORSの確認事項:

  • Origin: ブラウザ画面のOriginが、許可対象として設定されているか。

  • Method: OPTIONSで要求されたmethodが許可されているか。

  • Request headers: Access-Control-Request-Headers の全headerが、実装のallow-listと整合するか。

  • Preflight response: OPTIONSにredirectや認証middlewareによる拒否がなく、期待するstatusとCORS headerを返すか。

  • Actual response: 実requestの成功・エラーresponseにもブラウザが読めるCORS headerがあるか。

  • SDK / wrapper: FunctionがどのCORS方式を使い、SDKのversionがその例に合っているか。

  • Credentials: Cookie等のcredentialsを使う場合、Originとcredentialの組み合わせがブラウザ・サーバーの規則に合っているか。

Access-Control-Allow-Origin: * は、すべてのrequestに無条件で適する設定ではありません。credentialsを伴うrequestではwildcard Originを使えず、許可するOriginを明示する必要があります。Cookieを使わないpublic endpointではwildcardが使われる例もありますが、公開可否は認証・認可とendpointの用途で判断します。許可Originを狭めても、サーバー側で認証やデータ権限を検証しなければ、APIを保護したことにはなりません。CORSの設定を直すためにJWT validationを無効化したり、認証を迂回したりしないでください。

OPTIONSが失敗する場合の手順

  1. DevToolsを開いてNetworkを選び、Preserve logを有効にし、失敗を一度だけ再現する。

  2. FunctionのURL、method、Origin、Request Headersを記録する。secret・token・個人データは記録に含めない。

  3. OPTIONS行を選び、status、redirect、response headerを確認する。

  4. Access-Control-Request-Method と Access-Control-Request-Headers を、FunctionのCORS設定または公式helperが返すallow-listと照合する。

  5. OPTIONSがFunctionに到達しているか、Supabase DashboardのFunction Invocations / Logsで確認する。

  6. 修正が必要なら、まずローカルまたは検証用環境で同じOrigin・method・headerを使って試す。

  7. OPTIONSが成功した後、本体requestが実際に送られたか確認し、別のstatusとして調査する。

requestが同じhost上へredirectされる場合や、異なるendpointへ転送される場合もあります。Networkの最終URLと Location headerを確認し、初期URLだけで原因を判断しないでください。ブラウザのcacheやService Workerが影響することもありますが、設定変更前にNetworkの応答statusとheaderを保存して、server側の証拠を先に確保します。

OPTIONSは成功しているのに「Failed to fetch」になる場合

OPTIONSが2xxでallow-listも合っている場合、本体requestのNetwork entryを探します。存在しなければ、ブラウザのConsoleに表示された詳細、client fetch、SDK呼び出し、endpoint URL、Service Workerやnetwork errorを確認します。本体が表示されるならstatusとSupabaseの sb-error-code response header(取得可能な場合)を見ます。エラー本文の情報を共有するときは、user dataや認証情報を除去してください。

Supabase公式のEdge Function 404 troubleshootingによると、Function名やdeployed bundleが認識されないplatform 404は、ブラウザ側ではgeneric CORS errorまたは Failed to send a request to the Edge Function と見える場合があります。この場合、endpoint path・Function名・deploy状況・Dashboard logsで確認します。アプリ自身が返した404とPlatform 404を混同しないでください。

401/403ならFunctionのJWT validation・Authorization header・利用者権限を見ます。500ならFunction内の未処理例外やruntime errorをLogsで調べます。SupabaseのLogsにはInvocations(Request/Response、status等)とLogs(platform event、例外、custom log)があるため、該当時刻のentryを照合するとOPTIONSとFunction本体のどこまで到達したかを確認できます。なお、request/response headersやbodyにtokenが含まれる可能性があるため、ログをexport・共有するときは機密情報の扱いを確認してください。

Functionログで確認するときの注意

Functionが起動しているか、OPTIONSや実requestが記録されているか、実行時に例外が出ていないかをDashboardのFunctionsから確認します。Platform 404の診断では、Supabaseの公式案内に従ってDashboard Logsを確認します。Function内で応答が生成されているなら、status、時間、例外種別を使って処理を絞ります。

Debug用のlogを足す場合も、request全体、Authorization、API key、cookie、個人情報を出力しないでください。Origin、method、path、status、許可header名など必要最小限の情報で診断できるようにします。ログへの出力は永続的な観測データとなる可能性があるため、調査後に不要な詳細logを残さないようにします。

診断を終える前の再試験

原因を修正したら、まず同じ環境で同じOrigin・method・headerの再現requestを行い、OPTIONS responseと本体responseを分けて確認します。次に、期待する認証状態で本体処理が成功し、必要なresponse headerとstatusが返ることを確認します。エラー時にもブラウザが安全に読めるresponseになるか試します。許可Originを広げただけ、またはConsoleでOPTIONSが200になっただけで完了にはしません。

表は横にスクロールできます

再試験項目PASSの条件
対象URL現在デプロイされている正しいFunction endpoint
OPTIONS必要な場合にstatusが成功し、Origin・method・requested headersが整合
本体requestNetwork上で送信・応答statusを確認できる
認証期待するJWT/roleで成功し、不正な認証を許可していない
Response成功とエラーの両方でブラウザが必要なresponseを扱える
Logs対象時刻のInvocation・exceptionと照合し、secretが不用意に記録されていない
Origin範囲用途に必要な範囲にとどめ、CORSを認証代替にしていない

NetworkのresponseやDashboard logsを第三者へ渡す場合は、access token、API key、Cookie、個人情報を伏せます。ProductionでCORS設定・JWT設定・Function deploymentを変えると実ユーザーへ影響するため、検証用環境で結果を確かめ、既存の承認・リリース手順を通してください。

この記事で確認できていないこと

特定のSupabase Project、Function、SDK version、JWT設定、許可Origin、deployment状態は確認していません。したがって本文は診断手順であり、個別のCORS設定値やJWT validation変更を指示するものではありません。Function wrapper・SDK helper・手動CORSのどれを使うべきかは、対象実装のversionと現行公式資料に基づいて決めてください。

Sources