如何为HTTP配置请求和响应转换
代理请求时,通常修改请求或响应的各个部分,以适应目标服务器的要求或流出其他数据,例如客户端的原始 IP 地址。 此过程通过Transforms实现。 为应用程序全局定义转换类型,然后各个路由提供参数以启用和配置这些转换。 原始请求对象不会由这些转换修改,只修改代理请求。
如果内置转换集不足,则可以通过定制化扩展添加自定义转换。
( PS: 如下实现参考于 Yarp )
默认行为
遵循HTTP代理公认行为,为方便大家使用,以下转换默认启用
Host
代理请求默认会修改为目标服务器地址中指定的主机名
如需特殊修改请使用
RequestHeaderOriginalHost
X-Forwarded-For
将客户端的 IP 地址设置为 X-Forwarded-For 标头 (默认情况下会覆盖)
如需特殊修改(比如多层代理情况下想保留请求中已有的值以准确传递客户端的IP)请使用
X-Forwarded
X-Forwarded-Proto
将请求的原始方案 (http/https) 设置为 X-Forwarded-Proto 标头。
如需特殊修改请使用
X-Forwarded
X-Forwarded-Host
将请求的原始方案 (http/https) 设置为 X-Forwarded-Proto 标头。
如需特殊修改请使用
X-Forwarded
X-Forwarded-Prefix
将请求的原始 PathBase 标头(如果有)设置为 X-Forwarded-Prefix 标头。 (理论上默认情况下应该没有, 除非特殊使用 PathBase 之类的中间件)
如需特殊修改请使用
X-Forwarded
举例,传入 http://IncomingHost:5000/path
的以下请求:
GET /path HTTP/1.1
Host: IncomingHost:5000
Accept: */*
header1: foo
将转换并代理到目标服务器 https://DestinationHost:6000/
,如下所示,使用以下默认值:
GET /path HTTP/1.1
Host: DestinationHost:6000
Accept: */*
header1: foo
X-Forwarded-For: 5.5.5.5
X-Forwarded-Proto: http
X-Forwarded-Host: IncomingHost:5000
请求转换
请求转换包括请求路径、查询、HTTP 版本、方法和标头。内置转换集支持以下:
PathPrefix
为请求路径加上给定值前缀。
Key | Memo |
---|---|
PathPrefix | 以“/”开头的路径 |
示例:/request/path
变为 /prefix/request/path
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{ "PathPrefix": "/prefix" }
]
}
}
}
}
PathRemovePrefix
将从请求路径中删除匹配的前缀。 匹配是在路径段边界(/) 上进行的。 如果前缀不匹配,则不会进行更改。
Key | Memo |
---|---|
PathRemovePrefix | 以“/”开头的路径 |
示例:/prefix/request/path
变为 /request/path
, /prefix2/request/path
未修改
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{ "PathRemovePrefix": "/prefix" }
]
}
}
}
}
PathSet
将使用给定值设置请求路径。
Key | Memo |
---|---|
PathSet | 以“/”开头的路径 |
示例:/request/path
变为 /newpath
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{ "PathSet": "/newpath" }
]
}
}
}
}
QueryValueParameter
在请求查询字符串中添加或替换参数
Key | Memo |
---|---|
QueryValueParameter | 查询字符串参数的名称 |
Set/Append | 静态值 |
示例:将添加一个包含名称 foo 的查询字符串参数,并将其设置为静态值 bar。即 /request/path?a=v&foo=x
变为 /request/path?a=v&foo=x&foo=bar
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"QueryValueParameter": "foo",
"Append": "bar"
}
]
}
}
}
}
QueryRemoveParameter
从请求查询字符串中删除指定的参数
Key | Memo |
---|---|
QueryRemoveParameter | 查询字符串参数的名称 |
示例:如果请求中存在,这将删除具有名称 foo 的查询字符串参数。即 /request/path?a=v&foo=x
变为 /request/path?a=v
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"QueryRemoveParameter": "foo"
}
]
}
}
}
}
HttpMethodChange
更改请求中使用的 http 方法
Key | Memo |
---|---|
HttpMethodChange | 需要替换的 HTTP 方法 |
Set | 新的 http 方法 |
示例:将 PUT 请求更改为 POST。即 PUT /request/path
变为 POST /request/path?a=v
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"HttpMethodChange": "PUT",
"Set": "POST"
}
]
}
}
}
}
RequestHeadersCopy
这将设置是否将所有传入请求标头复制到代理请求中。 此设置默认处于启用状态,可通过将转换配置为 false 值来禁用。 即使禁用了此功能,引用特定标头的转换仍将运行。
Key | Memo |
---|---|
RequestHeadersCopy | true/false |
示例:禁用
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"RequestHeadersCopy": "false"
}
]
}
}
}
}
RequestHeaderOriginalHost
这指定是否应将传入请求主机标头复制到代理请求。 此设置默认处于禁用状态,可以通过配置具有 true 值的转换来启用此设置。 直接引用 Host 标头的转换将替代此转换。
Key | Memo |
---|---|
RequestHeaderOriginalHost | true/false |
示例:启用
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"RequestHeaderOriginalHost": "true"
}
]
}
}
}
}
RequestHeader
这会设置或追加命名标头的值。 Set 替换任何现有标头。 Append 会添加具有给定值的额外标头。 注意:不建议将“”设置为标头值,并可能导致未定义的行为。
Key | Memo |
---|---|
RequestHeader | 标头名称 |
Set/Append | 标头值 |
示例:
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"RequestHeader": "HeaderName",
"Append": "value"
}
]
}
}
}
}
RequestHeaderRemove
删除请求标头
Key | Memo |
---|---|
RequestHeaderRemove | 标头名称 |
示例:
原始请求
MyHeader: MyValue
AnotherHeader: AnotherValue
处理后
AnotherHeader: AnotherValue
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"RequestHeaderRemove": "MyHeader"
}
]
}
}
}
}
RequestHeadersAllowed
默认将大多数请求标头复制到代理请求(请参阅 RequestHeadersCopy)。 某些安全模型仅允许代理特定标头。 此转换将禁用 RequestHeadersCopy,并且仅复制给定标头。 如果未包含在允许列表中,则修改或追加到现有标头的其他转换可能会受到影响。
请注意,默认情况下,某些标头不会复制,因为它们特定于连接或其他安全敏感(例如Connection)。 Alt-Svc 将这些标头名称放在允许列表中将绕过该限制,但强烈建议不要这样做,因为它可能会对代理的功能产生负面影响或导致安全漏洞。
Key | Memo |
---|---|
RequestHeadersAllowed | 以分号分隔的允许标头名称列表。 |
示例:
原始请求
Header1: value1
header2: value2
AnotherHeader: AnotherValue
处理后
Header1: value1
header2: value2
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"RequestHeadersAllowed": "Header1;header2"
}
]
}
}
}
}
X-Forwarded
当代理连接到目标服务器时,连接与客户端对代理建立的连接是独立的。 目标服务器可能需要原始连接信息进行安全检查,并正确生成链接和重定向的绝对 URI。 若要将有关客户端连接的信息传递给目标,可以添加一组额外的标头。 在Forwarded标准建立之前,一种常见的解决方案是使用X-Forwarded-标头。 没有定义X-Forwarded-标头的官方标准,因此实现可能因服务器而异,请检查目标服务器是否支持。
即使路由配置中未指定,也默认启用此转换。
将 X-Forwarded 值设置为包含你需要启用的标头的逗号分隔列表。 默认情况下,所有 for 标头都处于启用状态。 可以通过指定值 "Off"来禁用所有值。
Prefix 指定要用于每个标头的标头名称前缀。 使用默认X-Forwarded-前缀时,生成的标头将为X-Forwarded-For、X-Forwarded-Proto和X-Forwarded-HostX-Forwarded-Prefix。
转换操作指定了如何将每个标头与同名的现有标头组合在一起。 它可以是“Set”、“Append”、“Remove”或“Off”(完全禁用转换功能)。 遍历多个代理的请求可能会累积此类标头的列表,目标服务器需要评估列表以确定原始值。 如果操作是“Set”并且关联的值在请求中不可用(例如 RemoteIpAddress 为 null),则仍会删除现有的任何标头以防止欺骗。
{Prefix}For 标头值取自 HttpContext.Connection.RemoteIpAddress 表示前一个调用方 IP 地址。 不包括端口。 IPv6 地址不包括方括号 []。
{Prefix}Proto 标头值取自 HttpContext.Request.Scheme 指示前一个调用方是否使用了 HTTP 或 HTTPS。
{Prefix}主机标头值取自传入请求的主机标头。 这独立于上面指定的 RequestHeaderOriginalHost。 Unicode/IDN 主机经过 punycode 编码。
{Prefix}Prefix 标头值取自 HttpContext.Request.PathBase。 生成代理请求时不使用 PathBase 属性,因此目标服务器需要原始值才能正确生成链接和重定向。 该值采用百分比编码 URI 格式。
Key | Memo |
---|---|
X-Forwarded | 要应用于下面列出的所有 X-Forwarded* 的默认操作(Set、Append、Remove、Off) |
For | 要应用于下面列出的所有 X-Forwarded* 的默认操作(Set、Append、Remove、Off) |
Proto | 要应用于下面列出的所有 X-Forwarded* 的默认操作(Set、Append、Remove、Off) |
Prefix | 要应用于下面列出的所有 X-Forwarded* 的默认操作(Set、Append、Remove、Off) |
HeaderPrefix | 标头名称前缀 |
示例:
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"X-Forwarded": "Set",
"For": "Remove",
"Proto": "Append",
"Prefix": "Off",
"HeaderPrefix": "X-Forwarded-"
}
]
}
}
}
}
Forwarded
标头 Forwarded 由 RFC 7239 定义。 它整合了与非官方 X-Forwarded 标头相同的许多功能,将信息流向目标服务器,否则将使用代理隐藏这些信息。
启用此转换将禁用默认的 X-Forwarded 转换,因为它们以其他格式传递类似的信息。 仍可以显式启用 X-Forwarded 转换。
操作:这指定转换应如何处理现有的 Forwarded 标头。 它可以是“Set”、“Append”、“Remove”或“Off”(完全禁用转换功能)。 遍历多个代理的请求可能会累积此类标头的列表,目标服务器需要评估列表以确定原始值。
Proto:此值取自 HttpContext.Request.Scheme 指示前一个调用方是否使用了 HTTP 或 HTTPS。
主机:此值取自传入请求的主机标头。 这独立于上面指定的 RequestHeaderOriginalHost。 Unicode/IDN 主机经过 punycode 编码。
对于:此值标识以前的调用方。 IP 地址取自 HttpContext.Connection.RemoteIpAddress。 有关详细信息,请参阅下面的 ByFormat 和 ForFormat。
接收源:此值标识代理接收请求的位置。 IP 地址取自 HttpContext.Connection.LocalIpAddress。 有关详细信息,请参阅下面的 ByFormat 和 ForFormat。
ByFormat 和 ForFormat:
RFC 允许 By 和 For 字段 的各种格式 。 它要求默认格式使用此处标识为 Random 的经过模糊处理的标识符。
Key | Memo |
---|---|
Forwarded | 逗号分隔列表,其中包含以下任何值:for、by、proto、host |
ForFormat | Random/RandomAndPort/RandomAndRandomPort/Unknown/UnknownAndPort/UnknownAndRandomPort/Ip/IpAndPort/IpAndRandomPort |
ByFormat | Random/RandomAndPort/RandomAndRandomPort/Unknown/UnknownAndPort/UnknownAndRandomPort/Ip/IpAndPort/IpAndRandomPort |
Action | Set、Append、Remove、Off |
示例:
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"Forwarded": "by,for,host,proto",
"ByFormat": "Random",
"ForFormat": "IpAndPort",
"Action": "Append"
}
]
}
}
}
}
ClientCert
将入站连接上使用的客户端证书作为标头转发到目标
由于入站和出站连接是独立的,因此需要有一种方法将任何入站客户端证书传递到目标服务器。 此转换会导致从 HttpContext.Connection.ClientCertificate 中获取的客户端证书进行 Base64 编码,并设置为给定标头名称的值。 目标服务器可能需要该证书对客户端进行身份验证。 没有定义该标头的标准,不同的实现方式可能为此有所不同,请检查目标服务器是否支持。
默认情况下,服务器对传入客户端证书进行最小验证。 应在代理或目标中验证证书,有关详细信息,请参阅 客户端证书身份验证 文档。
仅当连接上已存在客户端证书时,此转换才适用。
Key | Memo |
---|---|
ClientCert | 标头名称 |
示例:
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"ClientCert": "X-Client-Cert"
}
]
}
}
}
}
响应转换
默认情况下,所有响应标头和尾部都从代理响应复制到传出客户端响应。 响应和响应尾部转换可以指定它们是只应用于成功的响应还是应用于所有响应。
ResponseHeadersCopy
此设置用于确定是否将所有代理响应标头复制到客户端响应。 此设置默认处于启用状态,可以通过配置变换为 false 值来禁用此设置。 即使禁用了此功能,引用特定标头的转换仍将运行。
Key | Memo |
---|---|
ResponseHeadersCopy | true/false |
示例:
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"ResponseHeadersCopy": "false"
}
]
}
}
}
}
ResponseHeader
这会设置或追加命名响应标头的值。 Set 替换任何现有标头。 Append 会添加具有给定值的额外标头。 注意:不建议将“”设置为标头值,并可能导致未定义的行为。
When 指定是否应在所有响应、成功响应或失败响应中包含响应头。 任何状态代码小于 400 的响应都被视为成功。
Key | Memo |
---|---|
ResponseHeader | 标头名称 |
Set/Append | 标头值 |
When | Success/Failure/Always |
示例:
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"ResponseHeader": "HeaderName",
"Append": "value",
"When": "Success"
}
]
}
}
}
}
ResponseHeaderRemove
这会删除命名的响应标头。
When 指定是否应针对所有响应、成功或失败响应删除响应标头。 任何状态代码小于 400 的响应都被视为成功。
Key | Memo |
---|---|
ResponseHeaderRemove | 标头名称 |
When | Success/Failure/Always |
示例:
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"ResponseHeaderRemove": "HeaderName",
"When": "Success"
}
]
}
}
}
}
ResponseHeadersAllowed
默认从代理响应复制大多数响应标头(请参阅 ResponseHeadersCopy)。 某些安全模型仅允许代理特定标头。 此转换将禁用 ResponseHeadersCopy,并且仅复制给定标头。 如果未包含在允许列表中,则修改或追加到现有标头的其他转换可能会受到影响。
请注意,默认情况下,某些标头不会复制,因为它们特定于连接或其他安全敏感(例如Connection)。 Alt-Svc 将这些标头名称放在允许列表中将绕过该限制,但强烈建议不要这样做,因为它可能会对代理的功能产生负面影响或导致安全漏洞。
Key | Memo |
---|---|
ResponseHeadersAllowed | 以分号分隔的允许标头名称列表。 |
示例:
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"ResponseHeadersAllowed": "Header1;header2"
}
]
}
}
}
}
ResponseTrailersCopy
此选项设置是否将所有代理响应尾部复制到客户端响应。 此设置默认处于启用状态,可以通过配置变换为 false 值来禁用此设置。 即使禁用了此功能,引用特定标头的转换仍将运行。
Key | Memo |
---|---|
ResponseTrailersCopy | true/false |
示例:
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"ResponseTrailersCopy": "Header1;header2"
}
]
}
}
}
}
ResponseTrailer
添加或替换尾随响应标头
响应尾部是在响应正文末尾发送的标头。 对预告片的支持在 HTTP/1.1 实现中并不常见,但在 HTTP/2 实现中很常见。 检查客户端和服务器是否支持。
Key | Memo |
---|---|
ResponseTrailer | 标头名称 |
Set/Append | 标头值 |
When | Success/Failure/Always |
示例:
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"ResponseTrailer": "HeaderName",
"Append": "value",
"When": "Success"
}
]
}
}
}
}
ResponseTrailerRemove
删除尾随响应标头
When 指定是否应针对所有响应、成功或失败响应删除响应标头。 任何状态代码小于 400 的响应都被视为成功。
Key | Memo |
---|---|
ResponseTrailerRemove | 标头名称 |
When | Success/Failure/Always |
示例:
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"ResponseTrailerRemove": "HeaderName",
"When": "Success"
}
]
}
}
}
}
ResponseTrailersAllowed
默认从代理响应复制大多数响应预告片(请参阅 ResponseTrailersCopy)。 某些安全模型仅允许代理特定标头。 此转换将禁用 ResponseTrailersCopy,并且仅复制给定标头。 如果未包含在允许列表中,则修改或追加到现有标头的其他转换可能会受到影响。
请注意,默认情况下,某些标头不会复制,因为它们特定于连接或其他安全敏感(例如Connection)。 Alt-Svc 将这些标头名称放在允许列表中将绕过该限制,但强烈建议不要这样做,因为它可能会对代理的功能产生负面影响或导致安全漏洞。
Key | Memo |
---|---|
ResponseTrailersAllowed | 以分号分隔的允许标头名称列表。 |
示例:
{
"ReverseProxy": {
"Routes": {
"xxxRoute": {
"Transforms": [
{
"ResponseTrailersAllowed": "Header1;header2"
}
]
}
}
}
}