使用应用程序网关重写 HTTP 标头和 URL
可以通过应用程序网关重写请求和响应的所选内容。 使用此功能,你可以转换 URL、查询字符串参数并修改请求标头和响应标头。 还可以通过它来添加条件,确保只有在满足特定条件的情况下才能重写 URL 或指定的标头。 这些条件基于请求和响应信息。
HTTP 标头和 URL 重写功能仅适用于应用程序网关 v2 SKU。
请求和响应标头
当请求和响应数据包在客户端与后端池之间移动时,可以通过应用程序网关添加、移除或更新 HTTP 请求和响应标头。 HTTP 标头可让客户端和服务器连同请求或响应一起传递附加的信息。 重写这些标头可以完成重要的任务,例如,添加安全相关的标头字段(如 HSTS/ X-XSS-Protection)、删除可能透露敏感信息的响应标头字段,以及从 X-Forwarded-For 标头中删除端口信息。
可以重写请求和响应中的所有标头,但 Connection
和 Upgrade
标头除外。 还可以使用应用程序网关创建自定义标头,并将其添加到通过该网关路由的请求和响应。 若要了解如何使用 Azure 门户在应用程序网关中重写请求标头和响应标头,请参阅此文。
URL 路径和查询字符串
利用应用程序网关中的 URL 重写功能,你可以:
重写请求 URL 的主机名、路径和查询字符串
选择重写侦听器上所有请求的 URL,或者只重写与所设置的一个或多个条件匹配的要求的 URL。 这些条件基于请求属性(请求头和服务器变量)。
选择基于原始 URL 或重写的 URL 来路由请求(选择后端池)
若要了解如何使用 Azure 门户在应用程序网关中重写 URL,请参阅此文。
了解应用程序网关中的重写
重写集是路由规则、条件和操作的集合。
请求路由规则关联:重写配置将通过其路由规则关联到源侦听器。 使用“基本”类型的路由规则时,重写配置将与其侦听器相关联,并以全局重写的形式运行。 使用基于路径的路由规则时,将根据 URL 路径映射定义重写配置。 在后一种情况下,该规则只会应用于站点的特定路径区域。 可以将重写集应用于多个路由规则,但一个路由规则只能与一个重写相关联。
重写条件:这是一个可选配置。 根据定义的条件,应用程序网关将评估 HTTP(S) 请求和响应的内容。 如果 HTTP(S) 请求或响应与重写条件匹配,则会发生后续的“重写操作”。 如果将多个条件关联到一个操作,仅当满足所有条件时,才会发生该操作。 换句话说,它是一个逻辑 AND 运算。 可以使用重写条件评估 HTTP 请求和响应的内容。 此可选配置允许仅在满足一个或多个条件时执行重写。 应用程序网关使用以下类型的变量来评估请求和响应的内容:
可以选择以下类型来查找条件:
- HTTP 标头(请求和响应)
- 支持的服务器变量
使用条件可以评估指定的标头或变量是否存在,为此可以通过文本或正则表达式模式对其值进行匹配。 对于高级重写配置,还可以捕获标头或服务器变量的值,供稍后在重写操作中使用。 了解有关模式和捕获的详细信息。
重写操作:使用重写操作集可以重写标头(请求或响应)或 URL 组件。
操作可以使用以下值类型或其组合:
- Text。
- 请求标头的值 - 要使用捕获的请求标头值,请将语法指定为
{http_req_headerName}
。 - 响应标头的值 - 要使用从前面的条件中捕获的响应标头值,请将语法指定为
{http_resp_headerName}
。 从操作集的“Set-Cookie”响应标头中捕获值时,可以使用{capt_header_value_matcher}
。 了解有关操作集下的捕获的详细信息。 - 服务器变量 - 要使用服务器变量,请将语法指定为
{var_serverVariable}
。 支持的服务器变量列表。
使用操作重写 URL 时,支持以下操作:
- URL 路径:要设置为路径的新值。
- URL 查询字符串:必须将查询字符串重写到的新值。
- 重新评估路径映射:指定重写后是否必须重新评估 URL 路径映射。 如果保持未选中状态,则将使用原始 URL 路径来匹配 URL 路径映射中的路径模式。 如果设置为 true,则会重新评估 URL 路径映射,以检查它是否与重写的路径匹配。 启用此开关有助于在重写后将请求路由到不同的后端池。
模式匹配和捕获
支持在条件和操作下使用模式匹配和捕获(在操作下,仅支持对特定的标头使用)。
模式匹配
应用程序网关使用正则表达式进行模式匹配。 编写模式匹配语法时,应使用与正则表达式 2 (RE2) 兼容的表达式。
可以在条件和操作下使用模式匹配。
- 条件:用于匹配标头或服务器变量的值。 若要匹配“条件”下的模式,请使用“pattern”属性。
- 操作:操作集下的模式匹配仅适用于响应标头“Set-Cookie”。 若要匹配操作下的 Set-Cookie 模式,请使用“HeaderValueMatcher”属性。 如果已捕获,其值可以用作 {capt_header_value_matcher}。 由于可能存在多个 Set-Cookie,此处的模式匹配允许查找特定的 Cookie。 示例:对于特定版本的 user-agent,你想要使用 max-age=3600(一小时)重写“cookie2”的 set-cookie 响应标头。 在本例中,可以使用
- 条件 - 类型:请求标头,标头名称:user-agent,要匹配的模式:*2.0
- 操作 - 重写类型:响应标头,操作类型:设置,标头名称:Set-Cookie,标头值匹配器:cookie2=(.*),标头值:cookie2={capt_header_value_matcher_1};Max-Age=3600
注意
如果使用核心规则集 3.1 或更低版本来运行应用程序网关 Web 应用程序防火墙 (WAF),则在使用 Perl 兼容正则表达式 (PCRE) 时(此时也在执行先行和后行(负或正)断言)可能会遇到问题。
捕获语法
模式还可用于捕获子字符串供稍后使用。 请在正则表达式定义中的子模式两侧加上括号。 第一对括号将其子字符串存储在 1 中,第二对则将其子字符串存储在 2 中,依此类推。 你可以根据需要使用任意多个括号;Perl 只需定义更多的编号变量供你用来表示这些捕获的字符串。 可以在此 Perl 编程指南中找到一些示例。
- (\d)(\d) # 匹配两个数字,将它们捕获到第 1 组和第 2 组中
- (\d+) # 匹配一个或多个数字,将它们全部捕获到第 1 组中
- (\d)+ #与一个数字匹配一次或多次,将最后一个捕获到第 1 组中
捕获后,可以使用以下格式在操作集值中使用它们:
- 对于请求标头捕获,必须使用 {http_req_headerName_groupNumber}。 例如,{http_req_User-Agent_1} 或 {http_req_User-Agent_2}
- 对于响应标头捕获,必须使用 {http_resp_headerName_groupNumber}。 例如 {http_resp_Location_1} 或 {http_resp_Location_2}。 对于通过“HeaderValueMatcher”属性捕获的响应标头 Set-Cookie,必须使用 {capt_header_value_matcher_groupNumber}。 例如 {capt_header_value_matcher_1} 或 {capt_header_value_matcher_2}。
- 对于服务器变量,必须使用 {var_serverVariableName_groupNumber}。 例如,{var_uri_path_1} 或 {var_uri_path_2}
注意
- 不应在模式中指定使用 / 作为模式的前缀和后缀来匹配值。 例如,(\d)(\d) 将匹配两个数字。 /(\d)(\d)/ 不会匹配两个数字。
- 条件变量的大小写需要与捕获变量的大小写匹配。 例如,如果条件变量是 User-Agent,则捕获变量必须为 User-Agent(即 {http_req_User-Agent_2})。 如果条件变量定义为 user-agent,则捕获变量必须为 user-agent(即 {http_req_user-agent_2})。
- 如果要使用整个值,则不应提到编号。 直接使用 {http_req_headerName} 之类的格式,不带 groupNumber。
服务器变量
应用程序网关使用服务器变量来存储有关服务器、与客户端建立的连接以及对连接的当前请求的有用信息。 例如,存储的信息包括客户端的 IP 地址和 Web 浏览器类型。 服务器变量会动态更改,例如,加载新页或发布表单时就会更改。 可以使用这些变量来评估重写条件和重写标头。 若要使用服务器变量的值重写标头,需要在语法 {var_serverVariableName} 中指定这些变量
应用程序网关支持以下服务器变量:
变量名称 | 说明 |
---|---|
add_x_forwarded_for_proxy | X-Forwarded-For 客户端请求标头字段,其中追加了 IP1、IP2、IP3 等格式的 client_ip 变量(请参阅此表后面的解释)。 如果 X-Forwarded-For 字段不在客户端请求标头中,则 add_x_forwarded_for_proxy 变量等于 $client_ip 变量。 若要重写由应用程序网关设置的 X-Forwarded-For 标头,使该标头仅包含 IP 地址而不包含端口信息,则此变量很有用。 |
ciphers_supported | 客户端支持的加密法列表。 |
ciphers_used | 用于已建立 TLS 连接的加密法字符串。 |
client_ip | 客户端的 IP 地址,应用程序网关从中接收请求。 如果应用程序网关和发起方客户端的前面有反向代理,则 client_ip 会返回该反向代理的 IP 地址。 |
client_port | 客户端端口。 |
client_tcp_rtt | 有关客户端 TCP 连接的信息。 在支持 TCP_INFO 套接字选项的系统上可用。 |
client_user | 使用 HTTP 身份验证时提供用于身份验证的用户名。 |
host | 按此优先顺序排列:请求行中的主机名、Host 请求标头字段中的主机名,或与请求匹配的服务器名称。 示例:在请求 http://contoso.com:8080/article.aspx?id=123&title=fabrikam 中,主机值为 contoso.com |
cookie_name | name Cookie。 |
http_method | 用于发出 URL 请求的方法。 例如 GET 或 POST。 |
http_status | 会话状态。 例如 200、400 或 403。 |
http_version | 请求协议。 通常为 HTTP/1.0、HTTP/1.1 或 HTTP/2.0。 |
query_string | 请求的 URL 中“?”后面的变量/值对列表。 示例:在请求 http://contoso.com:8080/article.aspx?id=123&title=fabrikam 中,query_string 值为 id=123&title=fabrikam |
received_bytes | 请求的长度(包括请求行、标头和请求正文)。 |
request_query | 请求行中的参数。 |
request_scheme | 请求方案:http 或 https。 |
request_uri | 完整的原始请求 URI(带参数)。 示例:在请求 http://contoso.com:8080/article.aspx?id=123&title=fabrikam* 中,request_uri 值为 /article.aspx?id=123&title=fabrikam |
sent_bytes | 发送到客户端的字节数。 |
server_port | 接受请求的服务器端口。 |
ssl_connection_protocol | 已建立的 TLS 连接的协议。 |
ssl_enabled | 如果连接在 TLS 模式下建立,则为“On”。 否则为空字符串。 |
uri_path | 标识 Web 客户端要访问的主机中的特定资源。 变量引用任何操作之前的原始 URL 路径。 这是请求 URI 中没有参数的部分。 例如,在请求 http://contoso.com:8080/article.aspx?id=123&title=fabrikam 中,uri_path 值为 /article.aspx 。 |
相互身份验证服务器变量
应用程序网关支持将以下服务器变量用于相互身份验证方案。 使用这些服务器变量的方式与上面用于其他服务器变量的方式相同。
变量名称 | 说明 |
---|---|
client_certificate | 已建立的 SSL 连接的客户端证书,采用 PEM 格式。 |
client_certificate_end_date | 客户端证书的结束日期。 |
client_certificate_fingerprint | 用于已建立 SSL 连接的客户端证书的 SHA1 指纹。 |
client_certificate_issuer | 用于已建立 SSL 连接的客户端证书的“颁发者 DN”字符串。 |
client_certificate_serial | 用于已建立 SSL 连接的客户端证书的序列号。 |
client_certificate_start_date | 客户端证书的开始日期。 |
client_certificate_subject | 用于已建立 SSL 连接的客户端证书的“使用者 DN”字符串。 |
client_certificate_verification | 客户端证书验证结果为:“成功”、“失败:<原因>”或“无”(如果不存在证书)。 |
标头重写的常见方案
从 X-Forwarded-For 标头中删除端口信息
应用程序网关先在所有请求中插入 X-Forwarded-For 标头,然后将请求转发到后端。 此标头是 IP 端口的逗号分隔列表。 在某些情况下,后端服务器只需在标头中包含 IP 地址。 你可以使用标头重写从 X-Forwarded-For 标头中删除端口信息。 若要这样做,一种做法是将该标头设置为 add_x_forwarded_for_proxy 服务器变量。 此外,也可使用变量 client_ip:
修改重定向 URL
在某些情况下,修改重定向 URL 可能非常有用。 例如:客户端最初被重定向到“/blog”这样的路径,但由于内容结构的变化,现在应该被发送到“/updates”。
警告
在进行配置时,有时可能需要修改重定向 URL,由此,应用程序网关被配置为替代面向后端的主机名。 在这种情况下,后端看到的主机名与浏览器看到的主机名不同。 在这种情况下,重定向不会使用正确的主机名。 不建议使用此配置。
在反向代理与其后端 Web 应用程序之间保留原始 HTTP 主机名中介绍了此类配置的限制和影响。 对于应用服务,建议按照使用应用程序网关配置应用服务中的“自定义域(建议)”进行操作。 按以下示例所述重写响应中的 location 标头应被视为一种变通方法,但不能解决根本原因。
当应用服务发送重定向响应时,它会在其响应的位置标头中,使用它从应用程序网关收到的请求中的相同主机名。 因此,客户端会直接向 contoso.chinacloudsites.cn/path2
发出请求,而不是通过应用程序网关 (contoso.com/path2
) 发出请求。 不应该绕过应用程序网关。
将 location 标头中的主机名设置为应用程序网关的域名即可解决此问题。
下面是替换主机名的步骤:
创建一个重写规则,其中的某个条件可以评估响应中的 location 标头是否包含 chinacloudsites.cn。 输入模式
(https?):\/\/.*chinacloudsites\.cn(.*)$
。执行相应的操作来重写 location 标头,使其包含应用程序网关的主机名。 为此,请输入
{http_resp_Location_1}://contoso.com{http_resp_Location_2}
作为标头值。 此外,也可使用服务器变量host
将主机名设置为与原始请求匹配。
实现安全 HTTP 标头以防止漏洞
在应用程序响应中实现必要的标头可以修复多个安全漏洞。 这些安全标头包括 X-XSS-Protection、Strict-Transport-Security 和 Content-Security-Policy。 可以使用应用程序网关为所有响应设置这些标头。
删除不需要的标头
你可能想要从 HTTP 响应中删除透露敏感信息的标头。 例如,你可能想要移除后端服务器名称、操作系统或库详细信息等信息。 可以使用应用程序网关删除以下标头:
无法创建重写规则来删除主机标头。 如果尝试创建重写规则,将该规则的操作类型设置为删除,并将标头设置为主机,则会导致错误。
检查某个标头是否存在
可以在 HTTP 请求或响应标头中评估某个标头或服务器变量是否存在。 如果你希望只有存在特定的标头时执行标头重写,则此评估非常有用。
URL 重写的常见方案
根据参数选择路径
若要完成根据标头的值、URL 的一部分或请求中的查询字符串选择后端池的方案,可以组合使用 URL 重写功能和基于路径的路由。
为此,请创建一个重写集,在其中包含检查特定参数(查询字符串、标头等)的条件然后执行更改 URL 路径的操作(确保启用重新评估路径映射)。 然后,必须将重写集关联到基于路径的规则。 基于路径的规则必须包含重写集中指定的相同 URL 路径及其相应的后端池。
因此,重写集允许用户检查特定参数并为其分配新路径,并且基于路径的规则允许用户将后端池分配到这些路径。 只要启用了“重新评估路径映射”,流量就会根据重写集中指定的路径进行路由。
有关使用查询字符串的用例示例,请参阅在门户中使用基于参数的路径选择来路由流量。
基于 URL 重写查询字符串参数
假设有一个购物网站方案,其中的用户可见链接应简单明了,但后端服务器需要使用查询字符串参数来显示正确的内容。
在这种情况下,应用程序网关可以从 URL 中捕获参数,并从 URL 添加来自这些参数的查询字符串键值对。 例如,假设用户想要将 https://www.contoso.com/fashion/shirts
重写为 https://www.contoso.com/buy.aspx?category=fashion&product=shirts
,则可通过以下 URL 重写配置来实现。
条件 - 如果服务器变量 uri_path
等于模式 /(.+)/(.+)
操作 - 将 URL 路径设置为 buy.aspx
,将查询字符串设置为 category={var_uri_path_1}&product={var_uri_path_2}
有关如何实现上述方案的分步指南,请参阅使用 Azure 门户在应用程序网关中重写 URL
重写配置常见缺陷
不允许对基本请求传递规则启用“重新评估路径映射”。 这是为了防止基本传递规则出现无限评估循环。
需要至少有 1 个条件重写规则或 1 个没有为基于路基的传递规则启用“重新评估路径映射”的重写规则,以防止基于路径的传递规则出现无限评估循环。
如果根据客户端输入动态创建了循环,则传入的请求会终止,并出现 500 错误代码。 在这种情况下,应用程序网关继续为其他请求提供服务,而不进行任何降级。
将 URL 重写或主机标头重写与 Web 应用程序防火墙结合使用 (WAF_v2 SKU)
配置 URL 重写或主机头重写时,WAF 评估会在修改请求头或 URL 参数之后(重写后)发生。 删除应用程序网关上的 URL 重写或主机头重写配置时,WAF 评估将在标头重写之前(重写前)完成。 此顺序可确保将 WAF 规则应用到后端池接收的最终请求。
例如,假设标头 "Accept" : "text/html"
具有以下标头重写规则 - 如果标头 "Accept"
的值等于 "text/html"
,则将该值重写为 "image/png"
。
在此,只配置了标头重写,系统会在 "Accept" : "text/html"
上完成 WAF 评估。 但配置了 URL 重写或主机头重写时,则会在 "Accept" : "image/png"
上完成 WAF 评估。
URL 重写与 URL 重定向
进行 URL 重写时,应用程序网关会在将请求发送到后端之前重写 URL。 这不会更改用户在浏览器中看到的内容,因为用户不会看到这些更改。
进行 URL 重定向时,应用程序网关会将包含新 URL 的重定向响应发送到客户端。 这样一来,客户端就必须将其请求重新发送到重定向中提供的新 URL。 用户在浏览器中看到的 URL 会更新为新的 URL。
限制
- 当应用程序网关配置为重定向请求或显示自定义错误页时,不支持重写。
- 请求标头名称可以包含字母数字字符和连字符。 在请求发送到后端目标时,包含其他字符的标头名称将会被丢弃。
- 响应头名称可以包含任何字母数字字符和 RFC 7230 中定义的特定符号。
- 无法重写 Connection 和 Upgrade 标头
- 直接从应用程序网关生成的 4xx 和 5xx 响应不支持重写