Fix guide · high · cors_origin_reflected
Server reflects arbitrary Origin in Access-Control-Allow-Origin
vibecheck sent a request with Origin: https://attacker.example and your server echoed that exact value back in the Access-Control-Allow-Origin response header. The CORS allowlist isn't actually filtering — every origin is accepted.
Why it matters
The 'echo the Origin header' pattern is the most common CORS bug in vibe-coded apps. It looks correct (browsers permit cross-origin requests, dev appears to work) but it's the same as Access-Control-Allow-Origin: * from a security standpoint — there's no filtering. Without Allow-Credentials: true an attacker can't read authenticated responses, but they can still:
- Read public-but-rate-limited endpoints from their own site
- Probe internal-network hostnames if your API is reachable from the public internet
- Stage data-exfiltration if the API leaks per-IP information back
If this finding is paired with cors_origin_reflected_with_credentials (the credentials variant), every authenticated visitor's data is readable from any malicious site.
How to fix it
Replace echo-the-Origin with an allowlist. Express + cors:
import cors from "cors";
const ALLOWED_ORIGINS = [
"https://your-app.com",
"https://www.your-app.com",
"https://app.your-app.com",
];
app.use(cors({
origin: (origin, cb) => {
if (!origin) return cb(null, true); // Allow same-origin/no-Origin requests
if (ALLOWED_ORIGINS.includes(origin)) return cb(null, true);
return cb(new Error("Origin not allowed"));
},
credentials: true,
}));
Hono / Cloudflare Workers:
import { cors } from "hono/cors";
app.use("/*", cors({
origin: ALLOWED_ORIGINS, // explicit list, not a function that always returns origin
credentials: true,
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
}));
Two anti-patterns to specifically avoid:
origin: truein any cors middleware — that's "allow whatever Origin came in." It's the default in many tutorials.- A regex like
/\.your-app\.com$/without a leading anchor (/^https:\/\/[^.]+\.your-app\.com$/). Without anchoring,https://attacker.example/.your-app.commatches.
After deploying, verify with curl:
curl -i -H "Origin: https://attacker.example" https://your-app.com/api/anything
# Look for Access-Control-Allow-Origin in the response. It should NOT be
# "https://attacker.example". It should either be missing or be one of
# your allowed origins.
Full guide: /blog/cors-misconfig-vibe-coded.
Did vibecheck flag this on your app?
If you reached this page from a vibecheck inspection report, the redacted match in your scan output is the exact string we found in your bundle. After applying the fix above, run the inspection again — the finding should clear.
Run another inspection