How OmniRoute Handles CORS Requests: A Complete Technical Guide

OmniRoute implements a centralized, declarative CORS strategy using static headers in src/shared/utils/cors.ts and dynamic origin resolution in src/server/cors/origins.ts, with environment-driven configuration for flexible cross-origin policies.

Every modern API needs robust Cross-Origin Resource Sharing (CORS) handling to serve browser-based clients securely. The OmniRoute repository takes a configuration-first approach that keeps CORS logic maintainable across dozens of API routes. This guide breaks down exactly how the system resolves origins, sets headers, and handles pre-flight requests.


The Core CORS Architecture

OmniRoute separates CORS concerns into two modules: static header definitions and dynamic origin resolution. This separation lets developers change cross-origin policies without touching individual route files.

Static CORS Headers (src/shared/utils/cors.ts)

The foundation is the CORS_HEADERS object exported from src/shared/utils/cors.ts. This constant defines the mandatory headers for every API response:

// src/shared/utils/cors.ts
export const CORS_HEADERS = {
  "Access-Control-Allow-Methods": "OPTIONS, GET, POST, PUT, DELETE, PATCH",
  "Access-Control-Allow-Headers":
    "Authorization, Content-Type, Accept, X-Omni-Request-ID, X-Omni-Client",
  // Access-Control-Allow-Origin is injected at request time
};

Routes compose these headers into response-specific variants. For JSON endpoints, the code spreads CORS_HEADERS and adds the content type:

const JSON_HEADERS = { ...CORS_HEADERS, "Content-Type": "application/json" };

Pre-Flight Request Handling

The handleCorsOptions() helper in the same file standardizes OPTIONS request responses. It returns a 204 No Content status with the complete CORS header set:

// src/shared/utils/cors.ts
export function handleCorsOptions(): Response {
  return new Response(null, { status: 204, headers: CORS_HEADERS });
}

Every public API route imports this function. For example, src/app/api/v1/route.ts delegates its OPTIONS handler directly to this utility.


Dynamic Origin Resolution

Static headers cannot know the requesting origin at build time. OmniRoute solves this through src/server/cors/origins.ts, which computes Access-Control-Allow-Origin at runtime.

Environment-Driven Configuration

Three environment variables control origin behavior:

  • CORS_ALLOW_ALL — When truthy, echoes back any Origin header from the incoming request (mirror mode)
  • CORS_ALLOWED_ORIGINS — Comma-separated whitelist of explicit allowed origins
  • CORS_ORIGIN — Legacy single-origin variable maintained for backward compatibility

The Origin Resolution Logic

The resolveOrigin() function inspects the request's Origin header and returns the permitted value:

// src/server/cors/origins.ts (conceptual)
function resolveOrigin(request: Request): string {
  const requestOrigin = request.headers.get("Origin");
  
  if (process.env.CORS_ALLOW_ALL) {
    return requestOrigin ?? "*";
  }
  
  const allowed = process.env.CORS_ALLOWED_ORIGINS?.split(",") ?? [];
  if (allowed.includes(requestOrigin)) {
    return requestOrigin;
  }
  
  // Fallback to CORS_ORIGIN or deny
  return process.env.CORS_ORIGIN ?? "";
}

The module exports STATIC_CORS_HEADERS as a frozen, immutable object combining the base headers with the resolved origin:

export const STATIC_CORS_HEADERS: Readonly<Record<string, string>> = Object.freeze({
  ...CORS_HEADERS,
  "Access-Control-Allow-Origin": resolveOrigin(request),
});

This freeze prevents accidental mutation across requests.


CORS Integration in Route Handlers

OmniRoute's pattern ensures every response carries correct CORS headers, including error responses. Here's how different route types implement the pattern.

Standard JSON Endpoints

// src/app/api/v1/relay/chat/completions/route.ts
import { CORS_HEADERS, handleCorsOptions } from "@/shared/utils/cors";

export async function POST(req: Request) {
  const completion = await generateCompletion(req);
  return new Response(JSON.stringify(completion), {
    status: 200,
    headers: { ...CORS_HEADERS, "Content-Type": "application/json" },
  });
}

export async function OPTIONS() {
  return handleCorsOptions();
}

Middleware Integration

The requireJsonContentType middleware in src/shared/middleware/requireJsonContentType.ts merges CORS_HEADERS into its error responses. This guarantees CORS compliance even when requests fail validation:

return new Response(JSON.stringify({ error: "Content-Type required" }), {
  status: 415,
  headers: CORS_HEADERS,
});

CORS Testing Strategy

OmniRoute validates CORS behavior through layered testing that prevents regressions as routes evolve.

Unit Tests: Origin Resolution

tests/unit/cors/origins.test.ts exercises the core logic:

  • Allow-all mode: Verifies that CORS_ALLOW_ALL=true mirrors any incoming origin
  • Whitelist parsing: Confirms comma-separated origins are correctly split and matched
  • Immutable headers: Asserts that Object.freeze() prevents accidental mutation
  • Legacy fallback: Ensures CORS_ORIGIN works when other variables are unset

Integration Tests: End-to-End Validation

tests/integration/proxy-pipeline.test.ts and route-specific OPTIONS tests verify that:

  • Every public endpoint responds to pre-flight requests
  • Actual responses include matching CORS headers
  • Error responses maintain CORS compliance

Configuration Examples

Development: Allow All Origins

CORS_ALLOW_ALL=true

This echoes the request's Origin header back, enabling local development with any frontend port.

Production: Explicit Whitelist

CORS_ALLOWED_ORIGINS=https://app.example.com,https://admin.example.com

Only the two specified origins receive Access-Control-Allow-Origin responses. Requests from unauthorized origins get no CORS headers (browser blocks them).

Legacy Compatibility

CORS_ORIGIN=https://legacy.example.com

Supported for existing deployments migrating to newer OmniRoute versions.


Summary

  • Static headers in src/shared/utils/cors.ts provide the canonical Access-Control-Allow-Methods and Access-Control-Allow-Headers values used across all routes
  • Dynamic origin resolution in src/server/cors/origins.ts computes Access-Control-Allow-Origin from environment variables at request time
  • Pre-flight standardization through handleCorsOptions() ensures uniform 204 responses for every OPTIONS request
  • Immutable header objects prevent accidental cross-request pollution
  • Comprehensive test coverage in tests/unit/cors/ and tests/integration/ validates policy enforcement

Frequently Asked Questions

How do I enable CORS for all origins in OmniRoute?

Set CORS_ALLOW_ALL=true in your environment. According to the OmniRoute source code, this triggers mirror-mode behavior in src/server/cors/origins.ts where the server echoes back whatever Origin header the browser sends. This is ideal for development but should be avoided in production.

Why does OmniRoute use a frozen headers object?

The Object.freeze() call on STATIC_CORS_HEADERS prevents accidental mutation. In src/server/cors/origins.ts, the frozen object ensures that one request's origin resolution cannot leak into subsequent requests—a critical safety measure for multi-tenant API servers.

Where are CORS headers actually applied to responses?

Every public route file imports from src/shared/utils/cors.ts. For example, src/app/api/v1/vscode/raw/[token]/route.ts returns handleCorsOptions() for OPTIONS requests, while POST handlers spread CORS_HEADERS into their response init objects. Middleware like requireJsonContentType.ts also merges these headers into error responses.

What happens if no CORS environment variables are set?

Per the implementation in src/server/cors/origins.ts, the system falls back to an empty string for Access-Control-Allow-Origin when no origin matches and no legacy CORS_ORIGIN is configured. Browsers will reject cross-origin requests in this scenario, effectively defaulting to a same-origin-only policy.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →