Endpoint CORS Configuration

Configure Cross-Origin Resource Sharing (CORS) on a per-endpoint basis to enable direct browser-to-API access for web applications.

Overview

By default, browsers block JavaScript from making requests to a different origin than the page was loaded from. CORS (Cross-Origin Resource Sharing) is a mechanism that allows your inference endpoints to accept requests from web applications hosted on different domains.

Xerotier.ai supports per-endpoint CORS configuration, meaning each inference endpoint can have its own CORS policy. This is useful when:

  • Your web application calls inference endpoints directly from the browser
  • You have multiple frontend applications on different domains that need endpoint access
  • You want to restrict which origins can access specific endpoints

CORS configuration is applied on a per-endpoint basis and enforced on every request to the endpoint.

Configuration

CORS settings are configured when creating or updating an endpoint. The following fields control CORS behavior:

Field Type Default Description
allowedOrigins string[] [] List of origins permitted to make requests. Use * for any origin.
allowedMethods string[] ["GET", "POST", "OPTIONS"] HTTP methods the client is allowed to use
allowedHeaders string[] ["Content-Type", "Authorization"] Request headers the client is allowed to send
maxAge integer 86400 How long (in seconds) browsers should cache the preflight response
allowCredentials boolean false Whether the browser should send credentials (cookies, auth headers) with requests

How It Works

Preflight Requests

When a browser makes a cross-origin request that is not "simple" (e.g., uses custom headers or non-standard methods), it first sends an OPTIONS preflight request. Xerotier handles this automatically:

  1. Browser sends OPTIONS request with Origin and Access-Control-Request-Method headers
  2. The origin is checked against the endpoint's allowedOrigins list
  3. If the origin is allowed, appropriate CORS headers are returned
  4. Browser proceeds with the actual request

Simple Requests

For simple requests (GET/POST with standard headers), the browser sends the request directly. The Access-Control-Allow-Origin header is added to the response if the origin is allowed.

Origin Validation

The Origin header is validated against the configured allowedOrigins list:

  • If allowedOrigins contains *, all origins are allowed
  • Otherwise, the origin must exactly match one of the listed origins
  • If the origin is not allowed, no CORS headers are added and the browser blocks the response

Examples

Single Origin

Allow requests only from your production web application:

JSON
{ "cors": { "allowedOrigins": ["https://app.example.com"], "allowedMethods": ["GET", "POST", "OPTIONS"], "allowedHeaders": ["Content-Type", "Authorization"], "maxAge": 86400, "allowCredentials": false } }

Multiple Origins

Allow requests from both production and staging environments:

JSON
{ "cors": { "allowedOrigins": [ "https://app.example.com", "https://staging.example.com", "http://localhost:3000" ], "allowedMethods": ["GET", "POST", "OPTIONS"], "allowedHeaders": ["Content-Type", "Authorization"], "maxAge": 3600, "allowCredentials": false } }

Wildcard Origin

Allow requests from any origin (use with caution):

JSON
{ "cors": { "allowedOrigins": ["*"], "allowedMethods": ["GET", "POST", "OPTIONS"], "allowedHeaders": ["Content-Type", "Authorization"], "maxAge": 86400, "allowCredentials": false } }

Setting CORS via API

Configure CORS on an endpoint using the endpoint update API:

curl
curl -X PATCH https://api.xerotier.ai/proj_ABC123/my-endpoint/v1/endpoints/my-endpoint \ -H "Authorization: Bearer xero_my-project_abc123" \ -H "Content-Type: application/json" \ -d '{ "cors": { "allowedOrigins": ["https://app.example.com"], "allowedMethods": ["GET", "POST", "OPTIONS"], "allowedHeaders": ["Content-Type", "Authorization"], "maxAge": 86400, "allowCredentials": false } }'
Python
import requests headers = { "Authorization": "Bearer xero_my-project_abc123", "Content-Type": "application/json" } response = requests.patch( "https://api.xerotier.ai/proj_ABC123/my-endpoint/v1/endpoints/my-endpoint", headers=headers, json={ "cors": { "allowedOrigins": ["https://app.example.com"], "allowedMethods": ["GET", "POST", "OPTIONS"], "allowedHeaders": ["Content-Type", "Authorization"], "maxAge": 86400, "allowCredentials": False } } ) print(response.json())

JavaScript Fetch Example

Once CORS is configured on your endpoint, you can make direct browser requests:

JavaScript
const response = await fetch( "https://api.xerotier.ai/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer xero_my-project_abc123" }, body: JSON.stringify({ model: "my-model", messages: [ { role: "user", content: "Hello!" } ] }) } ); const data = await response.json(); console.log(data.choices[0].message.content);

Security Considerations

  • Avoid wildcard origins in production: Using * for allowedOrigins means any website can make requests to your endpoint. This is acceptable for public APIs but not recommended for endpoints with sensitive data.
  • Wildcard and credentials are mutually exclusive: Browsers will reject responses that set Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true. If you need credentials, list specific origins.
  • API keys in browser code: When calling endpoints from browser JavaScript, your API key is visible to anyone who inspects the page source. Consider using scoped API keys with minimal permissions and endpoint restrictions.
  • Restrict allowed headers: Only allow the headers your application actually needs. The defaults (Content-Type and Authorization) are sufficient for most inference use cases.
  • Use short maxAge during development: A shorter maxAge (e.g., 300 seconds) during development means CORS policy changes take effect faster in browsers.

Important: CORS is a browser-enforced security mechanism. Server-to-server requests (curl, backend services, SDKs) are not affected by CORS settings and will always work regardless of configuration.

Troubleshooting

Common CORS Errors

Error Cause Fix
"No 'Access-Control-Allow-Origin' header" Origin not in allowedOrigins list Add your origin to the endpoint's allowedOrigins
"Method not allowed by CORS" HTTP method not in allowedMethods Add the method (e.g., POST) to allowedMethods
"Request header not allowed" Custom header not in allowedHeaders Add the header name to allowedHeaders
"Credentials flag is true but origin is wildcard" allowCredentials=true with allowedOrigins=["*"] Use specific origins instead of wildcard when credentials are needed

Diagnostic Steps

  1. Open browser DevTools and check the Network tab for the failed request
  2. Look for the OPTIONS preflight request -- check its response headers
  3. Verify the Origin header in your request matches an entry in allowedOrigins exactly (protocol, domain, and port must match)
  4. Test with curl to confirm the endpoint works without CORS:
curl
curl -H "Origin: https://app.example.com" \ -H "Access-Control-Request-Method: POST" \ -X OPTIONS \ "https://api.xerotier.ai/v1/chat/completions" -v
  1. Check the response for Access-Control-Allow-Origin and Access-Control-Allow-Methods headers

Simple vs Preflighted Requests

Browsers classify cross-origin requests into two categories:

  • Simple requests: GET or POST with standard Content-Types (text/plain, multipart/form-data, application/x-www-form-urlencoded) and no custom headers. These skip the preflight step.
  • Preflighted requests: Requests with custom headers (like Authorization), non-standard Content-Types (like application/json), or non-simple methods. These trigger an OPTIONS preflight first.

Most inference API calls use application/json and Authorization headers, so they are preflighted. Make sure your CORS configuration includes OPTIONS in allowedMethods.