Cross-Origin Resource Sharing (CORS) controls which web applications on different domains can access your API. Zuplo handles CORS at the gateway level, automatically responding to preflight requests and adding the appropriate headers to responses.
Built-in Policies
Every route has a corsPolicy property in its x-zuplo-route configuration.
Zuplo provides two built-in policies:
none
Disables CORS for the route. All CORS headers are stripped from responses, and
preflight OPTIONS requests return a 404 response. This is the default when
no corsPolicy is set.
config/routes.oas.json
anything-goes
Allows any origin, method, and header. This is useful for development or internal APIs but is not recommended for production. It sets:
Access-Control-Allow-Origin: The requesting origin (reflected back)Access-Control-Allow-Methods: The route's configured methodsAccess-Control-Allow-Headers:*Access-Control-Expose-Headers:*Access-Control-Allow-Credentials:trueAccess-Control-Max-Age:600
config/routes.oas.json
Custom CORS Policies
For production use, create custom CORS policies with fine-grained control over which origins, methods, and headers are allowed.
Custom CORS policies are defined in the policies.json file alongside regular
policies, under the corsPolicies array:
config/policies.json
Then reference the policy by name on each route:
config/routes.oas.json
You can also select the CORS policy from the route designer dropdown in the Zuplo Portal.
Policy Properties
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | A unique name used to reference this policy on routes. |
allowedOrigins | string[] or string | Yes | Origins permitted to make cross-origin requests. Supports wildcards (see Origin Matching). |
allowedMethods | string[] or string | No | HTTP methods allowed for cross-origin requests (e.g., GET, POST). |
allowedHeaders | string[] or string | No | Request headers the client can send. Use * to allow any header. |
exposeHeaders | string[] or string | No | Response headers the browser can access from JavaScript. |
maxAge | number | No | Time in seconds the browser caches preflight results. |
allowCredentials | boolean | No | Whether to include credentials (cookies, authorization headers) in cross-origin requests. |
All list properties (allowedOrigins, allowedMethods, allowedHeaders,
exposeHeaders) accept either a JSON array of strings or a single
comma-separated string:
Code
Do not include a trailing / on origin values. For example,
https://example.com is valid but https://example.com/ does not work.
Origin Matching
The allowedOrigins property supports several matching patterns:
Exact Match
Specify the full origin including the protocol:
Code
Origin matching is case-insensitive, so https://APP.EXAMPLE.COM matches
https://app.example.com.
Wildcard (*)
Allow any origin:
Code
Subdomain Wildcards
Use *. to match a single subdomain level:
Code
This matches https://app.example.com and https://api.example.com, but does
not match:
https://example.com(no subdomain)https://v2.api.example.com(multi-level subdomain)
Wildcards with Ports
Subdomain wildcards work with ports:
Code
This matches http://app.localhost:3000 but not http://localhost:3000.
Multiple Patterns
Combine exact origins and wildcard patterns:
Code
Using Environment Variables
Use environment variables to configure different origins per environment:
config/policies.json
Set the environment variable as a comma-separated string:
Code
Environment variables work for allowedOrigins, allowedMethods,
allowedHeaders, and exposeHeaders.
How CORS Works in Zuplo
Preflight Requests
When a browser makes a cross-origin request that requires preflight, it sends an
OPTIONS request with Origin and Access-Control-Request-Method headers.
Zuplo handles these automatically:
- Zuplo matches the
OPTIONSrequest path and the requested method to a configured route. - If the route has a CORS policy, Zuplo checks whether the request origin
matches the policy's
allowedOrigins. - If the origin matches, Zuplo responds with a
200 OKand the appropriate CORS headers. - If the origin does not match or the route has no CORS policy, Zuplo responds
with a
404 Not Found.
Preflight handling runs before any policies or handlers on the route.
Simple Requests
For simple cross-origin requests (e.g., GET with standard headers), there is
no preflight. Zuplo adds CORS headers to the response based on the route's
policy. If the origin does not match, no CORS headers are added and the browser
blocks the response.
Header Precedence
Zuplo strips any existing CORS headers from upstream responses before applying the configured policy headers. This prevents conflicts and ensures the gateway is the single source of truth for CORS configuration.
Troubleshooting
No CORS headers in response
- Verify the route has a
corsPolicyset (notnone). - Check that the request includes an
Originheader. Browsers add this automatically for cross-origin requests, but tools likecurldo not. - Confirm the
Originvalue matches one of theallowedOriginspatterns exactly (including the protocol likehttps://).
Preflight returns 404
- Ensure the
corsPolicyon the matching route is not set tonone. - Verify the
Access-Control-Request-Methodheader in the preflight request matches a method configured on the route. - Check that the request path matches an existing route.
Preflight returns 400
- The preflight request must include both the
OriginandAccess-Control-Request-Methodheaders. A400response means one or both are missing.
Wildcard subdomain not matching
- The
*.pattern only matches a single subdomain level.https://*.example.comdoes not matchhttps://v2.api.example.com. - The
*.pattern does not match the base domain.https://*.example.comdoes not matchhttps://example.com. Add the base domain separately if needed.
Credentials not working
- Set
allowCredentialstotruein the CORS policy. - When using credentials,
allowedOriginscannot rely on a literal*being sent as theAccess-Control-Allow-Originheader value. Zuplo reflects the actual requesting origin instead, which is compatible with credentials.
For more details on CORS, see the MDN documentation: Cross-Origin Resource Sharing (CORS).