> ## Documentation Index
> Fetch the complete documentation index at: https://wemstudios.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# CORS Middleware in Blaze: Origins, Preflight, Credentials

> Configure cross-origin resource sharing in Blaze with cors(). Supports dynamic origins, preflight handling, credentials, and maxAge configuration.

The `cors()` middleware handles everything your browser needs to make cross-origin requests work: it sets `Access-Control-*` response headers on every matching request and responds to `OPTIONS` preflight requests with a `204 No Content` before your route handlers ever run. Because it has access to `req.env`, you can resolve the list of allowed origins from a Cloudflare environment binding at request time — no code changes required when you rotate or expand your origin list.

```typescript theme={null}
import { cors } from 'blaze/middleware/cors'
```

## Basic usage

Allow all origins with zero configuration:

```typescript theme={null}
import { createApp } from 'blaze'
import { cors } from 'blaze/middleware/cors'

const app = createApp<Env>()

app.use(cors())

app.get('/api/data', (req, res) => {
  res.json({ hello: 'world' })
})

export default { fetch: app.fetch }
```

This sets `Access-Control-Allow-Origin: *` on every response and handles `OPTIONS` preflight automatically.

## `CorsOptions` reference

<ParamField body="origins" type="string | string[] | (req) => string | string[] | Promise<string | string[]>">
  Allowed origin(s). Pass `'*'` (or omit entirely) to allow all origins. Pass an array of origin strings for an allowlist. Pass a function to resolve origins dynamically at request time — the function receives the full `BlazeRequest` so you can read from `req.env`. If the incoming `Origin` header is not in the allowlist, Blaze skips CORS headers entirely and calls `next()`.
</ParamField>

<ParamField body="methods" type="string[]" default="['GET','HEAD','PUT','PATCH','POST','DELETE']">
  HTTP methods to include in the `Access-Control-Allow-Methods` preflight header.
</ParamField>

<ParamField body="allowHeaders" type="string[]" default="[]">
  Request headers to include in `Access-Control-Allow-Headers`. When this is empty and the client sends an `Access-Control-Request-Headers` preflight header, Blaze reflects the requested headers back automatically.
</ParamField>

<ParamField body="exposeHeaders" type="string[]" default="[]">
  Response headers to expose to the browser via `Access-Control-Expose-Headers`.
</ParamField>

<ParamField body="credentials" type="boolean" default="false">
  Set `Access-Control-Allow-Credentials: true`. When `true`, the wildcard `'*'` is automatically replaced with the actual request `Origin` — see the note below.
</ParamField>

<ParamField body="maxAge" type="number">
  `Access-Control-Max-Age` in seconds. Controls how long browsers cache the preflight response. When omitted, the header is not set.
</ParamField>

## Dynamic origins from env

Store your allowed origins as a comma-separated Cloudflare variable and resolve them at request time:

```typescript theme={null}
// wrangler.toml (use Secrets for sensitive values)
// [vars]
// ALLOWED_ORIGINS = "https://app.example.com,https://staging.example.com"

import { cors } from 'blaze/middleware/cors'

app.use(
  cors({
    origins: (req) => req.env.ALLOWED_ORIGINS.split(','),
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    allowHeaders: ['Content-Type', 'Authorization'],
  }),
)
```

The resolver is `async`-compatible, so you can also fetch the origin list from KV if you need dynamic updates without redeployment:

```typescript theme={null}
app.use(
  cors({
    origins: async (req) => {
      const list = await req.env.CONFIG_KV.get('allowed-origins')
      return list ? list.split(',') : []
    },
  }),
)
```

## Credentials and cookies

When your frontend sends requests with cookies or an `Authorization` header, set `credentials: true` and specify an explicit origin (not `'*'`). Browsers reject credentialed responses that use the wildcard origin.

<Note>
  When `credentials: true`, Blaze automatically replaces the `'*'` wildcard with the actual request `Origin` header value. It also adds a `Vary: Origin` response header so caches don't serve one client's credentialed response to another.
</Note>

```typescript theme={null}
app.use(
  cors({
    origins: ['https://app.example.com'],
    credentials: true,
    allowHeaders: ['Content-Type', 'Authorization'],
    exposeHeaders: ['X-Request-Id'],
    maxAge: 86400, // cache preflight for 24 hours
  }),
)
```

With this configuration, Blaze produces these headers on a credentialed response:

```
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: X-Request-Id
Vary: Origin
```

And on a preflight `OPTIONS` response:

```
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, HEAD, PUT, PATCH, POST, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
```

## Scoped CORS

Apply `cors()` only to specific path prefixes rather than globally. This is useful when your Worker serves both an API (which needs CORS) and server-rendered HTML pages (which don't).

```typescript theme={null}
import { createApp } from 'blaze'
import { cors } from 'blaze/middleware/cors'

const app = createApp<Env>()

// CORS only applies to /api/* — HTML routes are unaffected
app.use(
  '/api',
  cors({
    origins: (req) => req.env.ALLOWED_ORIGINS.split(','),
    allowHeaders: ['Content-Type', 'Authorization'],
    credentials: true,
  }),
)

// Public HTML route — no CORS headers
app.get('/', (req, res) => {
  res.html('<h1>Home</h1>')
})

// CORS-enabled API routes
app.get('/api/products', listProducts)
app.post('/api/orders', createOrder)

export default { fetch: app.fetch }
```

You can also stack `cors()` with different configurations on different sub-routers:

```typescript theme={null}
const publicApi = app.Router()
publicApi.use(cors()) // wildcard — open public API
publicApi.get('/search', handleSearch)

const partnerApi = app.Router()
partnerApi.use(cors({ origins: ['https://partner.example.com'] }))
partnerApi.get('/feed', handleFeed)

app.use('/v1/public', publicApi)
app.use('/v1/partner', partnerApi)
```
