Skip to main content

Import

import { cors } from 'hono/cors'

Usage

const app = new Hono()

app.use('/api/*', cors())

app.all('/api/abc', (c) => {
  return c.json({ success: true })
})

Options

The cors middleware accepts an optional CORSOptions object:
origin
string | string[] | ((origin: string, c: Context) => string | undefined | null | Promise<string | undefined | null>)
default:"*"
The value of “Access-Control-Allow-Origin” CORS header. Can be:
  • A single origin string (e.g., 'http://example.com')
  • An array of allowed origins
  • A function that returns the allowed origin or null/undefined to deny
allowMethods
string[] | ((origin: string, c: Context) => string[] | Promise<string[]>)
default:["GET","HEAD","PUT","POST","DELETE","PATCH"]
The value of “Access-Control-Allow-Methods” CORS header. Can be an array of methods or a function that returns the array.
allowHeaders
string[]
default:[]
The value of “Access-Control-Allow-Headers” CORS header. If not specified, reflects the headers specified in the request’s “Access-Control-Request-Headers” header.
maxAge
number
The value of “Access-Control-Max-Age” CORS header. Indicates how long the results of a preflight request can be cached.
credentials
boolean
The value of “Access-Control-Allow-Credentials” CORS header. Set to true to allow credentials (cookies, authorization headers).
exposeHeaders
string[]
default:[]
The value of “Access-Control-Expose-Headers” CORS header. Indicates which headers can be exposed to the client.

Signature

cors(options?: CORSOptions): MiddlewareHandler

Examples

Basic usage (allow all origins)

app.use('/api/*', cors())

Specific origin

app.use(
  '/api/*',
  cors({
    origin: 'http://example.com',
  })
)

Multiple origins

app.use(
  '/api/*',
  cors({
    origin: ['http://example.com', 'https://app.example.com'],
  })
)

Custom allowed methods

app.use(
  '/api/*',
  cors({
    origin: 'http://example.com',
    allowMethods: ['POST', 'GET', 'OPTIONS'],
  })
)

With credentials

app.use(
  '/api/*',
  cors({
    origin: 'http://example.com',
    credentials: true,
  })
)

Custom headers

app.use(
  '/api2/*',
  cors({
    origin: 'http://example.com',
    allowHeaders: ['X-Custom-Header', 'Upgrade-Insecure-Requests'],
    allowMethods: ['POST', 'GET', 'OPTIONS'],
    exposeHeaders: ['Content-Length', 'X-Kuma-Revision'],
    maxAge: 600,
    credentials: true,
  })
)

Dynamic origin validation

app.use(
  '/api/*',
  cors({
    origin: (origin, c) => {
      // Allow any subdomain of example.com
      if (origin.endsWith('.example.com')) {
        return origin
      }
      return null // Deny
    },
  })
)

Async origin validation

app.use(
  '/api/*',
  cors({
    origin: async (origin, c) => {
      // Check against database
      const allowed = await db.isOriginAllowed(origin)
      return allowed ? origin : null
    },
  })
)

Behavior

  • Automatically handles preflight OPTIONS requests
  • Returns 204 No Content for successful preflight requests
  • Sets Vary: Origin header when origin is not *
  • Reflects Access-Control-Request-Headers if allowHeaders is not specified
  • Removes Content-Length and Content-Type headers from preflight responses
  • If origin validation function returns null/undefined, the origin is denied

Build docs developers (and LLMs) love