2 months ago
I have a FastAPI backend deployed on Railway. My CORS_ORIGINS variable contains two comma-separated origins:
https://budtender-testing.vercel.app,https://budtender-next-mf8k.vercel.app
The first origin works correctly — preflight returns 200 OK with Access-Control-Allow-Origin header. The second origin consistently returns 400 Bad Request / Disallowed CORS origin from railway-edge.
Both origins are in the same variable, same format, no spaces. The variable was deleted entirely and retyped manually (no paste) to rule out invisible characters. Same result.
Evidence — staging origin (works):
curl -X OPTIONS https://budtender-backend-production-ad48.up.railway.app/chat \
-H "Origin: https://budtender-testing.vercel.app" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: content-type"
→ HTTP/1.1 200 OK, Access-Control-Allow-Origin: https://budtender-testing.vercel.app
Production origin (fails):
curl -X OPTIONS https://budtender-backend-production-ad48.up.railway.app/chat \
-H "Origin: https://budtender-next-mf8k.vercel.app" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: content-type"
→ HTTP/1.1 400 Bad Request, Server: railway-edge, Disallowed CORS origin
Railway Request ID from the failed preflight: FTVVLfe-QP6DH4wDg4a9AQ
FastAPI also has allow_origins=["*"] and an explicit @app.options("/chat") handler — Railway's edge intercepts the OPTIONS request before it reaches the app, so application-level CORS config has no effect.
Is there a known issue with Railway's edge CORS parsing for URLs containing hyphens in the subdomain (e.g. budtender-next-mf8k)? Or is there a format other than comma-separated that CORS_ORIGINS expects for multiple origins?
Thank you,
Lisa
3 Replies
Status changed to Awaiting Railway Response Railway • 2 months ago
2 months ago
The issue is that railway-edge is intercepting the request before it hits your FastAPI code. Your allow_origins=["*"] doesn't matter .
What I think is happening:
Railway-edge often expects specific environment variables to trigger its CORS logic. If you are using a generic CORS_ORIGINS variable, the edge might only be parsing the first entry or failing on the comma.
Try these:
- Swap the Order: Put the failing origin first in the
CORS_ORIGINSstring. - Check Railway UI: Go to Service Settings > Networking. If Railway has a built-in CORS section, add the origins there as separate entries rather than a single comma-separated string.
It’s unlikely a hyphen bug in 2026. It's more likely a string-length or parsing limit where the edge controller stops after the first valid URL it sees in that specific environment variable.
2 months ago
This is likely an issue with how your application is setup to handle CORS requests.
Take a look at these FastAPI docs on setting up CORS as a starting point: https://fastapi.tiangolo.com/tutorial/cors/
Railway doesn't actually do any CORS control on the edge that would cause this issue- so it probably has to do with how you're setting up CORS in Fast API 🙂
Specifically, make sure it's setup to handle OPTIONS requests- that seems to be where you're having problems?
Make sure your app has something like this set up before your routes:
import os
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# pull out your custom origins
custom_origins = os.getenv("CORS_ORIGINS", "")
custom_origins_list = [o.strip() for o in custom_origins.split(",") if o.strip()]
origins = [
"http://localhost",
"http://localhost:8080",
*custom_origins_list,
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)Status changed to Solved saruhime • 2 months ago
2 months ago
The root cause was Railway's edge proxy intercepting OPTIONS preflights before FastAPI could handle them — confirmed via curl returning Server: railway-edge / Disallowed CORS origin 400s. FastAPI CORS middleware changes had no effect. The fix was a Vercel proxy architecture: browser → Vercel API routes → Railway (server-to-server, no CORS enforcement). The Railway Networking settings panel has no built-in CORS configuration UI. The CORS_ORIGINS env var parsing theory (Reply 1) could not be tested conclusively since the proxy resolved the immediate issue. Leaving open for Railway to investigate the asymmetric edge behavior.
Status changed to Awaiting Railway Response Railway • 2 months ago
Status changed to Closed brody • 2 months ago
