httpclient与hertzclient在处理Host header时的差别
本文中的Host header、request.header.host、req.Header[“Host”]都指的是请求头中的“Host”字段。
问题提出
在请求转发的过程中,需要从原请求中拿到一些信息来构造新的转发出去的请求,其中就有Host header的问题。一开始我使用的是httpclient,复制请求头的逻辑就是清除一些字段后直接全部复制过去(Host header不在清除范围内),此时程序正常运行。
c.Request.Header.VisitAll(func(key, value []byte) {req.Header.Add(string(key), string(value))})
当我将httpclient切换成hertzclient后,发现一模一样的复制请求头的逻辑遇到了404的错误。经过一番排查发现是httpclient和hertzclient两者对于request.header.host的处理方式不同。对于hertzclient来说,正确的方式应该是不复制请求头中的Host字段。
c.Request.Header.VisitAll(func(key, value []byte) {if string(key) != "Host" { // hertz框架中的client不会更改header.Host,所以不能用原来的Hostreq.Header.Add(string(key), string(value))}})
httpclient
httpclient请求最终发送的 Host header 并不是取自 req.Header[“Host”],而是由 req.Host 字段决定,如果 req.Host 为空,则默认为 req.URL.Host。
所以说虽然我一开始直接把原始的、错误的 Host header 复制了过去,但是httpclient对请求的 Host header 进行了替换,没有用我设置的 Host header ,所以并没有出现问题。
hertzclient
hertzclient最后发送的 Host header 就是取自 req.Header[“Host”] 的,所以说当我将原来的 request.header.host 写到新请求中后,hertzclient发送请求直接使用的是我设置的 Host header ,所以出现404的问题。
如果说依然使用完整复制的方法,在最后需要用req.Header.SetHost()
替换原始的、错误的 Host header 。注意不是req.SetHost()
,这个方法是设置建立连接的主机地址。
Host header作用
那话说回来,为什么使用错误的Host header会出现404的问题呢?Host header究竟是起到什么样的作用呢?
当一个网络请求发出去之后,网址会经过DNS解析为ip地址并建立连接,当连接到服务器后,则会用到 Host header 进行虚拟主机分流。
而虚拟主机(Virtual Host)是一种服务器技术,它允许在同一台物理服务器(同一个IP地址)上运行多个网站。换句话说,你可以用一台服务器托管多个不同域名的网站,而不需要每个网站都分配一个独立的服务器。区分用户到底访问的是哪个网站的方式就是使用 Host header 进行分流。
总体来说,一个请求的流程是:域名 → DNS 解析 → IP 地址 → 建立 TCP/SSL 连接 → 发送 HTTP 请求头 (Host) → 虚拟主机分流。
这里举一个Nginx配置规则进行分流的例子:
server {listen 80;server_name www.example.com;root /var/www/example;
}server {listen 80;server_name www.test.com;root /var/www/test;
}
listen 80;
→ 表示监听 HTTP 默认端口 80。server_name
→ 表示这个虚拟主机匹配的域名。root
→ 表示网站的根目录,也就是请求/
时返回的文件位置。
Nginx 的分流步骤可以总结为:
- 根据端口匹配 server 块
- 这里两个 server 都监听 80,所以都在候选列表中。
- 根据
Host
匹配 server_name- Nginx 会查找与请求
Host
最匹配的 server 块。
- Nginx 会查找与请求
- 匹配成功 → 使用该 server 块的 root 和配置处理请求
- 没有匹配 → 使用默认 server
- Nginx 会选择
listen
指令中第一个或default_server
标记的 server 作为默认返回。
- Nginx 会选择