---
title: CORS细粒度策略与预检缓存最佳实践
keywords:
- CORS
- 预检
- Vary
- Allow-Credentials
- Allow-Methods
- Allow-Headers
- Max-Age
- 精确白名单
- 路由维度
- 预检缓存
description: 构建精确白名单与路由维度的CORS策略,正确处理预检与缓存(Vary与Max-Age),避免凭证与通配冲突,附服务端中间件与验收清单。
categories:
- 文章资讯
- 编程技术
---
一、目标与约束
- 精确来源白名单;凭证模式下禁止
*;按路由/方法/头维度控制。 - 预检响应必须设置
Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers与合理Max-Age≤600。
二、策略定义
type CorsRule = { origin: string; routes: Record<string, { methods: string[]; headers: string[]; credentials: boolean }> }
const rules: CorsRule[] = [
{ origin: 'https://example.com', routes: { '/api/user': { methods: ['GET','POST'], headers: ['Content-Type','Authorization'], credentials: true } } }
]
三、匹配与响应
type Req = { method: string; path: string; headers: Record<string, string | undefined> }
type Res = { setHeader: (k: string, v: string) => void; status: (n: number) => Res; end: (b?: string) => void }
function matchRule(origin: string, path: string): { methods: string[]; headers: string[]; credentials: boolean } | null {
const r = rules.find(x => x.origin === origin)
if (!r) return null
const conf = r.routes[path]
return conf || null
}
function applySimple(req: Req, res: Res) {
const origin = req.headers['origin'] || ''
const conf = matchRule(origin, req.path)
if (!conf) return
res.setHeader('Access-Control-Allow-Origin', origin)
res.setHeader('Vary', 'Origin')
if (conf.credentials) res.setHeader('Access-Control-Allow-Credentials', 'true')
}
四、预检处理
function preflight(req: Req, res: Res) {
const origin = req.headers['origin'] || ''
const method = req.headers['access-control-request-method'] || ''
const hdrs = (req.headers['access-control-request-headers'] || '').split(',').map(s => s.trim()).filter(Boolean)
const conf = matchRule(origin, req.path)
if (!conf) return res.status(403).end('cors_forbidden')
if (!conf.methods.includes(method)) return res.status(405).end('method_not_allowed')
for (const h of hdrs) if (!conf.headers.map(x => x.toLowerCase()).includes(h.toLowerCase())) return res.status(400).end('header_not_allowed')
res.setHeader('Access-Control-Allow-Origin', origin)
res.setHeader('Access-Control-Allow-Methods', conf.methods.join(','))
res.setHeader('Access-Control-Allow-Headers', conf.headers.join(','))
res.setHeader('Access-Control-Max-Age', '600')
if (conf.credentials) res.setHeader('Access-Control-Allow-Credentials', 'true')
res.setHeader('Vary', 'Origin, Access-Control-Request-Method, Access-Control-Request-Headers')
return res.status(204).end()
}
五、整合与验收
function corsMiddleware(req: Req, res: Res, next: Function) {
const origin = req.headers['origin'] || ''
const conf = matchRule(origin, req.path)
if (!conf) return next()
if (req.method === 'OPTIONS') return preflight(req, res)
applySimple(req, res)
next()
}
- 白名单来源精确匹配;凭证模式不使用
*;路由/方法/头严格控制。 - 预检缓存
Max-Age≤600且Vary包含请求方法与头;简单请求返回允许来源与可选凭证。 - 审计包含来源、路由与方法;拒绝时返回明确错误码与原因。

发表评论 取消回复