Traefik Headers 中间件完全指南:自定义请求/响应头、CORS 与安全响应头实战
本文基于当前仓库 docs/content/reference/routing-configuration/http/middlewares/headers.md 文档,结合 Traefik v3 源码(
pkg/middlewares/headers/与pkg/config/dynamic/)展开。核心主题是 HTTP Headers 中间件:它在请求被转发到后端之前修改请求头,并在响应返回客户端之前修改响应头,可用于注入自定义头、配置 CORS 策略、下发 HSTS 等安全头。读完本文,你将掌握 Headers 中间件在 YAML / TOML / Docker Labels / Swarm Tags / Kubernetes CRD 五种载体下的完整配置方法,理解其内部的 Secure(安全头)、Custom(自定义头)、CORS 三层处理模型,并能区分“同名字段覆盖旧值”的语义。
Headers 中间件在 Traefik 动态配置中的位置
Headers 属于 HTTP 层中间件,与 AddPrefix、BasicAuth、RateLimit 等一样,在动态配置(Dynamic Configuration)中通过 http.middlewares.<name>.headers 声明。中间件本身不产生路由规则,而是挂在 Router(路由器)上,在请求命中某条路由之后、转发给后端 Service 之前按顺序执行。
Traefik 默认代理转发时会自动带上若干与客户端、宿主、协议相关的 X-Forwarded-* 头(详见下文)。而 Headers 中间件处理的则是显式配置的头:
| Property | HTTP Header | 说明 |
|---|---|---|
| Client's IP | X-Forwarded-For、X-Real-Ip |
客户端真实 IP |
| Host | X-Forwarded-Host |
原始请求的 Host |
| Port | X-Forwarded-Port |
原始请求端口 |
| Protocol | X-Forwarded-Proto |
原始请求协议(http/https) |
| Proxy Server's Hostname | X-Forwarded-Server |
代理服务器主机名 |
这些头由 Traefik 在代理转发时自动加入,即使不配置 Headers 中间件也存在;Headers 中间件的主要价值在于按你的业务需要增删、覆盖或改写它们以及其它任意头。
基础示例一:同时为请求与响应添加自定义头
以下配置给被转发的请求添加 X-Script-Name: test,并给响应添加 X-Custom-Response-Header: value。五种配置载体写法等价:
http:
middlewares:
testHeader:
headers:
customRequestHeaders:
X-Script-Name: "test"
customResponseHeaders:
X-Custom-Response-Header: "value"
[http.middlewares]
[http.middlewares.testHeader.headers]
[http.middlewares.testHeader.headers.customRequestHeaders]
X-Script-Name = "test"
[http.middlewares.testHeader.headers.customResponseHeaders]
X-Custom-Response-Header = "value"
labels:
- "traefik.http.middlewares.testHeader.headers.customrequestheaders.X-Script-Name=test"
- "traefik.http.middlewares.testHeader.headers.customresponseheaders.X-Custom-Response-Header=value"
{
//...
"Tags": [
"traefik.http.middlewares.testheader.headers.customrequestheaders.X-Script-Name=test",
"traefik.http.middlewares.testheader.headers.customresponseheaders.X-Custom-Response-Header=value"
]
}
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: test-header
spec:
headers:
customRequestHeaders:
X-Script-Name: "test"
customResponseHeaders:
X-Custom-Response-Header: "value"
注意:
customRequestHeaders与customResponseHeaders为map[string]string类型(参见 pkg/config/dynamic/middlewares.go)。在 Labels / Tags 载体中,map 的每个 key 都会展开成一条独立的key=value项,且 map 键大小写不敏感——文档与示例中testHeader与testheader混用正是因为中间件名会被统一规范化处理。
基础示例二:添加与删除头(空值即删除)
Headers 中间件遵循一个关键约定:将某个头的值设为空字符串 "",等于删除该头。下面的配置同时演示了三种操作——为请求添加 X-Script-Name、从请求中剥离 X-Custom-Request-Header、从响应中剥离 X-Custom-Response-Header:
http:
middlewares:
testHeader:
headers:
customRequestHeaders:
X-Script-Name: "test" # 添加
X-Custom-Request-Header: "" # 删除
customResponseHeaders:
X-Custom-Response-Header: "" # 删除
[http.middlewares]
[http.middlewares.testHeader.headers]
[http.middlewares.testHeader.headers.customRequestHeaders]
X-Script-Name = "test" # 添加
X-Custom-Request-Header = "" # 删除
[http.middlewares.testHeader.headers.customResponseHeaders]
X-Custom-Response-Header = "" # 删除
labels:
- "traefik.http.middlewares.testheader.headers.customrequestheaders.X-Script-Name=test"
- "traefik.http.middlewares.testheader.headers.customrequestheaders.X-Custom-Request-Header="
- "traefik.http.middlewares.testheader.headers.customresponseheaders.X-Custom-Response-Header="
{
"Tags" : [
"traefik.http.middlewares.testheader.headers.customrequestheaders.X-Script-Name=test",
"traefik.http.middlewares.testheader.headers.customrequestheaders.X-Custom-Request-Header=",
"traefik.http.middlewares.testheader.headers.customresponseheaders.X-Custom-Response-Header="
]
}
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: test-header
spec:
headers:
customRequestHeaders:
X-Script-Name: "test" # 添加
X-Custom-Request-Header: "" # 删除
customResponseHeaders:
X-Custom-Response-Header: "" # 删除
这一“空值即删除”语义有清晰的分支实现依据:在 pkg/middlewares/headers/header.go 的 modifyCustomRequestHeaders 中,value == "" 时调用 req.Header.Del(header)(对 X-Forwarded-For 有特殊处理,见下文);而响应侧,PostRequestModifyResponseHeaders 对空值调用 res.Header.Del(header),非空则 res.Header.Set(header, value)。同名的响应头删除逻辑适用于后端返回的任意响应头。
修改 Host 与 X-Forwarded-For 的源码级细节
在请求头修改逻辑中(pkg/middlewares/headers/header.go),有两个值得注意的特殊分支,它们来自 Go 标准库 net/http 的特定行为:
Host头:Host是 HTTP/1.1 中不进入Headermap 的特殊字段。因此当 key 为Host(大小写不敏感比较,strings.EqualFold)时,代码直接赋值req.Host = value,而不是req.Header.Set(...)。即customRequestHeaders: {Host: "api.example.com"}可以改写转发目标的主机头。X-Forwarded-For置空:当 key 是X-Forwarded-For且值为空时,代码执行req.Header[header] = nil而非Del,这是为了规避 Go 标准库一个已知提交(golang/go中关于反向代理X-Forwarded-For合并行为的改动)导致的特殊路径,确保在彻底移除该头时不会让httputil/oxy 的转发逻辑把它“重新拼接”出来。
安全响应头:HSTS、点击劫持防护等
安全相关头(HSTS、浏览器 XSS 过滤等)与自定义头的配置方式完全一致,只需把对应布尔开关或字符串值设为 true / 具体值即可。例如同时启用“禁止 iframe 嵌入”与“浏览器 XSS 过滤”:
http:
middlewares:
testHeader:
headers:
frameDeny: true
browserXssFilter: true
[http.middlewares]
[http.middlewares.testHeader.headers]
frameDeny = true
browserXssFilter = true
labels:
- "traefik.http.middlewares.testHeader.headers.framedeny=true"
- "traefik.http.middlewares.testHeader.headers.browserxssfilter=true"
{
"Tags" : [
"traefik.http.middlewares.testheader.headers.framedeny=true",
"traefik.http.middlewares.testheader.headers.browserxssfilter=true"
]
}
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: test-header
spec:
headers:
frameDeny: true
browserXssFilter: true
安全头内部的执行模型
在实现上,安全头由独立的 Secure 处理层负责。看 pkg/middlewares/headers/secure.go,Traefik 将 Headers 配置中所有安全相关字段逐一映射进 unrolled/secure 库的 secure.Options,随后构建 secure.New(opt) 实例。响应头写入发生在请求转发完成之后:secureHeader.ServeHTTP 先让 unrolled/secure 对请求做校验与改写(通过 HandlerFuncWithNextForRequestOnly),再经由 secure.ModifyResponseHeaders 在响应阶段补上各类安全头。
安全头在开发环境可能需要“放开”:isDevelopment: true 时会缓解 allowedHosts、SSL、STS 等选项带来的副作用(本地调试常用 HTTP + localhost,而非生产域名)。配置结构体注释与默认值可对照 pkg/config/dynamic/middlewares.go。
CORS 头与预检请求的直答处理
CORS(跨源资源共享)头可以像自定义头一样配置。一个关键行为差异:一旦配置了 CORS 头,中间件不会把预检(preflight)请求转发给后端服务,而是直接生成并返回响应。下面是完整示例:
http:
middlewares:
testHeader:
headers:
accessControlAllowMethods:
- GET
- OPTIONS
- PUT
accessControlAllowHeaders:
- "*"
accessControlAllowOriginList:
- https://foo.bar.org
- https://example.org
accessControlMaxAge: 100
addVaryHeader: true
[http.middlewares]
[http.middlewares.testHeader.headers]
accessControlAllowMethods = ["GET", "OPTIONS", "PUT"]
accessControlAllowHeaders = [ "*" ]
accessControlAllowOriginList = ["https://foo.bar.org","https://example.org"]
accessControlMaxAge = 100
addVaryHeader = true
labels:
- "traefik.http.middlewares.testheader.headers.accesscontrolallowmethods=GET,OPTIONS,PUT"
- "traefik.http.middlewares.testheader.headers.accesscontrolallowheaders=*"
- "traefik.http.middlewares.testheader.headers.accesscontrolalloworiginlist=https://foo.bar.org,https://example.org"
- "traefik.http.middlewares.testheader.headers.accesscontrolmaxage=100"
- "traefik.http.middlewares.testheader.headers.addvaryheader=true"
{
"Tags" : [
"traefik.http.middlewares.testheader.headers.accesscontrolallowmethods=GET,OPTIONS,PUT",
"traefik.http.middlewares.testheader.headers.accesscontrolallowheaders=*",
"traefik.http.middlewares.testheader.headers.accesscontrolalloworiginlist=https://foo.bar.org,https://example.org",
"traefik.http.middlewares.testheader.headers.accesscontrolmaxage=100",
"traefik.http.middlewares.testheader.headers.addvaryheader=true"
]
}
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: test-header
spec:
headers:
accessControlAllowMethods:
- "GET"
- "OPTIONS"
- "PUT"
accessControlAllowHeaders:
- "*"
accessControlAllowOriginList:
- "https://foo.bar.org"
- "https://example.org"
accessControlMaxAge: 100
addVaryHeader: true
预检请求的判定与响应生成
判定逻辑见 processCorsHeaders:当请求同时满足以下三个条件时被识别为 CORS 预检请求——方法为 OPTIONS、带有 Origin 头、带有 Access-Control-Request-Method 头。此时中间件直接拼装响应头并返回 200 OK(并置 Content-Length: 0),不再调用后端(见 ServeHTTP)。
响应阶段(PostRequestModifyResponseHeaders)还会处理普通跨源请求:若请求的 Origin 命中允许列表,则回写 Access-Control-Allow-Origin;若配置 accessControlAllowCredentials 则回写 Access-Control-Allow-Credentials: true;配置了 accessControlExposeHeaders 时回写允许暴露的头;最后按 addVaryHeader 决定是否追加 Vary: Origin(已存在 Vary: Origin 时不重复添加)。
文档特别提醒:示例中的 CORS 配置并非权威或穷尽列举,不应直接照搬到生产环境,生产使用前请结合自身安全策略与 CORS 规范校验。
通配符与凭据的交互规则
从 matchOrigin(pkg/middlewares/headers/header.go)可以看到通配符 * 的精细处理:命中 * 时,若同时启用了 accessControlAllowCredentials,由于浏览器不允许带凭据的请求使用 * 通配,Traefik 会把实际请求的 Origin 值回写为 Access-Control-Allow-Origin;未启用凭据时才直接回写 *。同理,预检响应中若 Access-Control-Allow-Headers / Access-Control-Allow-Methods 配置了 * 且启用了凭据,会改为回显客户端发送的 Access-Control-Request-Headers / 实际请求方法(参见 pkg/middlewares/headers/header.go)。
全部配置选项参考
通用约束(重要):
- 若自定义头的名称与已存在的头相同,会直接覆盖(overwrite)原值。
- 安全相关头的详细语义遵循上游 unrolled/secure 库的可选配置项。
- 除下方主表外,Traefik 对几个“组合型”配置(
accessControlAllowOriginList、accessControlAllowOriginListRegex)有额外专节说明,见本文后半部分。
| Field | Description | Default | Required |
|---|---|---|---|
customRequestHeaders |
请求侧自定义头列表(name → value)。 | [] | No |
customResponseHeaders |
响应侧自定义头列表(name → value)。 | [] | No |
accessControlAllowCredentials |
是否允许请求携带用户凭据。 | false | No |
accessControlAllowHeaders |
允许的请求头名称列表(用于预检响应)。 | [] | No |
accessControlAllowMethods |
允许的请求方法列表(用于预检响应)。 | [] | No |
accessControlAllowOriginList |
允许的 Origin 列表(支持 * 通配),详见下文 accessControlAllowOriginList。 |
[] | No |
accessControlAllowOriginListRegex |
用正则匹配允许的 Origin,详见下文 accessControlAllowOriginListRegex。 | [] | No |
accessControlExposeHeaders |
允许暴露给浏览器 JS 的响应头(对应 Access-Control-Expose-Headers)。 |
[] | No |
accessControlMaxAge |
预检请求结果可被缓存的时长(秒)。 | 0 | No |
addVaryHeader |
与 accessControlAllowOriginList 配合使用:是否自动添加/修改 Vary 头,以声明服务端响应可能因 Origin 值不同而变化。 |
false | No |
allowedHosts |
允许的域名列表(白名单校验 Host)。 | [] | No |
hostsProxyHeaders |
携带被代理主机名的请求头 key 列表。 | [] | No |
sslProxyHeaders |
一组用于判定“请求已通过 HTTPS”的请求头键值对(例如 "X-Forwarded-Proto": "https"),适用于前面还有其他代理的场景。 |
{} | No |
stsSeconds |
Strict-Transport-Security 头的 max-age(秒)。 |
- | No |
stsIncludeSubdomains |
为 true 时,STS 头追加 includeSubDomains 指令。 |
false | No |
stsPreload |
为 STS 头追加 preload 标记。 |
false | No |
forceSTSHeader |
为 true 时,即使 HTTP 连接也添加 STS 头。 | false | No |
frameDeny |
为 true 时添加 X-Frame-Options: DENY(禁止页面被 iframe 嵌入)。 |
false | No |
customFrameOptionsValue |
自定义 X-Frame-Options 值;设置了该项会覆盖 frameDeny。 |
"" | No |
contentTypeNosniff |
为 true 时添加 X-Content-Type-Options: nosniff(禁止 MIME 类型嗅探)。 |
false | No |
browserXssFilter |
为 true 时添加 X-XSS-Protection: 1; mode=block。 |
false | No |
customBrowserXSSValue |
自定义 X-XSS-Protection 值;设置了该项会覆盖 browserXssFilter。 |
"" | No |
contentSecurityPolicy |
自定义 Content-Security-Policy 头的值。 |
"" | No |
contentSecurityPolicyReportOnly |
自定义 Content-Security-Policy-Report-Only 头的值。 |
"" | No |
publicKey |
实现 HPKP 公钥固定(用于防御证书伪造类中间人攻击)。 | "" | No |
referrerPolicy |
控制浏览器对 Referer 头的转发策略(Referrer-Policy 头)。 |
"" | No |
permissionsPolicy |
允许站点控制浏览器特性(Permissions-Policy 头)。 |
"" | No |
isDevelopment |
开发环境开关:为 true 时缓解 allowedHosts、SSL 与 STS 选项在本地调试中的副作用。 |
false | No |
结构体层面的字段类型、kubebuilder 校验与废弃标记可继续查阅 pkg/config/dynamic/middlewares.go,其中 FeaturePolicy(改用 PermissionsPolicy)、SSLRedirect / SSLTemporaryRedirect(改用 EntryPoint 重定向或 RedirectScheme)、SSLHost / SSLForceHost(改用 RedirectRegex)均已被标记为废弃。
accessControlAllowOriginList
accessControlAllowOriginList 通过为不同来源返回不同取值来声明“资源是否可以跨源共享”,它是一个允许的 Origin 列表:
- 可配置通配符
*,匹配所有请求;若后端服务自行写入了该头的值,会被 Traefik 覆盖。 - 不再支持
null值:因为Access-Control-Allow-Origin: null已不再被规范推荐作为返回值。
accessControlAllowOriginListRegex
accessControlAllowOriginListRegex 是 accessControlAllowOriginList 的“正则版”对应项:允许所有能匹配到列表中任一正则表达式的 Origin。
两个实用提示:
- 正则与替换可用在线 Go 正则工具或 Regex101 等预先验证。
- YAML 中转义陷阱:YAML 内定义正则时,所有需要转义的字符都要双写,例如
example\.com必须写成example\\.com。
正则的编译发生在中间件构建期:从 pkg/middlewares/headers/header.go 可以看到,NewHeader 会对 AccessControlAllowOriginListRegex 逐条执行 regexp.Compile,一旦有非法正则,会直接返回形如 error occurred during origin parsing: ... 的构建错误;编译成功的正则保存在 allowOriginRegexes 中,供请求匹配使用(matchOrigin)。
从源码看三层处理链与配置合法性
New(pkg/middlewares/headers/headers.go)是整个中间件的组装入口,它按“是否有某类配置”依次决定启用哪些层,最终形成 SecureMiddleware → HeaderMiddleware(自定义头 / CORS)→ next 的处理链:
- 用三个检测函数对配置归类:
HasSecureHeadersDefined()、HasCustomHeadersDefined()、HasCorsHeadersDefined()(三者定义见 pkg/config/dynamic/middlewares.go); - 若三者全部为 false(即中间件没有任何有效配置),构建直接失败,返回错误
"headers configuration not valid"——对应单元测试 TestNew_withoutOptions 对空配置断言报错的行为; - 有安全头配置则先挂 unrolled/secure 层,有自定义头或 CORS 配置则再包一层
NewHeader; STSSeconds指针为 0 时配置依然有效(测试 TestNew_withSTSSecondsZero 验证了这一点),此时按结构体注释语义“不输出 STS 头”。
allowedHosts 的行为可通过测试佐证:TestNew_allowedHosts 显示,Host 命中白名单时放行(200),Host 缺失或不在名单中时被拒绝。此外 Test1xxResponses 验证了中间件对 103 Early Hints 等 1xx 中间响应也能正确保留与追加自定义响应头,说明其响应改写是基于可捕获 1xx 的响应修改器(NewResponseModifier)完成的。
适用载体与引用方式小结
Headers 中间件的配置可来自 Traefik 的全部动态配置来源,本文示例覆盖了其中四类最常用载体:
- 文件(File):
http.middlewares下的 YAML / TOML 动态配置段; - Docker / Swarm:容器 Labels 与服务 Tags,键格式统一为
traefik.http.middlewares.<name>.headers.<字段>; - Kubernetes:
traefik.io/v1alpha1的MiddlewareCRD,通过metadata.name引用; - 在 Router 上通过
middlewares: [testheader]形式引用(K8s 则通过IngressRoute的middlewares列表引用)。
若你正在将 Traefik 用于企业级业务场景,可进一步阅读仓库中的 Traefik for Business Applications 补充说明;Headers 中间件作为其中基础的 HTTP 安全加固手段,通常与认证、限流等中间件一起编排在路由链上。
常见实战建议
- 用空值删除头是双向的:请求头与响应头都能删,常用于“剔除后端/上游带来的敏感头”或“清理默认的
X-Powered-By等指纹信息”。 - 统一收敛反代头:当 Traefik 前面还有其它代理(如云 LB、Nginx)时,用
sslProxyHeaders: {"X-Forwarded-Proto": "https"}让 Traefik 正确感知 HTTPS,避免把安全判断建立在错误的协议上。 - HSTS 与 HTTP:
forceSTSHeader默认 false,意味着纯 HTTP 连接不会下发 STS 头;若整体站点强制 HTTPS,可打开它,但需确保混合内容场景不会被误伤。 - CORS 与 Vary:当允许列表含多个来源且你使用了 CDN/浏览器缓存时,务必开启
addVaryHeader,避免不同 Origin 的响应被缓存复用。 - Kubernetes 命名:CRD 的
metadata.name需符合 K8s 命名规范,与testHeader这类 camelCase 用法略有差异,示例中使用了test-header。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00