文档
一个 项目

reverse_proxy

将请求代理到一个或多个后端,具有可配置的传输、负载均衡、健康检查、请求操作和缓冲选项。

语法

reverse_proxy [<matcher>] [<upstreams...>] {
	# backends
	to      <upstreams...>
	dynamic <module> ...

	# load balancing
	lb_policy       <name> [<options...>]
	lb_retries      <retries>
	lb_try_duration <duration>
	lb_try_interval <interval>
	lb_retry_match  <request-matcher>

	# active health checking
	health_uri      <uri>
	health_upstream <ip:port>
	health_port     <port>
	health_interval <interval>
	health_passes   <num>
	health_fails	<num>
	health_timeout  <duration>
	health_status   <status>
	health_body     <regexp>
	health_follow_redirects
	health_headers {
		<field> [<values...>]
	}

	# passive health checking
	fail_duration     <duration>
	max_fails         <num>
	unhealthy_status  <status>
	unhealthy_latency <duration>
	unhealthy_request_count <num>

	# streaming
	flush_interval     <duration>
	request_buffers    <size>
	response_buffers   <size>
	stream_timeout     <duration>
	stream_close_delay <duration>

	# request/header manipulation
	trusted_proxies [private_ranges] <ranges...>
	header_up   [+|-]<field> [<value|regexp> [<replacement>]]
	header_down [+|-]<field> [<value|regexp> [<replacement>]]
	method <method>
	rewrite <to>

	# round trip
	transport <name> {
		...
	}

	# optionally intercept responses from upstream
	@name {
		status <code...>
		header <field> [<value>]
	}
	replace_status [<matcher>] <status_code>
	handle_response [<matcher>] {
		<directives...>

		# special directives only available in handle_response
		copy_response [<matcher>] [<status>] {
			status <status>
		}
		copy_response_headers [<matcher>] {
			include <fields...>
			exclude <fields...>
		}
	}
}

上游

  • <upstreams...> 是要代理到的上游(后端)列表。
  • to 是一种指定上游列表的替代方法,每行一个(或多个)。
  • dynamic 配置一个动态上游模块。这允许为每个请求动态获取上游列表。有关标准动态上游模块的描述,请参阅下面的 动态上游。动态上游在每次代理循环迭代时检索(即,如果启用了负载均衡重试,则每个请求可能多次),并且将优先于静态配置的上游。如果发生错误,代理将回退到使用任何静态配置的上游。

上游地址

静态上游地址可以采用 URL 的形式,该 URL 仅包含 scheme 和 host/port,或传统的 Caddy 网络地址。有效示例

  • localhost:4000
  • 127.0.0.1:4000
  • [::1]:4000
  • http://localhost:4000
  • https://example.com
  • h2c://127.0.0.1
  • example.com
  • unix//var/php.sock
  • unix+h2c//var/grpc.sock
  • localhost:8001-8006
  • [fe80::ea9f:80ff:fe46:cbfd%eth0]:443

默认情况下,连接通过明文 HTTP 连接到上游。当使用 URL 形式时,可以使用 scheme 来设置一些 transport 默认值作为简写。

  • 使用 https:// 作为 scheme 将使用 http 传输 并启用 tls

    此外,您可能需要覆盖 Host 标头,使其与 TLS SNI 值匹配,服务器使用该值进行路由和证书选择。有关更多详细信息,请参阅下面的 HTTPS 部分。

  • 使用 h2c:// 作为 scheme 将使用 http 传输 并将 HTTP 版本 设置为允许明文 HTTP/2 连接。

  • 使用 http:// 作为 scheme 与省略 scheme 相同,因为 HTTP 已经是默认设置。包含此语法是为了与其他 scheme 快捷方式对称。

Scheme 不能混合使用,因为它们修改了公共传输配置(启用 TLS 的传输不能同时承载 HTTPS 和明文 HTTP)。任何显式传输配置都不会被覆盖,省略 scheme 或使用其他端口也不会假定特定的传输。

当 IPv6 与区域(例如,具有特定网络接口的链路本地地址)一起使用时,不能使用 scheme 作为快捷方式,因为 % 将导致 URL 解析错误;请显式配置传输。

当使用 网络地址 形式时,网络类型指定为上游地址的前缀。这不能与 URL scheme 结合使用。作为一种特殊情况,unix+h2c/ 被支持作为 unix/ 网络的快捷方式,加上与 h2c:// scheme 相同的效果。端口范围作为快捷方式被支持,它扩展到具有相同主机的多个上游。

上游地址 不能 包含路径或查询字符串,因为这将暗示在代理时同时重写请求,这种行为未定义或不受支持。如果您需要这样做,可以使用 rewrite 指令。

如果地址不是 URL(即没有 scheme),则可以使用 占位符,但这会使上游动态静态化,这意味着就健康检查和负载均衡而言,潜在的许多不同后端充当单个静态上游。如果可能,我们建议使用 动态上游 模块。当使用占位符时,必须包含端口(通过占位符替换或作为地址的静态后缀)。

动态上游

Caddy 的反向代理标配一些动态上游模块。请注意,使用动态上游对负载均衡和健康检查有影响,具体取决于特定的策略配置:主动健康检查不适用于动态上游;如果上游列表相对稳定和一致(尤其是轮询),则负载均衡和被动健康检查效果最佳。理想情况下,动态上游模块仅返回健康、可用的后端。

SRV

从 SRV DNS 记录检索上游。

	dynamic srv [<full_name>] {
		service   <service>
		proto     <proto>
		name      <name>
		refresh   <interval>
		resolvers <ip...>
		dial_timeout        <duration>
		dial_fallback_delay <duration>
	}
  • <full_name> 是要查找的记录的完整域名(即 _service._proto.name)。
  • service 是完整名称的服务组件。
  • proto 是完整名称的协议组件。tcpudp
  • name 是名称组件。或者,如果 serviceproto 为空,则为要查询的完整域名。
  • refresh 是刷新缓存结果的频率。默认值:1m
  • resolvers 是要覆盖系统解析器的 DNS 解析器列表。
  • dial_timeout 是拨号查询的超时时间。
  • dial_fallback_delay 是在生成 RFC 6555 快速回退连接之前等待的时间。默认值:300ms

A/AAAA

从 A/AAAA DNS 记录检索上游。

	dynamic a [<name> <port>] {
		name      <name>
		port      <port>
		refresh   <interval>
		resolvers <ip...>
		dial_timeout        <duration>
		dial_fallback_delay <duration>
		versions ipv4|ipv6
	}
  • name 是要查询的域名。
  • port 是后端要使用的端口。
  • refresh 是刷新缓存结果的频率。默认值:1m
  • resolvers 是要覆盖系统解析器的 DNS 解析器列表。
  • dial_timeout 是拨号查询的超时时间。
  • dial_fallback_delay 是在生成 RFC 6555 快速回退连接之前等待的时间。默认值:300ms
  • versions 是要解析的 IP 版本列表。默认值:ipv4 ipv6,分别对应 A 和 AAAA 记录。

多重

附加多个动态上游模块的结果。如果您想要冗余的上游源,例如:由辅助 SRV 集群备份的主 SRV 集群,这将非常有用。

	dynamic multi {
		<source> [...]
	}
  • <source> 是动态上游的模块名称,后跟其配置。可以指定多个。

负载均衡

负载均衡通常用于在多个上游之间分配流量。通过启用重试,它也可以与一个或多个上游一起使用,以将请求保持到可以选择健康的上游为止(例如,等待并减轻重启或重新部署上游时的错误)。

默认情况下启用此功能,并使用 random 策略。默认情况下禁用重试。

  • lb_policy 是负载均衡策略的名称,以及任何选项。默认值:random

    对于涉及哈希的策略,使用 最高随机权重 (HRW) 算法,以确保具有相同哈希键的客户端或请求映射到相同的上游,即使上游列表发生更改也是如此。

    一些策略支持回退作为选项(如果注明),在这种情况下,它们采用一个 ,其中包含 fallback <policy>,它采用另一个负载均衡策略。对于这些策略,默认回退是 random。配置回退允许在主要策略未选择一个时使用辅助策略,从而实现强大的组合。如果需要,可以嵌套多次回退。

    例如,header 可以用作主要策略,以允许开发人员选择特定的上游,而 first 作为所有其他连接的回退,以实现主/辅助故障转移。

    lb_policy header X-Upstream {
    	fallback first
    }
    
    • random 随机选择一个上游

    • random_choose <n> 随机选择两个或多个上游,然后选择负载最轻的一个(n 通常为 2)

    • first 从配置中定义的顺序中选择第一个可用的上游,允许主/辅助故障转移;请记住启用健康检查以及此项,否则不会发生故障转移

    • round_robin 依次迭代每个上游

    • weighted_round_robin <weights...> 依次迭代每个上游,并考虑提供的权重。权重参数的数量应与配置的上游数量匹配。权重应为非负整数。例如,对于两个上游和权重 5 1,在选择第二个上游一次之前,将连续选择第一个上游 5 次,然后循环重复。如果将零用作权重,则将禁用为新请求选择上游。

    • least_conn 选择当前请求数最少的上游;如果多个主机具有最少的请求数,则随机选择其中一个主机

    • ip_hash 将远程 IP(直接对等方)映射到粘性上游

    • client_ip_hash 将客户端 IP 映射到粘性上游;这最好与 servers > trusted_proxies 全局选项 配对使用,该选项启用真实客户端 IP 解析,否则其行为与 ip_hash 相同

    • uri_hash 将请求 URI(路径和查询)映射到粘性上游

    • query [key] 通过哈希查询值将请求查询映射到粘性上游;如果指定的键不存在,则将使用回退策略来选择上游(默认为 random

    • header [field] 通过哈希标头值将请求标头映射到粘性上游;如果指定的标头字段不存在,则将使用回退策略来选择上游(默认为 random

    • cookie [<name> [<secret>]] 在来自客户端的第一个请求(当没有 cookie 时)时,将使用回退策略来选择上游(默认为 random),并且 Set-Cookie 标头将添加到响应中(如果未指定,则默认 cookie 名称为 lb)。cookie 值是所选上游的拨号地址,使用 HMAC-SHA256 哈希(使用 <secret> 作为共享密钥,如果未指定,则为空字符串)。

      在后续请求中,如果 cookie 存在,则 cookie 值将映射到相同的上游(如果可用);如果不可用或未找到,则使用回退策略选择新的上游,并将 cookie 添加到响应中。

      如果您希望出于调试目的使用特定的上游,则可以使用密钥哈希上游地址,并在 HTTP 客户端(浏览器或其他方式)中设置 cookie。例如,对于 PHP,您可以运行以下命令来计算 cookie 值,其中 10.1.0.10:8080 是您的上游地址之一,而 secret 是您配置的密钥。

      echo hash_hmac('sha256', '10.1.0.10:8080', 'secret');
      // cdd96966817dd14a99f47ee17451464f29998da170814a16b483e4c1ff4c48cf
      

      您可以在浏览器中通过 Javascript 控制台设置 cookie,例如设置名为 lb 的 cookie

      document.cookie = "lb=cdd96966817dd14a99f47ee17451464f29998da170814a16b483e4c1ff4c48cf";
      
  • lb_retries 是在每个请求的下一个可用主机关闭时,重试选择可用后端多少次。默认情况下,禁用重试(零)。

    如果还配置了 lb_try_duration,则如果达到持续时间,重试可能会提前停止。换句话说,重试持续时间优先于重试计数。

  • lb_try_duration 是一个 持续时间值,用于定义在每个请求的下一个可用主机关闭时,尝试选择可用后端多长时间。默认情况下,禁用重试(零持续时间)。

    客户端将等待最长达这么长时间,而负载均衡器尝试查找可用的上游主机。一个合理的起点可能是 5s,因为 HTTP 传输的默认拨号超时为 3s,因此如果无法访问第一个选定的上游,则应允许至少一次重试;但请随意尝试以找到适合您用例的正确平衡点。

  • lb_try_interval 是一个 持续时间值,用于定义从池中选择下一个主机之间等待的时间。默认值为 250ms。仅当对上游主机的请求失败时才相关。请注意,如果将此值设置为 0 且非零 lb_try_duration,则如果所有后端都关闭且延迟非常低,则可能导致 CPU 空转。

  • lb_retry_match 限制了哪些请求允许重试。请求必须匹配此条件才能在与上游的连接成功但后续往返失败时重试。如果与上游的连接失败,则始终允许重试。默认情况下,仅重试 GET 请求。

    此选项的语法与 命名请求匹配器 的语法相同,但不带 @name。如果您只需要单个匹配器,则可以在同一行上配置它。对于多个匹配器,需要一个块。

主动健康检查

主动健康检查在后台定时执行健康检查。要启用此功能,需要 health_urihealth_port

  • health_uri 是主动健康检查的 URI 路径(和可选的查询)。

  • health_upstream 是用于主动健康检查的 ip:port,如果与上游不同。这应与 health_header{http.reverse_proxy.active.target_upstream} 结合使用。

  • health_port 是用于主动健康检查的端口,如果与上游的端口不同。如果使用 health_upstream,则忽略此项。

  • health_interval 是一个 持续时间值,用于定义执行主动健康检查的频率。默认值:30s

  • health_passes 是在将后端再次标记为健康之前所需的连续健康检查次数。默认值:1

  • health_fails 是在将后端标记为不健康之前所需的连续健康检查次数。默认值:1

  • health_timeout 是一个 持续时间值,用于定义等待回复多长时间后将后端标记为关闭。默认值:5s

  • health_status 是从健康后端期望的 HTTP 状态代码。可以是 3 位状态代码,也可以是状态代码类,以 xx 结尾。例如:200(默认值)或 2xx

  • health_body 是一个子字符串或正则表达式,用于匹配主动健康检查的响应正文。如果后端未返回匹配的正文,则将其标记为关闭。

  • health_follow_redirects 将使健康检查跟随上游提供的重定向。默认情况下,重定向响应将导致健康检查计为失败。

  • health_headers 允许指定要在主动健康检查请求上设置的标头。如果您需要更改 Host 标头,或者您需要在健康检查中为后端提供一些身份验证,这将非常有用。

被动健康检查

被动健康检查与实际代理请求内联发生。要启用此功能,需要 fail_duration

  • fail_duration 是一个 持续时间值,用于定义记住失败请求多长时间。持续时间 > 0 启用被动健康检查;默认值为 0(关闭)。一个合理的起点可能是 30s,以平衡错误率与将不健康的上游重新联机时的响应能力;但请随意尝试以找到适合您用例的正确平衡点。

  • max_fails 是在 fail_duration 内认为后端关闭之前所需的失败请求的最大数量;必须 >= 1;默认值为 1

  • unhealthy_status 如果响应返回这些状态代码之一,则将请求计为失败。可以是 3 位状态代码或状态代码类,以 xx 结尾,例如:4045xx

  • unhealthy_latency 是一个 持续时间值,如果获取响应的时间过长,则将请求计为失败。

  • unhealthy_request_count 是在将后端标记为关闭之前允许的同时请求数。换句话说,如果特定后端当前正在处理这么多请求,则将其视为“过载”,并且将优先选择其他后端。

    这应该是一个相当大的数字;配置此值意味着代理将具有 unhealthy_request_count × upstreams_count 的总并发请求限制,并且超过该点的任何请求都将因没有可用的上游而导致错误。

事件

当上游从健康状态转换为不健康状态或反之亦然时,会发出 事件。这些事件可用于触发其他操作,例如发送通知或记录消息。事件如下

  • healthy 当上游从先前的不健康状态标记为健康状态时发出
  • unhealthy 当上游从先前的健康状态标记为不健康状态时发出

在这两种情况下,host 都作为元数据包含在事件中,以标识状态发生更改的上游。它可以与 exec 事件处理程序一起用作占位符,例如 {event.data.host}

流式传输

默认情况下,代理部分缓冲响应以提高线路效率。

代理还支持 WebSocket 连接,执行 HTTP 升级请求,然后将连接转换为双向隧道。

  • flush_interval 是一个 持续时间值,用于调整 Caddy 应多久将响应缓冲区刷新到客户端。默认情况下,不进行定期刷新。负值(通常为 -1)表示“低延迟模式”,该模式完全禁用响应缓冲,并在每次写入客户端后立即刷新,即使客户端提前断开连接,也不会取消对后端的请求。如果响应满足以下条件之一,则忽略此选项,并且响应会立即刷新到客户端:

    • Content-Type: text/event-stream
    • Content-Length 未知
    • 代理两侧均为 HTTP/2,Content-Length 未知,并且 Accept-Encoding 未设置或为 "identity"
  • request_buffers 将导致代理从请求正文中读取最多 <size> 字节的数据到缓冲区中,然后再将其发送到上游。这非常低效,仅应在上游需要读取没有延迟的请求正文时才执行(这是上游应用程序应修复的问题)。这接受 go-humanize 支持的所有大小格式。

  • response_buffers 将导致代理从响应正文中读取最多 <size> 字节的数据到缓冲区中,然后再将其返回给客户端。出于性能原因,应尽可能避免这种情况,但如果后端具有更严格的内存约束,则可能很有用。这接受 go-humanize 支持的所有大小格式。

  • stream_timeout 是一个 持续时间值,超过此持续时间后,流式请求(如 WebSocket)将在超时结束时被强制关闭。这实际上是在连接保持打开时间过长时取消连接。一个合理的起点可能是 24h,以剔除超过一天的连接。默认值:无超时。

  • stream_close_delay 是一个 持续时间值,它延迟了在卸载配置时强制关闭流式请求(如 WebSocket);相反,流将保持打开状态直到延迟完成。换句话说,启用此功能可防止流在 Caddy 的配置重新加载时立即关闭。启用此功能可能是避免重新连接客户端的雷鸣般集群的好主意,这些客户端的连接已由先前的配置关闭关闭。一个合理的起点可能是 5m,以允许用户在配置重新加载后有 5 分钟的时间自然离开页面。默认值:无延迟。

标头

代理可以操作标头在自身与后端之间

  • header_up 设置、添加(使用 + 前缀)、删除(使用 - 前缀)或在发送到后端的上游请求标头中执行替换(通过使用两个参数,搜索和替换)。

  • header_down 设置、添加(使用 + 前缀)、删除(使用 - 前缀)或在来自后端的下游响应标头中执行替换(通过使用两个参数,搜索和替换)。

例如,要设置请求标头,覆盖任何现有值

header_up Some-Header "the value"

要添加响应标头;请注意,一个标头字段可以有多个值

header_down +Some-Header "first value"
header_down +Some-Header "second value"

要删除请求标头,阻止其到达后端

header_up -Some-Header

要删除所有匹配的请求标头,使用后缀匹配

header_up -Some-*

要删除所有请求标头,以便能够单独添加您想要的标头(不建议)

header_up -*

要对请求标头执行正则表达式替换

header_up Some-Header "^prefix-([A-Za-z0-9]*)$" "replaced-$1-suffix"

使用的正则表达式语言是 RE2,包含在 Go 中。请参阅 RE2 语法参考Go regexp 语法概述。替换字符串是 展开的,允许使用捕获的值,例如 $1 是第一个捕获组。

默认值

默认情况下,Caddy 将传入标头(包括 Host)传递到后端,而无需修改,但有三个例外

对于这些 X-Forwarded-* 标头,默认情况下,代理将忽略来自传入请求的值,以防止欺骗。

如果 Caddy 不是客户端连接到的第一个服务器(例如,当 CDN 在 Caddy 前面时),您可以配置 trusted_proxies,其中包含 IP 范围 (CIDR) 列表,来自这些 IP 范围的传入请求被信任为已发送这些标头的良好值。

强烈建议您通过 servers > trusted_proxies 全局选项 而不是在代理中配置此项,以便这适用于服务器中的所有代理处理程序,并且这具有启用客户端 IP 解析的优势。

此外,当使用 http 传输 时,如果客户端的请求中缺少 Accept-Encoding: gzip 标头,则将设置该标头。这允许上游在可以的情况下提供压缩内容。可以使用传输上的 compression off 禁用此行为。

HTTPS

由于(大多数)标头在被代理时保留其原始值,因此在代理到 HTTPS 时,通常需要使用配置的上游地址覆盖 Host 标头,以便 Host 标头与 TLS ServerName 值匹配

reverse_proxy https://example.com {
	header_up Host {upstream_hostport}
}

X-Forwarded-Host 标头仍然 默认 传递,因此如果上游需要知道原始 Host 标头值,它仍然可以使用该标头。

重写

默认情况下,Caddy 使用与传入请求相同的 HTTP 方法和 URI 执行上游请求,除非在中间件链到达 reverse_proxy 之前执行了重写。

在代理之前,请求被克隆;这确保在处理程序期间对请求所做的任何修改都不会泄漏到其他处理程序。这在需要在代理之后继续处理的情况下很有用。

除了 标头操作 之外,还可以在请求发送到上游之前更改请求的方法和 URI

  • method 更改克隆请求的 HTTP 方法。如果方法更改为 GETHEAD,则此处理程序将不会将传入请求的正文发送到上游。如果您希望允许其他处理程序使用请求正文,这将非常有用。
  • rewrite 更改克隆请求的 URI(路径和查询)。这类似于 rewrite 指令,但它不会将重写持久化到此处理程序的范围之外。

这些重写通常对于“预检查请求”之类的模式很有用,在这种模式下,请求被发送到另一台服务器以帮助决定如何继续处理当前请求。

例如,可以将请求发送到身份验证网关,以确定请求是否来自经过身份验证的用户(例如,请求具有会话 cookie)并且应继续,或者是否应重定向到登录页面。对于此模式,Caddy 提供了一个快捷指令 forward_auth 以跳过大多数配置样板。

传输

Caddy 的代理传输是可插拔的

  • transport 定义如何与后端通信。默认值为 http

http 传输

transport http {
	read_buffer             <size>
	write_buffer            <size>
	max_response_header     <size>
	proxy_protocol          v1|v2
	dial_timeout            <duration>
	dial_fallback_delay     <duration>
	response_header_timeout <duration>
	expect_continue_timeout <duration>
	resolvers <ip...>
	tls
	tls_client_auth <automate_name> | <cert_file> <key_file>
	tls_insecure_skip_verify
	tls_curves <curves...>
	tls_timeout <duration>
	tls_trust_pool <module>
	tls_server_name <server_name>
	tls_renegotiation <level>
	tls_except_ports <ports...>
	keepalive [off|<duration>]
	keepalive_interval <interval>
	keepalive_idle_conns <max_count>
	keepalive_idle_conns_per_host <count>
	versions <versions...>
	compression off
	max_conns_per_host <count>
	forward_proxy_url <url>
}
  • read_buffer 是读取缓冲区的大小(以字节为单位)。它接受 go-humanize 支持的所有格式。默认值:4KiB

  • write_buffer 是写入缓冲区的大小(以字节为单位)。它接受 go-humanize 支持的所有格式。默认值:4KiB

  • max_response_header 是从响应标头读取的最大字节数。它接受 go-humanize 支持的所有格式。默认值:10MiB

  • proxy_protocol 在与上游的连接上启用 PROXY 协议(由 HAProxy 推广),在前面添加真实的客户端 IP 数据。如果 Caddy 位于另一个代理后面,则最好与 servers > trusted_proxies 全局选项 配对使用。支持 v1v2 版本。仅当您知道上游服务器能够解析 PROXY 协议时才应使用此选项。默认情况下,此选项处于禁用状态。

  • dial_timeout 是连接到上游套接字时等待的最长 持续时间。默认值:3s

  • dial_fallback_delay 是在生成 RFC 6555 快速回退连接之前等待的最长 持续时间。负值禁用此功能。默认值:300ms

  • response_header_timeout 是等待从上游读取响应标头的最长 持续时间。默认值:无超时。

  • expect_continue_timeout 如果请求具有标头 Expect: 100-continue,则在完全写入请求标头后,等待上游的第一个响应标头的最长 持续时间。默认值:无超时。

  • read_timeout 是等待从后端下一次读取的最长 持续时间。默认值:无超时。

  • write_timeout 是等待下一次写入后端的最长 持续时间。默认值:无超时。

  • resolvers 是 DNS 解析器列表,用于覆盖系统解析器。

  • tls 使用 HTTPS 连接后端。如果您使用 https:// 方案或端口 :443 指定后端,或者配置了以下任何 tls_* 选项,则将自动启用此功能。

  • tls_client_auth 启用 TLS 客户端身份验证,有两种方式:(1)指定一个域名,Caddy 应该为其获取证书并保持续订,或者(2)指定一个证书和密钥文件,用于与后端进行 TLS 客户端身份验证。

  • tls_insecure_skip_verify 关闭 TLS 握手验证,使连接不安全且容易受到中间人攻击。请勿在生产环境中使用。

  • tls_curves 是用于上游连接的椭圆曲线列表。Caddy 的默认设置是现代且安全的,因此只有在您有特定要求时才需要配置此项。

  • tls_timeout 是等待 TLS 握手完成的最长持续时间。默认值:无超时。

  • tls_trust_pool 配置受信任证书颁发机构的来源,类似于 trust_pool 子指令,该子指令在 tls 指令文档中描述。标准 Caddy 安装中可用的信任池源列表可在此处找到。

  • tls_server_name 设置在验证 TLS 握手中收到的证书时使用的服务器名称。默认情况下,这将使用上游地址的主机部分。

    只有当您的上游地址与上游可能使用的证书不匹配时,才需要覆盖此设置。例如,如果上游地址是 IP 地址,那么您需要将此项配置为上游服务器正在服务的主机名。

    可以使用请求占位符,在这种情况下,将在每个请求上使用 HTTP 传输配置的克隆,这可能会导致性能损失。

  • tls_renegotiation 设置 TLS 重新协商级别。TLS 重新协商是指在第一次握手之后执行后续握手的行为。级别可以是以下之一:

    • never(默认值)禁用重新协商。
    • once 允许远程服务器每个连接请求重新协商一次。
    • freely 允许远程服务器重复请求重新协商。
  • tls_except_ports 启用 TLS 后,如果上游目标使用给定的端口之一,则将为这些连接禁用 TLS。当配置动态上游时,这可能很有用,因为某些上游期望 HTTP 请求,而另一些上游期望 HTTPS 请求。

  • keepalive 可以是 off持续时间值,用于指定保持连接打开的时间(超时)。默认值:2m

  • keepalive_interval 是活动探测之间的持续时间。默认值:30s

  • keepalive_idle_conns 定义要保持活动状态的最大连接数。默认值:无限制。

  • keepalive_idle_conns_per_host 如果非零,则控制每个主机保持的最大空闲(保持活动)连接数。默认值:32

  • versions 允许自定义要支持的 HTTP 版本。

    有效选项包括:1.12h2c3

    默认值:1.1 2,或者如果上游的方案h2c://,则默认值为 h2c 2

    h2c 启用与上游的明文 HTTP/2 连接。这是一个非标准功能,不使用 Go 的默认 HTTP 传输,因此与其他功能互斥。

    3 启用与上游的 HTTP/3 连接。⚠️ 这是一个实验性功能,可能会发生变化。

  • compression 可以通过将其设置为 off 来禁用与后端的压缩。

  • max_conns_per_host 可选地限制每个主机的连接总数,包括拨号、活动和空闲状态的连接。默认值:无限制。

  • forward_proxy_url 指定 HTTP 传输将用于代理请求到上游服务器的服务器 URL。默认情况下,Caddy 遵循通过环境变量配置的代理,如 Go stdlib 中所述,例如 HTTP_PROXY。当为此参数提供值时,请求将按以下顺序流经反向代理:

    • 客户端(用户)🡒 reverse_proxy 🡒 forward_proxy_url 🡒 上游

fastcgi 传输

transport fastcgi {
	root  <path>
	split <at>
	env   <key> <value>
	resolve_root_symlink
	dial_timeout  <duration>
	read_timeout  <duration>
	write_timeout <duration>
	capture_stderr
}
  • root 是站点的根目录。默认值:{http.vars.root} 或当前工作目录。

  • split 是拆分路径以在 URI 末尾获取 PATH_INFO 的位置。

  • env 为给定的值设置额外的环境变量。可以多次指定以设置多个环境变量。

  • resolve_root_symlink 启用通过评估符号链接(如果存在)将 root 目录解析为其实际值。

  • dial_timeout 是连接到上游套接字时等待的时间。接受持续时间值。默认值:3s

  • read_timeout 是从 FastCGI 服务器读取时等待的时间。接受持续时间值。默认值:无超时。

  • write_timeout 是发送到 FastCGI 服务器时等待的时间。接受持续时间值。默认值:无超时。

  • capture_stderr 启用捕获和记录上游 fastcgi 服务器在 stderr 上发送的任何消息。默认情况下,日志记录在 WARN 级别完成。如果响应具有 4xx5xx 状态,则将改用 ERROR 级别。默认情况下,stderr 被忽略。

拦截响应

反向代理可以配置为拦截来自后端的响应。为了方便这一点,可以定义响应匹配器(类似于请求匹配器的语法),并且将调用第一个匹配的 handle_response 路由。

当调用响应处理程序时,来自后端的响应不会写入客户端,而是执行配置的 handle_response 路由,并且由该路由来写入响应。如果路由没有写入响应,则请求处理将继续执行任何排序在reverse_proxy 之后的处理程序。

  • @name响应匹配器的名称。只要每个响应匹配器都有唯一的名称,就可以定义多个匹配器。可以在状态代码和响应标头的存在或值上匹配响应。

  • replace_status 仅当给定匹配器匹配时,更改响应的状态代码。

  • handle_response 定义当给定匹配器匹配时(或者,如果省略匹配器,则为所有响应)要执行的路由。将应用第一个匹配的块。在 handle_response 块内,可以使用任何其他 指令

此外,在 handle_response 内部,可以使用两个特殊的处理程序指令

  • copy_response 将从后端接收到的响应正文复制回客户端。可以选择在执行此操作时更改响应的状态代码。此指令排序在 respond 之前

  • copy_response_headers 将响应标头从后端复制到客户端,可以选择包含排除标头字段列表(不能同时指定 includeexclude)。此指令排序在 header 之后

handle_response 路由中将提供三个占位符

  • {rp.status_code} 来自后端响应的状态代码。

  • {rp.status_text} 来自后端响应的状态文本。

  • {rp.header.*} 来自后端响应的标头。

示例

反向代理所有请求到本地后端

example.com {
	reverse_proxy localhost:9005
}

负载均衡所有请求在 3 个后端之间

example.com {
	reverse_proxy node1:80 node2:80 node3:80
}

相同,但仅限 /api 内的请求,并通过使用 cookie 策略保持粘性

example.com {
	reverse_proxy /api/* node1:80 node2:80 node3:80 {
		lb_policy cookie api_sticky
	}
}

使用 主动健康检查来确定哪些后端是健康的,并启用失败连接的 重试,保持请求直到找到健康的后端

example.com {
	reverse_proxy node1:80 node2:80 node3:80 {
		health_uri /healthz
		lb_try_duration 5s
	}
}

配置一些传输选项

example.com {
	reverse_proxy localhost:8080 {
		transport http {
			dial_timeout 2s
			response_header_timeout 30s
		}
	}
}

反向代理到 HTTPS 上游

example.com {
	reverse_proxy https://example.com {
		header_up Host {upstream_hostport}
	}
}

反向代理到 HTTPS 上游,但 ⚠️ 禁用 TLS 验证。这是不推荐的,因为它禁用了 HTTPS 提供的所有安全检查;如果可能,首选在专用网络中通过 HTTP 代理,因为它避免了虚假的安全感

example.com {
	reverse_proxy 10.0.0.1:443 {
		transport http {
			tls_insecure_skip_verify
		}
	}
}

相反,您可以通过显式信任上游的证书来建立与上游的信任,并且(可选)设置 TLS-SNI 以匹配上游证书中的主机名

example.com {
	reverse_proxy 10.0.0.1:443 {
		transport http {
			tls_trusted_ca_certs /path/to/cert.pem
			tls_server_name app.example.com
		}
	}
}

剥离路径前缀然后再代理;但请注意子文件夹问题

example.com {
	handle_path /prefix/* {
		reverse_proxy localhost:9000
	}
}

在使用 rewrite 代理之前替换路径前缀

example.com {
	handle_path /old-prefix/* {
		rewrite * /new-prefix{path}
		reverse_proxy localhost:9000
	}
}

X-Accel-Redirect 支持,即按需服务静态文件,通过拦截响应

example.com {
	reverse_proxy localhost:8080 {
		@accel header X-Accel-Redirect *
		handle_response @accel {
			root    * /path/to/private/files
			rewrite * {rp.header.X-Accel-Redirect}
			method  * GET
			file_server
		}
	}
}

来自上游的错误的自定义错误页面,通过按状态代码拦截错误响应

example.com {
	reverse_proxy localhost:8080 {
		@error status 500 503
		handle_response @error {
			root    * /path/to/error/pages
			rewrite * /{rp.status_code}.html
			file_server
		}
	}
}

A/AAAA 记录 DNS 查询动态获取后端

example.com {
	reverse_proxy {
		dynamic a example.com 9000
	}
}

SRV 记录 DNS 查询动态获取后端

example.com {
	reverse_proxy {
		dynamic srv _api._tcp.example.com
	}
}

当创建中间服务以进行更彻底的健康检查时,使用 主动健康检查health_upstream 可能会有所帮助。然后可以使用 {http.reverse_proxy.active.target_upstream} 作为标头,以将原始上游提供给健康检查服务。

example.com {
	reverse_proxy node1:80 node2:80 node3:80 {
		health_uri /health
		health_upstream 127.0.0.1:53336
		health_headers {
			Full-Upstream {http.reverse_proxy.active.target_upstream}
		}
	}
}