How to Fix CORS Misconfiguration in SaaS and AI-Built Apps
CORS misconfiguration is the #1 finding in SaaS pentests and was found in every AI-built app tested in 2026. Here is how to fix it before an attacker does.
CORS misconfiguration is the most common vulnerability in SaaS API security audits in 2026 — and it shows up in AI-built apps at an even higher rate. Astra Security found CORS issues in every one of four AI-generated applications it tested earlier this year. Bastion's SaaS pentest report listed it at the top of their most common findings. The reason it keeps appearing is not that developers don't know what CORS is — most do. The reason is that the default fix for a CORS error during development is to allow all origins, and that permissive setting often makes it into production without being reviewed.
A CORS misconfiguration lets a malicious website on the internet make authenticated requests to your API using a victim user's existing browser session — session cookies, auth headers, and all. The user does not have to click anything unusual. They just visit the attacker's site while logged into yours. That is the attack model, and it explains why CORS misconfiguration consistently scores as a critical or high severity finding when it appears on authenticated endpoints.
This guide covers the full CORS security picture: how the browser's same-origin policy works, what configuration makes it vulnerable, the specific mistakes AI code generators make, how to audit for CORS issues in Next.js and Supabase applications, and how to fix them. For the broader pre-launch security picture, see the SaaS pre-launch security checklist.
What Is CORS and Why Does Misconfiguration Create a Security Risk?
The browser's same-origin policy prevents JavaScript on one origin (such as attacker.com) from reading responses from a different origin (such as yourapi.com). Without it, any website the user visits could silently make requests to your API and read the responses. CORS (Cross-Origin Resource Sharing) is the mechanism that lets your API selectively relax that policy for specific trusted origins — your own frontend domain — while keeping all other origins blocked.
A CORS misconfiguration is a server configuration that makes the browser's same-origin policy either ineffective or trivially bypassable. The misconfiguration happens on the server — the server sends HTTP response headers that tell the browser which origins are allowed to read the response. If those headers are too permissive, the browser allows a malicious origin to read the response and use the data it contains.
- →Access-Control-Allow-Origin: * tells the browser that any origin may read the response. This is safe for truly public, unauthenticated API responses. It is a critical vulnerability on any endpoint that returns user-specific data, session-bound content, or sensitive application state.
- →Access-Control-Allow-Origin: null is a less obvious but equally dangerous setting. The null origin matches browser requests from sandboxed iframes and local files, both of which attackers use as delivery mechanisms for cross-origin attacks. Never set this header to null on production APIs.
- →Reflecting the request's Origin header without validation — responding with the caller's own origin in every case — is functionally equivalent to wildcard. Many developers do this accidentally when debugging CORS errors. Without validation against an explicit allowlist, this allows any origin.
- →Overly broad subdomain wildcards create lateral-movement risk. If your CORS policy allows every subdomain of your company domain, then any subdomain — including customer-owned subdomains, preview deployments, or a compromised subdomain — can make credentialed cross-origin requests to your production API.
CORS With Credentials: When the Risk Becomes Critical
The browser enforces one absolute rule: when Access-Control-Allow-Credentials is true, Access-Control-Allow-Origin cannot be a wildcard. Browsers refuse the credentialed cross-origin request if both are set. The intent is to prevent wildcard combined with credentials from being used together, because that combination would allow any website to make authenticated requests on behalf of any logged-in user.
What developers do instead, often without recognizing the security implication, is reflect the caller's Origin header dynamically: they respond with the caller's origin and Access-Control-Allow-Credentials: true for every request. This passes the browser's check — the value is a specific origin, not a wildcard — but it permits every origin equally. The security implication is identical to setting wildcard. The attacker's origin is reflected just as readily as your own frontend's.
- →Build an explicit allowlist of trusted origins and check every request's Origin header against it. Only reflect the Origin header value in the response if it is in the allowlist. For all other origins, omit the Access-Control-Allow-Origin header entirely — do not set it to null or to the request's origin.
- →Keep the allowlist short. Typically your production frontend domain, plus staging and local development domains in non-production environments. Each additional entry is an additional attack surface. Preview deployment domains should not be in your production API's allowlist.
- →Restrict Access-Control-Allow-Methods to the HTTP methods your API actually uses. An API that only uses GET and POST does not need to allow DELETE, PUT, or PATCH in the CORS headers. Each permitted method expands the action space for a cross-origin attacker.
- →Restrict Access-Control-Allow-Headers to the headers your API actually requires. Generic configurations that allow every custom header make the CORS policy toothless as a defense-in-depth layer.
CORS Misconfiguration in Next.js: Where Developers Get It Wrong
Next.js API routes, App Router Route Handlers, and Server Actions each have different CORS configuration surfaces, and mistakes appear in all three. Next.js Server Actions are protected by a CSRF-like origin check in the Next.js runtime, but API routes and Route Handlers are fully subject to CORS, and the default Next.js configuration does not add any CORS headers — leaving developers to add them manually.
- →The most common Next.js CORS mistake is handling CORS inside an individual API route handler rather than in middleware, then failing to apply the same policy to other routes added later. Centralize CORS in middleware.ts so the policy applies uniformly across all routes from day one.
- →Avoid the cors npm package's default configuration. When called with no options, it allows all origins with credentials — functionally equivalent to wildcard with credentials. Always pass an explicit origin allowlist: origin: ['https://your-frontend.com'], credentials: true.
- →In next.config.ts headers configuration, use exact origin strings rather than regular expressions where possible. A regular expression intended to match your domain might also match a lookalike domain if written without anchors.
- →In the App Router, Route Handler CORS configuration should be centralized in a shared helper function that returns the correct headers, enforcing the allowlist. Copy-pasted CORS handling across Route Handlers diverges over time and creates inconsistent enforcement.
CORS in Supabase and Firebase Applications
Supabase's PostgREST API and storage API are served with permissive default CORS headers designed for development convenience — they allow all origins on unauthenticated endpoints. The security model relies on Row-Level Security policies to restrict data access, not on CORS headers. The gap appears when CORS headers on the storage API are assumed to provide access control that they do not actually provide.
- →Supabase Edge Functions give you full control over CORS headers. The standard pattern is to handle the OPTIONS preflight with a 200 and return restrictive CORS headers on actual requests. Avoid community examples that set Access-Control-Allow-Origin: * on a function that also performs authenticated database operations — that header lets any website trigger those operations against any logged-in user's session.
- →Firebase Cloud Functions do not automatically add CORS headers; you configure them explicitly. The firebase-functions SDK's cors() helper, like Express's cors middleware, allows all origins by default. Pass an explicit origin allowlist when configuring it for any function that handles authenticated requests or returns user-specific data.
- →Storage APIs — Supabase Storage, Firebase Storage, AWS S3 — have their own CORS configurations separate from your application API CORS. An overly permissive storage CORS policy can allow cross-origin uploads from arbitrary domains, which creates a path to storing malicious content under your account.
CORS Misconfiguration in AI-Built and Vibe-Coded Apps
AI code generators produce permissive CORS configurations by default because they optimize for making the code work rather than making it secure. When a developer asks Lovable, Bolt, Cursor, or v0 to add CORS support to an API, the generated code typically allows all origins — the configuration that reliably removes the CORS error in development. That same setting then goes to production unchanged. AI-built apps make a predictable set of authentication and authorization mistakes that mirror this pattern exactly: defaults that work during development create vulnerabilities in production.
- →CORS misconfiguration in AI-built apps typically appears in one of three forms: a wildcard origin on an authenticated endpoint, reflected Origin with credentials enabled, or a copy-pasted CORS handler that someone added to fix a development error without understanding its security implications.
- →Generated API route handlers that include Access-Control-Allow-Origin: * combined with Cookie or Authorization header reading should be treated as security vulnerabilities regardless of how the code was generated. The generator does not know what data the route will return or whether the endpoint will be authenticated in production.
- →Audit AI-generated CORS configuration before launch by reading every API route handler and middleware for the word 'origin' and every occurrence of Access-Control-Allow-Origin. Check whether any configuration allows * or reflects the request's Origin without an allowlist check on an endpoint that reads from the database or processes user actions.
How to Test for CORS Misconfiguration Before Launch
Testing for CORS misconfiguration requires both manual verification and automated scanning. DeployReady includes CORS-related checks in its static analysis pass — it identifies API route handlers and middleware configurations that allow wildcard origins on routes that also read session context, and flags reflected-Origin patterns without allowlist validation. Use it as the automated baseline; follow it with manual testing that covers dynamic runtime behavior.
- →Manual test with curl: send a request to each API endpoint with an Origin: https://attacker.com header and check the Access-Control-Allow-Origin value in the response. If the response reflects attacker.com or returns *, the endpoint is misconfigured. For credentialed requests, add a Cookie or Authorization header to the curl command.
- →Test preflights explicitly: send an OPTIONS request with Origin: https://attacker.com and Access-Control-Request-Method: POST. Check whether the preflight response approves the request. Many CORS implementations correctly block simple requests but fail to validate the Origin on preflight responses, leaving a gap.
- →Test subdomain wildcards: if you use them, verify that your API does not accept subdomains you do not fully control as trusted origins. If it does, an attacker who compromises any subdomain gains the full trust your CORS policy grants.
- →Use browser-based testing to verify the full credentialed flow: load a page served from a test origin that makes a cross-origin fetch with credentials to your API and check whether the response body is accessible from the test page's JavaScript. Automated scanners sometimes miss edge cases that browser-based tests catch.
Frequently Asked Questions
What is CORS misconfiguration and why is it dangerous?
CORS misconfiguration is a server configuration that allows untrusted origins to make cross-origin requests to your API, bypassing the browser's same-origin policy. When the misconfigured endpoint also handles authentication — reading session cookies, accepting tokens, returning user-specific data — an attacker can host a malicious page that silently makes authenticated API requests on behalf of any logged-in user who visits it. The user does not need to interact with the malicious page beyond loading it in a browser tab.
Can CORS misconfiguration lead to account takeover?
Yes. In applications where the API exposes account management endpoints — password change, email change, API key rotation, payment method updates — a CORS misconfiguration on those endpoints can enable account takeover without any other vulnerability. An attacker whose page loads in a victim's browser can call these endpoints with the victim's session credentials, change the email address, and lock the victim out of their account. This is why CORS misconfiguration consistently receives critical or high severity ratings in SaaS security audits.
How do I fix CORS in a Next.js API route?
Create a middleware.ts file at the root of your Next.js project and implement CORS there, so the policy applies uniformly to all routes. Build an explicit allowlist of trusted origin strings — your production frontend domain and any staging domains — and check every request's Origin header against it. Only set Access-Control-Allow-Origin to the matched origin; for all other origins, omit the header entirely. Never set it to * on routes that read from your database or process authenticated actions.
Does CORS affect Supabase storage and database APIs?
Supabase's PostgREST and storage APIs use permissive default CORS headers designed for direct-from-browser access. The access control model relies on Row-Level Security and authenticated JWTs, not on CORS. This means CORS headers on the Supabase endpoints are not a security boundary — the security model assumes any browser can call them and enforces access control at the data layer. For Supabase Edge Functions you write yourself, CORS configuration is your responsibility; these functions do not get permissive headers by default.
What is the difference between CORS protection and CSRF protection?
CORS controls which origins the browser permits to read cross-origin responses. CSRF protection prevents unauthorized cross-origin state-changing requests regardless of whether the response is readable. They are complementary defenses, not alternatives: a correctly configured CORS policy does not prevent CSRF on form-based endpoints that use cookies; a CSRF token does not prevent a cross-origin attacker from reading responses on API endpoints with overly permissive CORS. Both are needed in a complete API security model.
How Belsoft Helps with API and CORS Security
CORS misconfiguration consistently appears in Belsoft's security audits for SaaS applications and AI-built apps. We review every API endpoint and middleware configuration manually — not just CORS, but auth enforcement, data access controls, and the full authorization model — because these vulnerabilities cluster. A CORS misconfiguration rarely appears alone. It appears alongside reflected Origin handling, missing authentication on adjacent routes, and overly broad IAM permissions that amplify the blast radius. We find it, document it, help the team fix it, and retest.
For teams that want an immediate automated baseline before the manual audit, DeployReady — Belsoft's open-source security scanner — catches CORS-related patterns in static analysis: wildcard origins on authenticated routes, reflected-Origin configurations, missing Content-Security-Policy headers, and other configuration-level risks. It runs locally, privately, in under a minute. The DeployReady scan is the starting point; a Belsoft audit goes deeper into the authorization model, session handling, and AI feature attack surface that automated tools cannot reach.
“CORS misconfiguration is the most preventable security issue on this list — and the one most often shipped to production because a developer was just trying to fix a development error.”
Written by
Marco Silva
Security Engineer, Belsoft Solutions
More from the blog
Ready to partner?
Let's talk about your company.
30 minutes. No pitch. We talk through how you run today and where AI and automation would help.
Book a free audit
