Skip to content

CORS

Handle Cross-Origin Resource Sharing in Nhost Functions

CORS cross-origin serverless functions Access-Control preflight OPTIONS

Nhost Functions inject a small set of default CORS headers so simple cross-origin GET and POST requests work without extra code. The defaults only fill in headers your function doesn’t set. Anything you set yourself (directly or via middleware like cors) is preserved as-is. Other methods, custom headers, or credentials are your responsibility.

Unless your function sets them itself, every response includes:

HeaderValue
Access-Control-Allow-Origin*
Access-Control-Allow-Headersorigin,Accept,Authorization,Content-Type

Access-Control-Allow-Methods and Access-Control-Allow-Credentials are not set. Requests that need them won’t work until you set them yourself.

If you want to block cross-origin access, return an empty Access-Control-Allow-Origin and/or Access-Control-Allow-Headers:

./functions/api.ts
res.setHeader('Access-Control-Allow-Origin', '')
res.setHeader('Access-Control-Allow-Headers', '')

Use the cors npm package for the cleanest setup:

Terminal window
npm install cors
npm install -D @types/cors
./functions/api.ts
import type { Request, Response } from 'express'
import cors from 'cors'
const corsMiddleware = cors({
origin: ['https://myapp.com', 'http://localhost:5173'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
})
export default (req: Request, res: Response) => {
corsMiddleware(req, res, () => {
res.status(200).json({ message: 'Hello from a CORS-enabled function!' })
})
}

Or set headers manually:

./functions/api.ts
import type { Request, Response } from 'express'
export default (req: Request, res: Response) => {
res.setHeader('Access-Control-Allow-Origin', 'https://myapp.com')
res.status(200).json({ message: 'Hello!' })
}

Any CORS header you set takes precedence over the default; headers you leave untouched keep the default value. The snippet above covers simple requests only — see Preflight Requests for anything else.

If your frontend sends cookies or authorization headers with credentials: 'include', you need Access-Control-Allow-Credentials: true. The CORS spec forbids combining that with Access-Control-Allow-Origin: *, so you must also override the origin to a specific value:

res.setHeader('Access-Control-Allow-Origin', 'https://myapp.com')
res.setHeader('Access-Control-Allow-Credentials', 'true')

Browsers send a preflight OPTIONS request before the actual request when:

  • The request uses a method other than GET, HEAD, or POST
  • The request includes custom headers beyond the defaults above
  • The request uses Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain

The runtime does not respond to OPTIONS on your behalf — the request reaches your function. Either use the cors package (it handles preflight automatically) or handle OPTIONS manually:

./functions/api.ts
import type { Request, Response } from 'express'
export default (req: Request, res: Response) => {
if (req.method === 'OPTIONS') {
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
return res.status(204).end()
}
res.status(200).json({ message: 'Hello!' })
}