首页
/ Traefik ErrorPages(errors)中间件技术指南:按状态码范围返回自定义错误页面

Traefik ErrorPages(errors)中间件技术指南:按状态码范围返回自定义错误页面

2026-09-07 11:46:57作者:平淮齐Percy

Traefik 的 errors(ErrorPages)中间件允许你根据配置的 HTTP 状态码区间,拦截后端返回的错误响应,改由指定的错误处理服务(error service)返回自定义错误页面。本文以 Traefik 官方参考文档为核心,结合本仓库源码、集成测试与 Fixture 配置,系统讲解该中间件的全部配置项、五种声明方式的完整示例、占位符变量的语义,以及它在内部实现中如何捕获状态码、重写状态码与转发请求头,帮助你直接落地可运行的自定义错误页方案。

中间件行为概述

errors 中间件的作用非常聚焦:当后端服务返回的状态码落在你配置的区间内时,不再把原始响应(通常是后端默认的错误页面)直接转发给客户端,而是改向另一个服务请求一个错误页面,并把该页面返回给客户端;如果状态码不在配置区间内,则原始响应被透传,不经过任何缓冲与改写。

该中间件是 Traefik 官方文档中列为内建参考的 HTTP 中间件之一,正式文档见 errorpages.md,其 Go 实现位于 custom_errors.go,对应动态配置结构体 ErrorPage 定义于 middlewares.go

典型应用场景:

  • 为 5xx 系列错误提供统一的品牌化错误页面,而非服务商默认的调试页;
  • 将不友好的状态码(如 418、502)重写为对访问者更友好的 404 或 500 展示;
  • 通过 {status}{originalStatus}{url} 占位符动态生成错误页 URL,按状态码返回不同页面。

配置示例:五种声明方式

errors 中间件在动态配置层有统一的字段结构,可在不同入口按对应语法声明。以下示例为「对 5XX 状态码返回自定义错误页面,但排除 502 与 504」的完整配置。

结构化配置(YAML)

# Dynamic Custom Error Page for 5XX Status Code excluding 502 and 504
http:
  middlewares:
    test-errors:
      errors:
        status:
          - "500"
          - "501"
          - "503"
          - "505-599"
        statusRewrites:
          "418": 404
          "502-504": 500
        service: error-handler-service
        query: "/{status}.html"

  services:
    # ... definition of the error-handler-service

结构化配置(TOML)

# Dynamic Custom Error Page for 5XX Status Code excluding 502 and 504
[http.middlewares]
  [http.middlewares.test-errors.errors]
    status = ["500","501","503","505-599"]
    service = "error-handler-service"
    query = "/{status}.html"

    [http.middlewares.test-errors.errors.statusRewrites]
      "418" = 404
      "502-504" = 500

[http.services]
  # ... definition of the error-handler-service

Docker / Swarm Labels 声明

# Dynamic Custom Error Page for 5XX Status Code
labels:
  - "traefik.http.middlewares.test-errors.errors.status=500,501,503,505-599"
  - "traefik.http.middlewares.test-errors.errors.statusRewrites.418=404"
  - "traefik.http.middlewares.test-errors.errors.statusRewrites.502-504=500"
  - "traefik.http.middlewares.test-errors.errors.service=error-handler-service"
  - "traefik.http.middlewares.test-errors.errors.query=/{status}.html"

Marathon Tags 声明

// Dynamic Custom Error Page for 5XX Status Code excluding 502 and 504
{
  // ...
  "Tags": [
    "traefik.http.middlewares.test-errors.errors.status=500,501,503,505-599",
    "traefik.http.middlewares.test-errors.errors.statusRewrites.418=404",
    "traefik.http.middlewares.test-errors.errors.statusRewrites.502-504=500",
    "traefik.http.middlewares.test-errors.errors.service=error-handler-service",
    "traefik.http.middlewares.test-errors.errors.query=/{status}.html"
  ]

}

注意:在 Labels 与 Tags 中,多个状态码与区间以逗号分隔写在同一字符串中(如 500,501,503,505-599)。

Kubernetes Middleware CRD 声明

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: test-errors
spec:
  errors:
    status:
      - "500"
      - "501"
      - "503"
      - "505-599"
    statusRewrites:
      "418": 404
      "502-504": 500
    query: /{status}.html
    service:
      name: error-handler-service
      port: 80

在 Kubernetes 场景下,该 CRD 由 Router(如 IngressRoute)通过 middlewares 字段引用,中间件随后会把错误页请求转发给其 spec.errors.service 指向的 Kubernetes Service。

配置项总览

ErrorPage 中间件各字段的官方定义如下(来自 errorpages.md 的配置项表):

字段 描述 默认值 必填
status 定义哪些状态码或状态码区间应触发错误页。状态码区间是闭区间505-599 会对 505 到 599 之间、包含两端点的每个码生效)。可以只写单个状态码(500),可以写多个以逗号分隔的状态码(500,502),可以用短横线连接两个码表示区间(505-599),也可以组合使用(404,418,505-599 []
statusRewrites 可选的状态码映射(将原状态码改写为另一个状态码)。详见后文 statusRewrites 小节 []
service 负责提供错误页的服务(即「错误服务」)。详见后文 service 小节 ""
query 错误页的请求路径(由 service 提供服务)。详见后文 query 小节 ""
errorRequestHeaders 定义要转发给错误页服务的原始请求头列表。详见后文 errorRequestHeaders 小节 []

从配置结构体的源码注释(middlewares.go)可以进一步确认每个字段的语义,其中 Status 以字符串切片形式保存(以容纳 500-599 这类区间写法),StatusRewritesmap[string]int 保存(区间写法作为 key,目标状态码作为 value)。

status:触发范围怎么写

status 决定「哪些响应需要被错误页接管」,支持四种写法并可自由混用:

  • 单个状态码:"500"
  • 逗号分隔的多个状态码:"500,502"
  • 闭区间:"505-599"(两端都包含,505599 都会触发);
  • 组合写法:"404,418,505-599"

区间最终会在运行时被解析为 [low, high] 的整数对。在 http_code_range.goNewHTTPCodeRanges 实现中,每个以短横线分隔的块会被拆成两个数字,没有短横线的单个码会被自动补全为同值的区间(如 503[503, 503]),随后由 Contains 通过「状态码大于等于 low 且小于等于 high」来判断是否命中。这段代码同样复用于 statusRewrites 的区间解析——所以映射的区间语法与 status 完全一致。

service 与 HostHeader 的协作语义

service 字段指定提供错误页的后端服务。在错误触发后,中间件会构造一个指向该服务的新请求来抓取错误页。

默认行为:客户端请求的 Host 头会被转发给所配置的错误服务。

若希望转发的是错误服务 URL 对应的 Host,则需要在错误服务的负载均衡器上把 passHostHeader 选项设置为 false。该选项的完整说明见 service.md

!> Kubernetes 注意事项:在 Kubernetes 中(例如 IngressRoute 引用时)指定 service,需要引用 Kubernetes Service 资源的 namenamespaceport。例如 my-service.my-namespace@kubernetescrd(或 my-service.my-namespace@kubernetescrd:80),可以确保请求正确到达目标服务与端口。

statusRewrites:改写触发状态码

statusRewrites 是可选的状态码映射,用于把「中间件实际看到的状态码」改写为另一个状态码后再返回给客户端。

例如后端返回 418 时,你可能希望它对外表现为 404;又如把 502-504 整体改写为 500。它同样支持单个状态码、逗号分隔的多个码、以及闭区间,区间语法与 status 选项完全一致。

statusRewrites:
  "418": 404          # 418 → 404
  "502-504": 500      # 502/503/504 → 500

从源码实现看(custom_errors.go),ServeHTTP 在捕获到命中 status 的状态码后,会先按配置顺序检查 statusRewrites,一旦命中区间即改写并 break(即先匹配到谁用谁)。改写后的码用于生成 query 中的 {status} 值;改写前的原始码则作为 {originalStatus} 保留。

query:错误页 URL 的占位符变量

query 定义向错误服务请求的路径(最终 URL 为 http://<req.Host> + query 形式)。query 中允许插入三类变量,中间件会在请求前完成替换:

变量 替换值
{status} 响应状态码(若被 statusRewrites 改写,则为改写后的码)
{originalStatus} 原始响应状态码(仅当被 statusRewrites 改写后才与原码不同)
{url} 经过 转义 处理的原始请求 URL

例如 query: "/{status}.html",当捕获到 500 时,会向错误服务请求 /500.htmlquery: "/?status={status}&url={url}" 则会把转义后的原始 URL 作为查询参数一并带给错误页。

源码中的替换逻辑位于 custom_errors.go:三个占位符分别由 strconv.Itoa(code)strconv.Itoa(originalCode)url.QueryEscape(orig.String()) 填充,其中 orig 是依据原始请求的 scheme、host、path、raw query 与 fragment 重建的 URL。对应单测在 custom_errors_test.goquery replacementfull query replacement 用例中给出了明确的期望值,例如替换后的完整请求会形如 /?status=503&url=http%3A%2F%2Flocalhost%2Ftest%3Ffoo%3Dbar%26baz%3Dbuz

errorRequestHeaders:控制跨服务边界的请求头

errorRequestHeaders 定义需要转发给错误页服务的原始请求头白名单

  • 不设置(默认):所有原始请求头都会被转发,其中包含 AuthorizationCookie 等认证信息;
  • 设置为显式列表:仅转发列出的请求头,例如 errorRequestHeaders: ["X-Request-Id"]
  • 设置为空列表errorRequestHeaders: []):不转发任何请求头。

如果你的错误页服务处于独立的信任域(trust domain),应使用该选项把跨服务边界的请求头收窄,避免认证材料外泄。这三种语义在源码中由 requestHeaders 字段是否为 nil 区分(custom_errors.go):nil 表示「全部转发」,走 utils.CopyHeaders 全量拷贝;否则仅遍历白名单、用 http.CanonicalHeaderKey 规范键名后拷贝命中的头。

源码透视:错误页是怎么被“抓”出来并替换的

errors 中间件的核心实现位于 pkg/middlewares/customerrors/custom_errors.go,整体流程如下:

  1. 构建期(New:把 config.Status 与每个 statusRewrites 的 key 通过 types.NewHTTPCodeRanges 解析成整数区间(解析失败即返回错误),并通过 serviceBuilder.BuildHTTP(ctx, config.Service) 构建错误服务的后端处理器(custom_errors.go)。可见错误服务必须在配置期即可解析,若错误服务不可用,请求会直接放行给下一个处理器并记录错误日志。

  2. 运行时拦截(ServeHTTP:请求先交给 codeCatcher(一个包装后的 http.ResponseWriter),再调用 next.ServeHTTP 让下游正常处理(custom_errors.go)。codeCatcher 的作用是尽早判断响应码是否命中配置区间:命中时丢弃响应体内容(不写回客户端),等待后续替换;未命中时则把头部与数据直接透传给原始客户端,无缓冲(见其 WriteHeader/Write 实现,custom_errors.go)。它同时实现了 http.Hijackerhttp.Flusher,兼容 WebSocket、流式响应等场景。

  3. 命中后的替换:若 codeCatcher.isFilteredCode() 为真,先执行 statusRewrites 改写逻辑;随后基于 query 模板生成错误页请求(newRequest 构造 GET 请求,custom_errors.go),按 errorRequestHeaders 规则带上请求头后,把请求交给错误服务后端。默认模式下,错误服务的响应会包一层 codeModifiercustom_errors.go),它强制以错误码(改写后的码)作为最终返回给客户端的响应码,即使错误服务自身返回了 200;同时错误服务返回的响应头与 1xx 信息头也会被透传。

  4. 附加的内部模式:配置结构体中还有一个仅对 IngressNGINX Provider 暴露、不对普通用户开放的内部字段 NginxHeadersmiddlewares.go)。当携带该字段时,中间件会切换为 Nginx 风格:额外设置 X-CodeX-FormatX-Original-UriX-Request-ID 等请求头,并保留错误服务自身返回的状态码而不强制改写。对应行为在测试中均有覆盖(custom_errors_test.go 的 nginx headers 系列用例)。

集成测试与 Fixture:可直接参考的完整配置

仓库在 integration 层提供了一套可直接参考的端到端用例。其 TOML Fixture 展示了中间件与普通服务、错误服务的组合方式,例如 simple.toml

[http.middlewares]
  [http.middlewares.error.errors]
    status = ["500-502", "503-599"]
    service = "error"
    query = "/50x.html"

[http.services]
  [http.services.error.loadBalancer]
    [[http.services.error.loadBalancer.servers]]
      url = "http://{{.Server2}}:80"

而在 statusRewrites.toml 中,则同时验证了区间捕获与改写映射的组合:

[http.middlewares.error.errors]
  status = ["500-502", "503-599", "418"]
  service = "error"
  query = "/50x.html"
  [http.middlewares.error.errors.statusRewrites]
    "418" = 400
    "500-502" = 404

上述文件与 integration/error_pages_test.go 配合,在真实 Traefik 实例中验证了「命中区间→请求错误页→改写状态码→返回自定义页面」的完整链路,可作为生产配置的样板。

小结

errors 中间件是 Traefik 动态配置体系中实现「统一错误呈现」的专用组件。把握住三个要点即可灵活使用:

  1. status 决定“抓谁”:闭区间、多码、组合写法都可以,且与 statusRewrites 的区间语法一致;
  2. query 决定“问谁要页面、要哪个页面”:配合 {status}{originalStatus}{url} 占位符,可实现按状态码、按原始请求动态取页;
  3. service + passHostHeader + errorRequestHeaders 决定“跨边界时如何呈现与保密”:默认转发全部请求头,跨信任域时务必用白名单或空列表收窄。

配合官方文档 errorpages.md 与源码 custom_errors.go、单测 custom_errors_test.go,你可以在 Docker、Kubernetes、Marathon、TOML/YAML 文件等任意 Provider 上落地一致的自定义错误页体验。

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