ブラウザで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またはredirect | OPTIONSのURL、status、Location、function/gateway log、必要method/headerを確認 | 正しいendpointへ到達しているか、preflight handlerが応答するか |
| OPTIONSは成功、本体requestが401/403 | Authorization、API key、JWT validation、function側認証を確認 | CORSではなく認証・権限エラーの可能性 |
| OPTIONSは成功、本体requestが404 | endpoint/function名、デプロイ状態、Platform logを確認 | ブラウザはPlatform 404をCORS風の一般エラーとして表示する場合がある |
| OPTIONSは成功、本体requestが500 | Functionの実行ログと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が失敗する場合の手順
-
DevToolsを開いてNetworkを選び、Preserve logを有効にし、失敗を一度だけ再現する。
-
FunctionのURL、method、Origin、Request Headersを記録する。secret・token・個人データは記録に含めない。
-
OPTIONS行を選び、status、redirect、response headerを確認する。
-
Access-Control-Request-MethodとAccess-Control-Request-Headersを、FunctionのCORS設定または公式helperが返すallow-listと照合する。 -
OPTIONSがFunctionに到達しているか、Supabase DashboardのFunction Invocations / Logsで確認する。
-
修正が必要なら、まずローカルまたは検証用環境で同じOrigin・method・headerを使って試す。
-
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が整合 |
| 本体request | Network上で送信・応答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
-
Supabase, CORS support for Invoking from the browser: https://supabase.com/docs/guides/functions/cors(新しいタブで開く) (確認日:2026-10-15)
-
Supabase, Edge Function 404 error response: https://supabase.com/docs/guides/troubleshooting/edge-function-404-error-response(新しいタブで開く) (確認日:2026-10-15)
-
Supabase, Logging: https://supabase.com/docs/guides/functions/logging(新しいタブで開く) (確認日:2026-10-15)
-
Supabase, Error handling: https://supabase.com/docs/guides/functions/error-handling(新しいタブで開く) (確認日:2026-10-15)
