Edge Function returns HTTP 520 "Origin is disallowed" for every POST request #52052
Replies: 6 comments
|
Interesting that GET and OPTIONS work, but every POST fails even without an Origin header. Since you've already ruled out the usual CORS configuration changes, I'd try deploying a minimal temporary Edge Function with no Hono middleware or custom authentication, just a POST handler returning a simple JSON response. If that works, it would help narrow the issue to your existing function's middleware or routing. If it fails with the same 520 response, that would be useful evidence for your Supabase support ticket. I'd also compare the response headers and request IDs between the successful GET and failed POST, along with their UTC timestamps. I wouldn't relax the origin checks or authentication rules until the rejecting layer is identified. Hope this helps narrow it down! |
|
The exact phrase // From hono/src/middleware/csrf/index.ts:
if (!isAllowed(origin)) {
return c.text('Origin is disallowed', 403)
}This explains 100% of the symptoms you observed: Why the behavior matched your test results:
How to Fix ItCheck your Hono initialization in your Edge Function entry point ( Option A: If this is an API / Backend Service (Remove CSRF)For APIs where clients (mobile apps, frontend SPAs with Bearer tokens, or backend workers) interact via import { Hono } from 'https://deno.land/x/hono/mod.ts'
import { cors } from 'https://deno.land/x/hono/middleware.ts'
// ❌ REMOVE THIS:
// import { csrf } from 'https://deno.land/x/hono/middleware.ts'
const app = new Hono()
// Keep CORS for cross-origin browser requests:
app.use('*', cors({
origin: '*',
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowHeaders: ['Content-Type', 'Authorization', 'apikey'],
}))
// ❌ REMOVE THIS LINE:
// app.use('*', csrf())Option B: If you need CSRF, configure allowed origins explicitlyIf your Edge Function serves cookie-authenticated sessions from a specific web app: import { csrf } from 'hono/csrf'
app.use(
'*',
csrf({
origin: (origin) => {
// Allow your frontend origins or local development:
return origin.endsWith('.yourdomain.com') || origin === 'http://localhost:3000'
},
})
)Remove |
|
Hono CSRF middleware is worth checking, but the error text alone does not establish that it explains your HTTP 520 response. In particular, the earlier “100%” diagnosis omits the content-type condition and assumes an undocumented 403 → 520 transformation. The current Hono documentation says its built-in check applies to unsafe methods with form-compatible content types ( A useful controlled comparison on a side-effect-free staging handler is:
Do not use a privileged bootstrap endpoint for these probes. Absence from your application logger does not by itself establish absence from the runtime if that logger runs after a rejecting middleware. If JSON and form requests both fail identically in the minimal handler, the current built-in CSRF path is a poor fit; attach that evidence to SU-517126. If the failure appears only after adding middleware, bisect that stack. I would retain the existing protections until the rejecting layer is identified. CORS response headers do not authenticate callers or resolve a server-side origin check, and changing an origin allowlist cannot explain a gateway error without evidence. I have not reproduced your deployment. |
|
To make the isolation test suggested above concrete, deploy a separate temporary function named Deno.serve((req) => {
console.log("origin-probe-entered", req.method);
return new Response(
JSON.stringify({ reached: true, method: req.method }),
{
status: 200,
headers: {
"Content-Type": "application/json",
"X-Origin-Probe": "v1",
},
},
);
});Keep its gateway JWT-verification setting consistent with your existing function. Call the project's standard endpoint directly, supplying valid authentication headers if required: Compare GET and POST using curl or another non-browser client, and inspect both Invocations and Logs in the dashboard.
If your application uses a custom domain or another reverse proxy, compare that path with the standard project endpoint too. A browser CORS failure and an HTTP 520 returned to a non-browser client are different observations. Changing Reference: |
|
I found one detail that might help narrow this down. The exact combination of HTTP 520 + https://github.com/cloudflare/containers/blob/main/src/lib/container.ts It returns that response when an outbound request's hostname is rejected by Meanwhile, Hono's built-in https://github.com/honojs/hono/blob/v4.10.0/src/middleware/csrf/index.ts I'm not saying Cloudflare Containers is definitely involved here. Its host restriction applies to outbound requests, and it wouldn't normally explain GET succeeding while POST fails against the same destination. But that exact error signature makes me curious whether there's another proxy, Worker, or sandbox layer involved in how you're invoking the function. If you haven't already, I'd compare:
If the direct request succeeds but the proxied request returns 520, check the intermediary's outbound host restrictions. If even the direct JSON POST to the minimal function returns 520, I wouldn't attribute it to Hono CSRF or Cloudflare Containers without more evidence. That difference, along with the response headers, would be useful for ticket SU-517126. |
|
One thing can be ruled out from source: Hono's The requests never show up in your function logs, so they're stopped before your code runs. To find which layer, run a quick test: // supabase/functions/probe/index.ts — no Hono, no middleware
Deno.serve(async (req) => {
console.log(req.method, req.headers.get('content-type'));
return Response.json({ method: req.method });
});supabase functions deploy probe --no-verify-jwt
curl -si -X POST "$SUPABASE_URL/functions/v1/probe" \
-H 'content-type: application/json' -d '{}'
Don't widen CORS or turn off auth to make it go away. CORS is enforced by browsers, not by curl, so it can't explain a 520 you get from curl. And |
Uh oh!
There was an error while loading. Please reload this page.
Problem
A deployed Supabase Edge Function accepts GET and OPTIONS requests, but every
POST, PUT, and DELETE request is rejected with:
HTTP 520
Origin is disallowed
The rejected requests do not appear in the Edge Function logs, which suggests
that they are being rejected before reaching the Deno runtime.
Verified behavior
/healthreturns HTTP 200 and{"status":"ok","version":"3"}/owner/statusreturns HTTP 200/healthreturns HTTP 520Origin is disallowed/owner/bootstrapreturns the same HTTP 520 responseor both headers
Configuration
@supabase/supabase-jsAccess-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONSAccess-Control-Allow-Headers: Content-Type, Authorization, apikeyAdditional information
The function was redeployed successfully from the Supabase dashboard.
The problem still occurs after redeployment.
Supabase support ticket: SU-517126
What project or gateway setting could reject all unsafe HTTP methods with
"Origin is disallowed" before the request reaches the Edge Function runtime?
All reactions