From c918e8a291f2f93aaab7670987dde22b9f99cd2c Mon Sep 17 00:00:00 2001 From: mohiit1502 Date: Tue, 3 Feb 2026 20:20:03 +0530 Subject: [PATCH] CORS: merge requested headers into allow-headers for preflight and responses --- v2/middlewares/security/cors.ts | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/v2/middlewares/security/cors.ts b/v2/middlewares/security/cors.ts index 6d84c82..a05546e 100644 --- a/v2/middlewares/security/cors.ts +++ b/v2/middlewares/security/cors.ts @@ -444,8 +444,21 @@ export async function initCors(app: Application, config: CorsConfig, logger?: Lo const { allowed, isPublic, credentials } = await isOriginAllowedAsync(origin, requestPath, req) if (allowed) { - setCorsHeaders(res, origin, isPublic, credentials, override) - res.status(204).end() + setCorsHeaders(res, origin, isPublic, credentials, override) + // Merge any Access-Control-Request-Headers into the response's Access-Control-Allow-Headers + try { + const acrh = req.headers['access-control-request-headers'] + if (acrh) { + const existing = (res.getHeader && res.getHeader('Access-Control-Allow-Headers')) || '' + const existingArr = (existing && String(existing).split(',').map(h => h.trim()).filter(Boolean)) || [] + const requestedArr = String(acrh).split(',').map(h => h.trim()).filter(Boolean) + const combined = Array.from(new Set(existingArr.concat(requestedArr))) + res.setHeader('Access-Control-Allow-Headers', combined.join(',')) + } + } catch (e) { + // swallow errors — header merging is best-effort for preflight + } + res.status(204).end() return } else { // Still return 204 but without CORS headers (browser will block) @@ -458,7 +471,20 @@ export async function initCors(app: Application, config: CorsConfig, logger?: Lo const { allowed, isPublic, credentials } = await isOriginAllowedAsync(origin, requestPath, req) if (allowed) { - setCorsHeaders(res, origin, isPublic, credentials, override) + setCorsHeaders(res, origin, isPublic, credentials, override) + // Ensure any runtime-requested headers are present on actual responses as well + try { + const acrh = req.headers['access-control-request-headers'] + if (acrh) { + const existing = (res.getHeader && res.getHeader('Access-Control-Allow-Headers')) || '' + const existingArr = (existing && String(existing).split(',').map(h => h.trim()).filter(Boolean)) || [] + const requestedArr = String(acrh).split(',').map(h => h.trim()).filter(Boolean) + const combined = Array.from(new Set(existingArr.concat(requestedArr))) + res.setHeader('Access-Control-Allow-Headers', combined.join(',')) + } + } catch (e) { + // best-effort + } } next()