Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

cors-middleware

CORS middleware for Remix. It adds standard CORS response headers to Fetch API servers and can either short-circuit preflight requests or pass them through to app-defined OPTIONS handlers.

Features

  • Preflight Handling - Automatically handles OPTIONS preflight requests
  • Flexible Origin Rules - Supports static, regex, list, and function-based origin policies
  • Credential Support - Supports credentialed requests with spec-safe origin reflection
  • Header Controls - Configure allowed and exposed headers, preflight methods, and max age
  • Private Network Support - Optionally allow private network preflight requests

Installation

npm i remix

Usage

import { createRouter } from 'remix/fetch-router'
import { cors } from 'remix/cors-middleware'

let router = createRouter({
  middleware: [
    cors({
      origin: ['https://app.example.com', 'https://admin.example.com'],
      credentials: true,
      exposedHeaders: ['X-Request-Id'],
    }),
  ],
})

router.get('/api/projects', () => {
  return Response.json([{ id: 'p1', name: 'Remix' }], {
    headers: {
      'X-Request-Id': 'req_123',
    },
  })
})

Origin Policies

origin supports:

  • '*' to allow all origins
  • string for a single exact origin
  • RegExp for pattern-based matching
  • Array<string | RegExp> for multiple exact and pattern matches
  • true to reflect the request origin
  • (origin, context) => boolean | string for dynamic policies

Restrict Origins

let router = createRouter({
  middleware: [
    cors({
      origin: ['https://app.example.com', 'https://admin.example.com'],
      credentials: true,
    }),
  ],
})

Dynamic Origin Policies

let router = createRouter({
  middleware: [
    cors({
      origin(origin, context) {
        if (context.url.pathname.startsWith('/public/')) {
          return '*'
        }

        return origin.endsWith('.trusted.example')
      },
    }),
  ],
})

Preflight Behavior

By default, preflight requests are short-circuited with status 204.

let router = createRouter({
  middleware: [
    cors({
      methods: ['GET', 'POST', 'PATCH'],
      allowedHeaders: ['Authorization', 'Content-Type'],
      maxAge: 600,
    }),
  ],
})

Use a function-based allowedHeaders policy when the header allowlist depends on the request:

let router = createRouter({
  middleware: [
    cors({
      allowedHeaders(request) {
        let requestedHeaders = request.headers.get('Access-Control-Request-Headers')

        if (requestedHeaders?.includes('x-admin-token')) {
          return ['Authorization', 'Content-Type', 'X-Admin-Token']
        }

        return ['Authorization', 'Content-Type']
      },
    }),
  ],
})

Function-based allowedHeaders responses vary on Access-Control-Request-Headers, so caches do not reuse a preflight response for a different requested-header set.

Set preflightContinue: true to let downstream handlers process preflight requests. Use preflightStatusCode when you want short-circuited preflight responses to return a status other than 204.

Private Network Preflights

let router = createRouter({
  middleware: [
    cors({
      allowPrivateNetwork: true,
    }),
  ],
})

When allowPrivateNetwork is enabled, the middleware adds Access-Control-Allow-Private-Network: true for preflight requests that ask for private network access.

Expose Response Headers

let router = createRouter({
  middleware: [
    cors({
      exposedHeaders: ['X-Request-Id', 'X-Trace-Id'],
    }),
  ],
})

Caveats

  • CORS is primarily a browser enforcement mechanism. Disallowed non-preflight requests still reach your handlers unless you add separate request validation.
  • When credentials: true is used with origin: '*', the middleware reflects the request origin and adds Vary: Origin so the response stays cache-safe.
  • When allowedHeaders is a function, preflight responses vary on Access-Control-Request-Headers so caches do not reuse a response for a different requested-header set.
  • preflightContinue and preflightStatusCode only affect how preflight OPTIONS requests are handled. They do not change actual request authorization.

Related Packages

  • cop-middleware - Browser-origin protection middleware for unsafe cross-origin requests
  • fetch-router - Router for the web Fetch API
  • headers - Typed HTTP header utilities

Related Work

License

See LICENSE