首页
/ Traefik RedirectScheme 中间件完整指南:将 HTTP 请求安全重定向到 HTTPS 与自定义端口

Traefik RedirectScheme 中间件完整指南:将 HTTP 请求安全重定向到 HTTPS 与自定义端口

2026-09-07 14:56:16作者:冯梦姬Eddie

技术导读:本文系统讲解 Traefik 官方 HTTP 中间件 RedirectScheme——当请求使用的 scheme(协议,如 http/https)与目标配置不一致时,向客户端返回重定向响应。文中将以当前仓库 Traefik v3 为对象,覆盖多种配置源(结构化 YAML/TOML、Docker/Swarm Labels、Marathon 等 Tags、Kubernetes CRD Middleware)的完整写法、scheme/permanent/port 三个参数的精确语义,并结合 redirect_scheme.goredirect_scheme_test.go 深入解析其如何依赖 X-Forwarded-Proto 判定 scheme、如何生成 301/302/307/308 响应以及默认端口自动归一化等底层原理。读完本文,你可以独立完成"全站 HTTP→HTTPS 强制跳转""多端口重定向"等生产级配置,并能准确预判在不同反向代理场景下的行为。

一、RedirectScheme 是什么

RedirectScheme 是 Traefik HTTP 路由链中的一种重定向类中间件。它的核心职责非常简单:当客户端请求所使用的 scheme(协议)与配置中期望的 scheme 不同时,把请求重定向到期望的 scheme

也就是说,它并不会把所有请求一律跳转,而是先做"协议是否一致"的判断:

  • 请求已是目标 scheme(例如配置为 https 且请求本身就是 HTTPS),则直接放行到下一个处理器;
  • 请求 scheme 与目标不一致(例如配置为 https 但请求是 HTTP),则返回 3xx 重定向,并把 Location 指向 scheme 与(可选的)端口重写后的同路径 URL。

最典型的生产场景是全站强制 HTTPS:将监听在 :80 入口点的 HTTP 请求统一重定向到 HTTPS 入口点,从而避免明文传输。此外,它也能在多个协议端口共存的场景下(如 http:8080 与 https:8443)把流量引导到正确的端点。

从源码结构看,重定向中间件家族共包含两类成员,均位于 pkg/middlewares/redirect 目录:

中间件 依据 实现文件
RedirectScheme 依据 scheme / 端口 redirect_scheme.go
RedirectRegex 依据 正则表达式 redirect_regex.go

其中 RedirectScheme 在服务启动装配时由 pkg/server/middleware/middlewares.go#L322 调用 redirect.NewRedirectScheme(ctx, next, *config.RedirectScheme, middlewareName) 创建,属于动态配置中声明即生效的中间件。

二、部署前置条件:反向代理链中的信任关系

文档(见 redirectscheme.md)在正文开头就给出了一个重要的前置警告

当 Traefik 前面还有至少一个反向代理时,这个"最后一跳"的反向代理必须被 Traefik 视为**可信(trusted)**来源。

原因在于 RedirectScheme 判断客户端实际使用的协议,依赖的是从上游转发的 X-Forwarded 系列请求头(详见下文"scheme 判定原理")。如果中间的代理不被信任,Traefik 会清理掉这最后一跳传入的 X-Forwarded-Proto 等转发头(以防止伪造),此时 RedirectScheme 便无从得知真实协议,重定向行为就会失效或产生错误结果。

如何把前置代理加入可信来源,请参阅入口点配置文档中的可信代理(Forwarded Headers / trusted IPs)相关小节:entrypoints.md 配置选项

三、Configuration Examples:五种配置源完整示例

原文档给出了四段直接可用的配置示例(YAML、TOML、Labels、Tags),外加 Kubernetes CRD 写法,此处完整保留并补充说明每一种写法的适用 Provider。

1. 结构化配置(YAML)

适用于以静态文件、Docker/K8s 等 Provider 注入的动态配置:

# Redirect to https
http:
  middlewares:
    test-redirectscheme:
      redirectScheme:
        scheme: https
        permanent: true

2. 结构化配置(TOML)

# Redirect to https
[http.middlewares]
  [http.middlewares.test-redirectscheme.redirectScheme]
    scheme = "https"
    permanent = true

3. Docker / Swarm 容器标签(Labels)

Labels 写法适用于 Docker、Docker Swarm Provider,在容器或服务上以 traefik.http.middlewares.<name>.<option>=<value> 形式声明:

# Redirect to https
labels:
  - "traefik.http.middlewares.test-redirectscheme.redirectscheme.scheme=https"
  - "traefik.http.middlewares.test-redirectscheme.redirectscheme.permanent=true"

4. Marathon 等 Tags

Tags 写法适用于 Marathon、Rancher、Consul Catalog 等以键值 Tags 提供元数据的 Provider,键名规则与 Labels 相同:

// Redirect to https
{
  // ...
  "Tags": [
    "traefik.http.middlewares.test-redirectscheme.redirectscheme.scheme=https",
    "traefik.http.middlewares.test-redirectscheme.redirectscheme.permanent=true"
  ]
}

5. Kubernetes CRD Middleware

在 Kubernetes 中推荐通过自定义资源 Middlewaretraefik.io/v1alpha1)声明,再用注解挂载到 IngressRoute:

# Redirect to https
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: test-redirectscheme
spec:
  redirectScheme:
    scheme: https
    permanent: true

注意 K8s Labels 写法中 key 的大小写:容器标签场景下中间件名称段统一使用小写 redirectscheme,而 YAML/TOML/CRD 中字段使用驼峰 redirectScheme。从源码看,Labels/Tags 这类键值对最终会通过 pkg/config/label 的解析器映射到结构化字段,因此键名遵循 Provider 的标签命名约定(traefik.http.middlewares.*),而 CRD 的 spec.redirectScheme 则由 pkg/provider/kubernetes/crd/kubernetes.go#L323 直接拷贝到动态配置结构体中。

6. 把中间件挂载到路由器上使用

声明中间件本身并不会生效,必须通过路由器的 middlewares 引用它才会进入请求处理链。以最常用的容器 Labels 为例,完整的"中间件 + 路由器 + HTTPS 入口点强制跳转"组合如下:

labels:
  # 1) 声明中间件
  - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
  - "traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true"

  # 2) 在路由器上引用(example.com 的 HTTP 请求将被跳转到 https://example.com/...)
  - "traefik.http.routers.my-router.rule=Host(`example.com`)"
  - "traefik.http.routers.my-router.entrypoints=web"
  - "traefik.http.routers.my-router.middlewares=redirect-to-https"

对 Kubernetes 的 IngressRoute 场景,则在 routes[].middlewares 中按名称引用已创建的 Middleware 资源。

四、Configuration Options:参数详解

原文档的参数表如下,这是该中间件全部对外暴露的配置项:

Field Description Default Required
scheme 新 URL 使用的协议(scheme) "" Yes
permanent 是否启用永久重定向(对应 301/308) false No
port 新 URL 使用的端口。注意必须传字符串,而不是数值 "" No

三个参数的精确语义与取值约束说明如下。

scheme(必填)

  • 目标协议,决定请求应被重定向到 httphttps(源码常量定义于 redirect.go),测试用例还覆盖了 wss 作为目标的场景。
  • 必填:构造函数中若 len(conf.Scheme) == 0,直接返回错误 "you must provide a target scheme",中间件创建失败(见 redirect_scheme.go#L32-L34)。

permanent(可选,默认 false

控制重定向的"永久性",与返回状态码的映射关系如下(详见下文源码分析):

permanent 请求方法 返回状态码
false(临时) GET 302 Found
false(临时) 其他方法(如 POST) 307 Temporary Redirect
true(永久) GET 301 Moved Permanently
true(永久) 其他方法(如 POST) 308 Permanent Redirect

状态码会因请求方法不同而变化,这一点很容易被忽略:permanent 对 GET 请求给出 301/302,对非 GET 请求则分别给出 308/307,以避免 301/302 被某些客户端改写为 GET 而破坏 POST 语义。

port(可选,默认 ""

  • 重定向目标 URL 的端口。文档特别强调:必须写成字符串(如 "8443"),而不是数值(如 8443,这与动态配置字段声明为 string 类型一致。
  • 若不设置,则目标 URL 保留原请求 Host 中的端口,并遵循默认端口归一化规则(当 http 配 80、https 配 443 时不携带端口,见下文)。
  • 该端口还可用于"改端口"跳转,例如把 http://foo:8080 永久重定向到 https://foo:8443

值得说明的内部细节:结构体 RedirectScheme 实际上还有第四个字段 ForcePermanentRedirect,但它在 YAML/TOML/Labels 等所有外部配置载体上均被标记为忽略(json/toml/yaml/label/file/kv 全部为 "-"),仅作为内部字段供 Kubernetes ingress-nginx Provider 使用——当 Ingress 上开启 SSL 重定向注解时,translator.go#L396-L402 会构造 RedirectScheme{Scheme: "https", ForcePermanentRedirect: true},强制所有方法一律返回 308(详见第五节状态码逻辑)。

五、源码级原理:请求如何被判定并重定向

1. 重定向目标 URL 的组装

NewRedirectSchemeredirect_scheme.go#L27-L57)在创建中间件时就固定了两件事:

  1. 目标替换模板为 conf.Scheme + "://" + ${2} + port + ${4}。这里的 ${2}${4} 引用的是正则捕获组;
  2. 用于匹配原请求 URL 的正则 uriPattern
    ^(https?:\/\/)?(\[[\w:.]+\]|[\w\._-]+)?(:\d+)?(.*)$
    
    即依次捕获:协议前缀($1)、Host(支持 IPv6 字面量 [...] 与普通域名/IP,$2)、端口($3)、以及其余路径部分($4)。

其中端口归一化规则在此完成(redirect_scheme.go#L36-L39):

port := ""
if len(conf.Port) > 0 && !(conf.Scheme == schemeHTTP && conf.Port == "80" || conf.Scheme == schemeHTTPS && conf.Port == "443") {
    port = ":" + conf.Port
}

含义是:schemehttpport80,或 schemehttpsport443 时,该端口属于协议默认端口,会被省略、不写进目标 URL(即 http→80https→443 都被视为"无端口")。例如配置 {scheme: http, port: "80"},访问 http://foo:80 时新旧 URL 完全一致,中间件不会触发重定向——测试用例 to HTTP 80to HTTPS 443 断言返回 200 OK 正是这一逻辑的验证。

2. 当前请求 scheme 的判定:X-Forwarded-Proto 是关键

中间件必须重建"客户端眼中看到的原始 URL",再与新目标比较。clientRequestURLredirect_scheme.go#L59-L107)按照如下优先级确定 scheme:

  1. req.RequestURI 中解析显式携带的协议前缀;
  2. req.TLS != nil(Traefik 本地已终止 TLS),判定为 https
  3. 若请求头存在 X-Forwarded-Proto,则以它为准,并做 WebSocket 语义归并(见下);
  4. 最终还会把默认端口从 URL 中剥离(http:80 / https:443 不展示)。

关于第 3 步有一个很关键的实现细节。由于前一跳代理可能把连接升级场景(WebSocket)的协议写成 ws/wss,而本中间件只在 HTTP(S) 语境中使用,因此 redirect_scheme.go#L87-L101 会做如下转换:

  • X-Forwarded-Protohttpws → 按 http 处理;
  • httpswss → 按 https 处理;
  • 其他未知值 → 记录 Debug 日志 Invalid X-Forwarded-Proto 并忽略,回落到原有判定。

这解释了第二节信任警告的必要性:如果 Traefik 不信任其前置代理并清除了 X-Forwarded-Proto,那么真实协议将无法被感知。测试用例 HTTP to HTTPS, with X-Forwarded-Proto to HTTPS...to wss 均断言不重定向、返回 200——因为携带的转发头已表明请求"实质上已是 https",无需再跳转,这能有效避免在代理链中形成无限重定向循环。

3. 状态码决策与重定向执行

真正的重定向执行位于通用实现 redirect.goServeHTTP 的处理流程为:

  1. 通过 rawURL(req)(即上面的 clientRequestURL)重建原始 URL;
  2. 正则不匹配 → 直接放行到 next handler;
  3. replacement 模板做正则替换得到 newURL
  4. newURL != oldURL → 交给 moveHandlerLocation 头并返回对应状态码;
  5. 若替换后与原来一致 → 原地把 req.URL 替换为解析后的新 URL 并继续向后传递(这正是"已是目标 scheme 则放行"的落点)。

状态码决策逻辑见 moveHandler.ServeHTTPredirect.go#L92-L115),其规则与第四节参数表中的映射完全对应:

status := http.StatusFound                    // 302
if req.Method != http.MethodGet {
    status = http.StatusTemporaryRedirect     // 307
}
if m.permanent {
    status = http.StatusMovedPermanently      // 301
    if req.Method != http.MethodGet {
        status = http.StatusPermanentRedirect // 308
    }
}
if m.statusCode != nil {                      // ForcePermanentRedirect 时强制 308
    status = *m.statusCode
}

ForcePermanentRedirect=true(即 ingress-nginx 的 SSL 重定向场景)时,permanentRedirectCode 被固定为 308 Permanent Redirect,无论 GET 还是其他方法都返回 308,以严格保留请求方法与请求体语义。测试用例 HTTP to HTTPS with explicit 308 status code...for GET request 验证了这一行为。

六、行为边界与典型场景验证(测试用例归纳)

redirect_scheme_test.go 用约 30 组表格化用例固化了中间件行为,以下行为边界均有测试背书,可在实际排障时作为"预期行为清单":

场景 配置 期望行为
缺省 scheme {} 创建中间件报错,handler 为 nil
HTTP→HTTPS scheme: https 302Location: https://foo
已是 HTTPS scheme: https,请求带 X-Forwarded-Proto: https 不重定向,200
代理头为 ws/未知值 scheme: httpsX-Forwarded-Proto: ws/bar 判定为 http → 302 跳 https
代理头为 wss scheme: httpsX-Forwarded-Proto: wss 判定为 https → 不重定向
改端口跳转 scheme: https, port: "8443" http://foo:8000https://foo:8443(302)
永久重定向 scheme: https, port: "8443", permanent: true http://foohttps://foo:8443(301)
默认端口归一 scheme: http, port: "80"(或 https/443) 与现 URL 相同 → 不重定向,200
IPv6 支持 scheme: https http://[::1]:80https://[::1](保留方括号与地址)
WebSocket 目标 scheme: wss, port: "9443" http://foowss://foo:9443(302)

几点从用例中可以总结出的生产经验:

  1. 不要担心"多一跳代理后死循环":只要前置代理正确设置 X-Forwarded-Proto: https,即便用户通过 http URL 进入,只要 Traefik 接收到的已是 TLS 连接且转发头为 https,中间件也会放行;但反过来,若信任配置缺失导致转发头被清理,就会退化为不断跳转。
  2. 端口跳转时会改写原端口https://foo:8000scheme: https(不带 port)的配置下会被归一为 https://foo,即目标 URL 的端口不是"自动沿用原端口",而是按配置(或默认端口剥离)重算。
  3. URL 中其他部分(路径、查询串)保持不变:重定向只作用于 scheme/host/port 前缀,正则 $4 捕获的路径部分被原样拼接回目标 URL。
  4. IPv6 地址能够正确处理:正则中的 \[[\w:.]+\] 分支专门处理 [::1] 这类字面量,跳转后不会丢失方括号。

七、与本仓库其他能力的关系与延伸阅读

  • 若你需要比 scheme 更复杂的重写规则(例如同时改写路径、host),应使用同一家族的 RedirectRegex 中间件,其实现位于 redirect_regex.go
  • RedirectScheme 的响应不会被再次套娃处理:它属于服务端返回的 3xx 响应,若你希望搭配"错误页/自定义响应体",可结合 customerrors 等机制,但最典型的组合仍是"HTTPS 入口点只挂 RedirectScheme,真实服务放在 HTTPS 路由上"。
  • 想让 Traefik 自动签发证书配合本中间件实现"零人工配置"的 HTTPS,可结合 Let's Encrypt 配置 与 HTTPS 路由相关文档使用。
  • 关于中间件在请求链中的顺序编排、以及 chain 中间件把多个中间件打包复用,可阅读 HTTP 中间件 overview

综上,RedirectScheme 虽是一个"小而专"的中间件,但其对 X-Forwarded-Proto 的依赖、对 301/302/307/308 的方法感知选择、对默认端口的归一化处理,共同决定了它在反向代理拓扑与 Kubernetes 场景下的精确行为。把握上述源码级细节,即可在生产中写出可靠且不会产生重定向风暴的强制 HTTPS 规则。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
899
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
532
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
521
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
392