http header及nginx的header转发问题

前言

       最近在项目http请求中使用header频次颇多,于是总结一下header的用法及遇到的一些坑。

常见请求体公共header

        以下为常见的请求发起方header。

名称示例说明
Hostwww.xxx.com本次http请求的host地址
Content-Typeapplication/jsonpost、put请求body数据格式
Accept-Languagezh-CN接收数据的内容语言列表
User-AgentMozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/70.0请求发起方的用户信息 浏览器或设备版本信息,可以通过这个取出设备及版本信息,做版本兼容或设备区分
Refererhttps://www.baidu.com/s?wd=header多用于web网站中表示请求发起之前的页面地址,即引导至这个页面的上一个页面地址。可以基于这个做百度、谷歌等搜索引擎到当前网页的关键词分析。
cookiesensorsdata2015jssdkcross=%7B%2这个域名下之前存在的cookie数据
Connectionkeep-alive是否需要持久化链接
DateDec, 26 Dec 2019 17:30:00 GMT请求发起时间点

       除了上面的请求体中的公共header,我们还可以定义一些其他header:如token、移动端的唯一设备id、一些自己业务的基础属性如用户id、城市id等等。

  1. 基于此可以根据设备、用户、点击链路做出用户行为分析,优化当前的产品,甚至是用在广告的个性化推荐上;
  2. 服务端可以基于这些基本信息对每个行为做日志记录;

nginx、apache服务器对header下划线过滤

问题出现及定位

       上面说的一些header的用法及常见的公共header,但在上周的业务使用中却出现了一些问题,客户端通过header传递了一个key为a_b的header,但是我们服务端却在getHeader中拿到的是null,其他的header正常,在检查了拼写问题及使用Charles工具抓包后,确认客户端传递了这个a_b的header,但没有传递到服务端,而且我们的一些过滤器也没有针对header做特殊处理。

       因此想到了是不是代理服务器的问题,我们每个服务端都会使用nginx做一层代理转发,而查询相关资料后发现nginx服务器默认会对http请求中带下划线的header做过滤丢失不会透传,需要在配置中打开这个选项即可,如下:

nginx.conf
http
	{
        underscores_in_headers on;
    }

原因

        为什么会使nginx和apache服务器对名称中带下划线header不做转发呢,查阅相关资料后发现是CGI(公共网关接口Common Gateway Interface,CGI是Web 服务器运行时外部程序的规范)的历史遗留问题如下,大概意思就是 下划线和中划线都为会被映射为 CGI 系统变量中名中的下划线,这样容易引起混淆。

If you do not explicitly set underscores_in_headers on ;, nginx will silently drop HTTP headers with underscores (which are perfectly valid according to the HTTP standard). This is done in order to prevent ambiguities when mapping headers to CGI variables, as both dashes and underscores are mapped to underscores during that process.

        因此我们在以后的header使用中,需要客户端和浏览器制定更规范的header名称,尽量避免使用带下划线的内容。

结语

        之前开发周期比较紧,除了鉴权及ua之外很少关注header中的内容,这次整理之后发现我们当前cs交互中有很多冗余的业务数据既在header中传递又在接口中传递,能够使用它做一些更多的事情。

参考资料:https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#missing–28disappearing-29-http-headers
https://www.jianshu.com/p/2320f1ece1a2

<think>我们正在处理用户的问题:如何配置Nginx以根据特定Header的值转发请求?用户引用了多个配置片段,其中引用[2]展示了如何根据一个特定的Headerhttp_x_lsc_version)来设置另一个Header(X-Login-Type),但并没有直接根据Header转发到不同的上游服务器。然而,用户的问题明确要求:“配置nginx转发包含特定header的请求”和“根据特定header转发请求”。根据Nginx的配置能力,我们可以通过多种方式实现根据Header转发请求:1.使用`if`指令结合`proxy_pass`(注意:官方文档建议谨慎使用`if`,因为它在某些上下文中可能有副作用)2.使用`map`指令将请求头映射到一个变量,然后根据该变量选择不同的`proxy_pass`3.使用`split_clients`模块进行请求的分流(适用于A/B测试等,但这里不适用)4.使用`nginx-plus`的商业版本提供的更高级的路由功能(但用户可能使用的是开源版本)这里我们重点介绍前两种方法。注意:用户需求是“转发包含特定header的请求”,可以理解为:a)当请求中包含某个Header(无论值是什么)时,就转发到特定的上游。b)或者根据该Header的特定值,转发到不同的上游。从用户引用[2]来看,他们更可能是根据Header的值进行转发,因为示例中根据`http_x_lsc_version`的值来设置另一个Header。因此,我们提供两种常见场景的配置:场景1:根据Header的存在与否进行转发(例如,请求中带有`X-API-Version`头就转发到新版本后端)场景2:根据Header的具体值进行转发(例如,`X-API-Version:1.0`转发到后端A,`X-API-Version:2.0`转发到后端B)由于用户引用的配置中使用了`if`,我们也使用`if`来演示,但会给出注意事项。另外,我们也可以使用`map`方式,这种方式更灵活且没有`if`的副作用。下面分别给出示例:方法一:使用`if`指令(注意:`if`在location块中使用时,可能会导致一些预期外的行为,比如重写模块的一些指令在if中不按预期工作,因此要谨慎)示例配置(场景1:根据Header的存在):```server{listen80;server_name_;#默认的上游服务器set$upstream_server"http://default_backend";#如果请求头中包含X-Special-Header,则转发到特殊的上游if($http_x_special_header){set$upstream_server"http://special_backend";}location/{proxy_pass$upstream_server;#其他proxy_set_header配置...proxy_set_headerHost$host;proxy_set_headerX-Real-IP$remote_addr;proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;}}```示例配置(场景2:根据Header的值):```server{listen80;server_name_;#默认的上游服务器set$upstream_server"http://default_backend";#如果请求头X-API-Version的值是1.0,则转发到后端v1if($http_x_api_version="1.0"){set$upstream_server"http://backend_v1";}#如果请求头X-API-Version的值是2.0,则转发到后端v2if($http_x_api_version="2.0"){set$upstream_server"http://backend_v2";}location/{proxy_pass$upstream_server;#其他proxy_set_header配置...}}```但是,官方建议尽量避免在location上下文中使用if,因为它的行为可能不像预期的那样。所以,我们更推荐使用map指令。方法二:使用`map`指令(更安全,推荐)在http块中定义map,将请求头的值映射到自定义变量,然后在location块中使用该变量来决定proxy_pass。示例配置(场景1:根据Header的存在):```http{#定义一个map,检查X-Special-Header是否存在map$http_x_special_header$upstream_server{default"http://default_backend";#如果X-Special-Header存在且不为空,则使用特殊后端~*".+""http://special_backend";}server{listen80;server_name_;location/{proxy_pass$upstream_server;#其他proxy_set_header配置...proxy_set_headerHost$host;proxy_set_headerX-Real-IP$remote_addr;proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;}}}```示例配置(场景2:根据Header的值):```http{#根据X-API-Version的值映射不同的上游map$http_x_api_version$upstream_server{default"http://default_backend";"1.0""http://backend_v1";"2.0""http://backend_v2";}server{listen80;server_name_;location/{proxy_pass$upstream_server;#其他proxy_set_header配置...}}}```注意:`map`块必须放在http块内。用户引用[3]中展示了重写URL并转发,引用[4]展示了HTTPS转发的配置。如果用户需要同时处理HTTPS,只需在server块中配置ssl相关参数即可。根据用户需求,我们重点展示根据特定Header转发的配置。这里我们采用更推荐的map方式。因此,我们可以给出一个完整的示例,假设用户要求:-当Header`X-Route`的值为`internal`时,转发到内部服务器`http://10.0.0.1:8080`-当Header`X-Route`的值为`external`时,转发到外部服务器`http://api.example.com`-其他情况转发到默认服务器`http://default.example.com`配置如下:```http{#定义map,根据X-Route头映射上游map$http_x_route$upstream{default"http://default.example.com";"internal""http://10.0.0.1:8080";"external""http://api.example.com";}server{listen80;#如果使用HTTPS,请参考引用[4]配置ssl#listen443ssl;#ssl_certificate...;#ssl_certificate_key...;server_nameyour_domain.com;location/{proxy_pass$upstream;proxy_set_headerHost$host;proxy_set_headerX-Real-IP$remote_addr;proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;#如果需要,可以像引用[2]那样设置其他Header}}}```注意:Header名称在Nginx变量中需要转换为小写,横线变为下划线,并加上`http_`前缀。例如,`X-Route`对应变量`$http_x_route`。最后,提醒用户:在修改配置后,使用`nginx-t`测试配置,然后`nginx-sreload`重新加载配置。如果用户的需求更复杂,例如需要组合多个Header条件,可能需要使用更复杂的map或使用多个变量,或者考虑使用OpenResty的Lua脚本。根据用户引用的内容,我们给出了基于Header转发的配置,并推荐了map方式。相关问题:1.如何配置Nginx根据多个Header条件进行请求转发?2.Nginx中使用`if`指令有哪些注意事项?3.如何调试Nginx的map变量映射结果?4.Nginx如何同时支持HTTPHTTPS的请求转发?5.在Nginx中如何安全地传递用户自定义Header到上游服务器?</think>### Nginx 根据特定 Header 转发请求配置指南 以下是完整的配置方法,支持根据特定 Header 的值进行请求转发,结合引用内容的最佳实践: #### 基础配置模板 ```nginx http { # 定义Header值到上游服务器的映射 map $http_x_custom_header $backend_server { default http://default-backend:8080; # 默认后端 "value1" http://backend-a:9000; # Header值=value1时 "value2" http://backend-b:9000; # Header值=value2时 } server { listen 80; server_name your-domain.com; location / { # 根据Header值动态选择上游服务器 proxy_pass $backend_server; # 标准Header传递配置(引用[1][4]) proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 传递原始Header(引用[2]) proxy_set_header X-Custom-Header $http_x_custom_header; } } } ``` #### 关键配置说明 1. **Header值映射**(`map`指令) - `$http_x_custom_header`:要检测的Header名称(实际使用时替换`x_custom_header`) - `$backend_server`:生成的映射变量 - 支持正则匹配(引用[2]): ```nginx map $http_x_api_version $backend_server { "~*v1" http://legacy-backend:8080; "~*v2" http://modern-backend:8080; default http://default-backend:8080; } ``` 2. **多条件处理**(引用[2]) ```nginx location / { set $target_backend "http://default-backend:8080"; # 条件1: Header存在且值为特定内容 if ($http_x_env = "production") { set $target_backend "http://prod-backend:9000"; } # 条件2: 另一个Header的特定值 if ($http_x_debug = "true") { set $target_backend "http://debug-backend:9999"; } proxy_pass $target_backend; } ``` 3. **URL重写+Header转发**(引用[3]) ```nginx location /api/ { # 根据Header重写路径 if ($http_x_api_version = "v2") { rewrite ^/api/(.*)$ /v2/$1 break; } proxy_pass http://backend-server; proxy_set_header X-API-Version $http_x_api_version; } ``` 4. **HTTPS转发**(引用[4]) ```nginx server { listen 443 ssl; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; location / { proxy_pass https://$backend_server; # 注意https://前缀 proxy_ssl_verify off; # 如需忽略上游证书验证 } } ``` #### 最佳实践 1. **Header名称规范** - 在Nginx变量中使用小写+下划线格式:`$http_<header_name>` - 例如 `X-Auth-Token` → `$http_x_auth_token` 2. **安全增强** ```nginx proxy_set_header X-Forwarded-Host $host; proxy_http_version 1.1; proxy_set_header Connection ""; ``` 3. **调试技巧** ```nginx # 在log_format中添加Header记录 log_format custom '$remote_addr - $http_x_custom_header'; access_log /var/log/nginx/access.log custom; ``` > 测试命令: > ```bash > curl -H "X-Custom-Header: value1" http://your-domain.com > ``` #### 常见问题排查 1. **Header未传递**: - 检查Header名称大小写(Nginx自动转换为小写) - 添加`underscores_in_headers on;`解决带下划线Header问题 2. **映射未生效**: - 确保`map`块位于`http`上下文中 - 使用`nginx -T`检查完整配置 3. **HTTPS证书错误**: - 上游使用自签名证书时添加: ```nginx proxy_ssl_trusted_certificate /path/to/ca.crt; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值