diff --git a/README.md b/README.md index 89a42a3..ac26317 100644 --- a/README.md +++ b/README.md @@ -116,20 +116,34 @@ fastify.register(async function (fastify) { fastify.listen({ port: 3000 }) ``` -### Disabling CORS for a specific route +### Route-Level CORS Overrides -CORS can be disabled at the route level by setting the `cors` option to `false`. +You can override or disable CORS on a per-route basis using the `config.cors` option:. ```js const fastify = require('fastify')() -fastify.register(require('@fastify/cors'), { origin: '*' }) +fastify.register(require('@fastify/cors'), { origin: 'https://example.com' }) fastify.get('/cors-enabled', (_req, reply) => { - reply.send('CORS headers') + reply.send('CORS headers applied') }) -fastify.get('/cors-disabled', { config: { cors: false } }, (_req, reply) => { +fastify.get('/cors-allow-all', { + config: { + cors: { + origin: '*', // Allow all origins for this route + }, + }, +}, (_req, reply) => { + reply.send('Custom CORS headers applied') +}) + +fastify.get('/cors-disabled', { + config: { + cors: false, // Disable CORS for this route + }, +}, (_req, reply) => { reply.send('No CORS headers') }) diff --git a/index.js b/index.js index ca946f9..4e64c05 100644 --- a/index.js +++ b/index.js @@ -156,7 +156,9 @@ function normalizeCorsOptions (opts, dynamic) { return corsOptions } -function addCorsHeadersHandler (fastify, options, req, reply, next) { +function addCorsHeadersHandler (fastify, globalOptions, req, reply, next) { + const options = { ...globalOptions, ...req.routeOptions.config?.cors } + if ((typeof options.origin !== 'string' && options.origin !== false) || options.dynamic) { // Always set Vary header for non-static origin option // https://fetch.spec.whatwg.org/#cors-protocol-and-http-caches diff --git a/test/cors.test.js b/test/cors.test.js index d64c48a..75cfcc1 100644 --- a/test/cors.test.js +++ b/test/cors.test.js @@ -1050,3 +1050,75 @@ test('Should allow routes to disable CORS individually', async t => { t.assert.strictEqual(res.statusCode, 200) t.assert.strictEqual(res.headers['access-control-allow-origin'], undefined) }) + +test('Should support route-level config', async t => { + t.plan(9) + + const fastify = Fastify() + fastify.register(cors, { + origin: 'https://default-example.com' + }) + + // ✅ Route with default CORS (inherits plugin config) + fastify.get('/cors-enabled', (_req, reply) => { + reply.send('CORS headers applied') + }) + + // 🌍 Route with custom CORS origin + fastify.get('/cors-allow-all', { + config: { + cors: { + origin: '*' + } + } + }, (_req, reply) => { + reply.send('Custom CORS headers applied') + }) + + // 🚫 Route with CORS disabled + fastify.get('/cors-disabled', { + config: { + cors: false + } + }, (_req, reply) => { + reply.send('No CORS headers') + }) + + await fastify.ready() + + // ✅ Default CORS + const resDefault = await fastify.inject({ + method: 'GET', + url: '/cors-enabled', + headers: { + origin: 'https://default-example.com' + } + }) + t.assert.ok(resDefault) + t.assert.strictEqual(resDefault.statusCode, 200) + t.assert.strictEqual(resDefault.headers['access-control-allow-origin'], 'https://default-example.com') + + // 🌍 Custom CORS + const resCustom = await fastify.inject({ + method: 'GET', + url: '/cors-allow-all', + headers: { + origin: 'https://example.com' + } + }) + t.assert.ok(resCustom) + t.assert.strictEqual(resCustom.statusCode, 200) + t.assert.strictEqual(resCustom.headers['access-control-allow-origin'], '*') + + // 🚫 CORS disabled + const resDisabled = await fastify.inject({ + method: 'GET', + url: '/cors-disabled', + headers: { + origin: 'https://example.com' + } + }) + t.assert.ok(resDisabled) + t.assert.strictEqual(resDisabled.statusCode, 200) + t.assert.strictEqual(resDisabled.headers['access-control-allow-origin'], undefined) +})