反向代理测试
由于我对计算机网络、反向代理等web技术学的不深,理解不够透彻,对于一些概念、架构还是一知半解,云里雾里,故这里进行一些Nginx反向代理实验测试,加深对反向代理的理解。
测试环境:就是这个网站,服务器的不同端口上部署了不同的web服务,通过Nginx反向代理实现域名/子页面访问不同的端口上的服务,而不是域名:端口号。
完整的 Nginx 配置通常包含两个 server 块:一个是监听 80 端口的 HTTP 跳转配置,另一个是监听 443 端口的 HTTPS 主配置。HTTP 配置负责将所有流量重定向到 HTTPS,HTTPS 配置负责处理实际的网站请求并配置 SSL 证书。
在备份了原始可用的nginx.conf文件后,开始下面的测试。
1. http重定向到https
配置文件中:
server {
listen 80;
server_name 域名 *.域名;
return 301 https://$host$request_uri;
}
这是一个 HTTP 到 HTTPS 的强制跳转配置,用于将所有 HTTP 流量自动重定向到 HTTPS。listen 80 这一行表示监听 80 端口,也就是 HTTP 的默认端口。当用户通过 http:// 访问网站时,这个配置块会响应请求。
server_name 域名 .域名 这一行定义了哪些域名会被这个配置块处理。其中”域名”是你的主域名,比如 example.com;.域名 表示所有一级子域名,比如 www.example.com、blog.example.com、api.example.com 等都会被匹配到。需要注意的是,*.example.com 只能匹配一级子域名,不能匹配多级子域名比如 a.b.example.com。
return 301 https://$host$request_uri 这一行是核心,执行 301 永久重定向。301 状态码告诉浏览器和搜索引擎这是一个永久性的跳转。$host 变量表示请求中的主机名,比如用户访问 www.example.com,$host 的值就是 www.example.com。$request_uri 变量表示完整的请求路径和参数,比如用户访问 /path/page?query=1,这个部分会被完整保留。
举个例子,如果用户访问 http://www.example.com/blog/post?id=123,经过这个配置后会被 301 重定向到 https://www.example.com/blog/post?id=123,域名、路径和参数都完整保留。
为什么要用 301 而不是 302?301 是永久重定向,搜索引擎会将原 URL 的权重转移到新 URL 上,有利于 SEO。302 是临时重定向,搜索引擎不会转移权重。
以下是网站跳转情况:
如果输入http://域名,则会跳转一次,301重定向为https://域名,由80端口改为访问443端口:



如果输入http://www.域名,则会跳转两次,首先从http跳转为https,然后从子域名www.域名跳转为域名,最终变成了https://域名:



2. 网站请求重定向
配置 HTTPS 反向代理,用于将同一个域名的不同路径代理到后端不同的容器服务。
server {
listen 443 ssl http2;
server_name 域名 *.域名;
ssl_certificate /path to certification.pem;
ssl_certificate_key /path to key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# WordPress - 必须使用 ^~ 确保优先匹配
location ^~ / {
proxy_pass http://域名:端口/;
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;
}
}
以wordpress的重定向为例。
listen 443 ssl http2 这一行表示监听 443 端口,也就是 HTTPS 的默认端口。ssl 表示启用 SSL/TLS 加密,http2 表示启用 HTTP/2 协议,相比 HTTP/1.1 有更好的性能和多路复用能力。
server_name 这一行定义了这个配置块处理的域名。主域名和所有一级子域名都会被这个配置处理。
ssl_certificate 和`ssl_certificate_key`这一行指定了 SSL 证书文件的路径和私钥文件的路径。这是由 Let’s Encrypt 生成的完整证书链,包含服务器证书和中间证书。私钥用于解密客户端发送的加密数据。
ssl_protocols TLSv1.2 TLSv1.3 这一行限制了只允许使用 TLSv1.2 和 TLSv1.3 协议。这两个是目前最安全的 TLS 版本,禁用了老旧且不安全的 SSLv3、TLSv1.0 和 TLSv1.1。
ssl_ciphers HIGH:!aNULL:!MD5 这一行配置了加密套件。HIGH 表示只使用高强度的加密算法,!aNULL 表示禁用匿名认证(没有证书验证),!MD5 表示禁用 MD5 哈希算法(已被证明不安全)。
接下来是第一个 location 块,用于代理 WordPress:
location ^~ / 这个匹配规则表示匹配根路径及其所有子路径。^~ 是一个修饰符,表示如果这个前缀匹配成功,就不再检查其他正则表达式 location 块,直接使用这个配置。这确保了根路径的优先级最高。
proxy_pass http://域名:端口/ 这一行是核心,将请求转发到后端的 WordPress 容器。后端服务运行在宿主机的某端口,使用 HTTP 协议。注意 proxy_pass 末尾的斜杠 / 很重要,它表示将 location 匹配的部分从请求路径中去掉。比如访问 /blog,实际转发到后端的是 /blog 而不是 //blog。
proxy_set_header Host $host 这一行将原始请求的 Host 头传递给后端。WordPress 需要知道原始域名来生成正确的链接和资源路径。
proxy_set_header X-Real-IP $remote_addr 这一行将客户端的真实 IP 地址传递给后端。$remote_addr 是直接连接到 Nginx 的客户端 IP。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for 这一行用于传递客户端的 IP 链。如果请求经过了多个代理,这个头会包含所有代理的 IP 地址链。
proxy_set_header X-Forwarded-Proto $scheme 这一行告诉后端原始请求使用的是什么协议。因为 Nginx 终止了 HTTPS,后端收到的是 HTTP 请求,这个头告诉后端原始请求是 HTTPS,这样 WordPress 才能正确生成 HTTPS 链接。
整个配置的工作流程是这样的:当用户访问 https://域名/ 时,请求被/路径的 location 块匹配,Nginx 将请求转发到 http://路径:端口/,也就是 WordPress 容器。这种配置方式的好处是可以用同一个域名托管多个不同的应用,通过路径来区分。用户只需要记住一个域名,Nginx 在后端自动将请求路由到正确的服务。同时,Nginx 统一处理了 SSL 证书、HTTP/2、安全配置等,后端服务可以专注于业务逻辑,不需要关心这些基础设施问题。
那么,如果我们将proxy_pass后面的地址改为https会发生什么呢?
答:会502 Bad Gateway。查看nginx错误日志:SSL_do_handshake() failed。
这是因为网站容器监听的是http服务,而反向代理配置的是https转发,Nginx 试图用 HTTPS 协议连接后端服务,但后端服务实际上提供的是 HTTP 服务。SSL 协议在解析后端返回的数据时失败了,因为后端返回的是明文的 HTTP 响应,而不是加密的 SSL 数据。
因此proxy_pass后面的地址不能随便写,要根据容器的实际情况写http或者https等。
3. 总结
Nginx从http和https两路监听入手:
监听到了http时,判断是否是目标域名,如果是,则重定向为https。
监听到了https时,判断是否是目标域名,如果是,nginx先与客户端进行SSL/TLS 握手,验证证书,建立加密连接,然后根据解析出来的域名后面的路径,匹配 location 规则,进行路径重写,生成新的请求url,转发请求到后端容器,建立nginx与后端容器的连接,这个连接根据容器自身属性可能是http也可能是https。后端容器处理完请求后,返回 HTTP 响应给nginx,nginx通过已建立的 HTTPS 连接将响应加密后发送给用户,实现反向代理。整个过程中,用户只知道访问了 https://域名/子页面/,完全不知道后端是 Docker 容器在某个端口提供服务。Nginx 作为反向代理,隐藏了后端架构,统一处理了 SSL 证书、HTTP/2、安全策略等基础设施问题。
请注意,在我的网站中,客户端(用户的浏览器)与 Nginx 之间建立的是 HTTPS 加密连接。用户访问的是 https://域名,Nginx 负责处理 SSL 证书验证、加密解密等工作。然后,Nginx 与后端容器之间建立的是 HTTP 明文连接。Nginx 将解密后的请求转发给后端容器(IP:端口),后端容器只需要处理普通的 HTTP 请求,不需要配置任何 SSL 证书。这种架构叫做 “SSL 终止” 或 “TLS 终止”。Nginx 作为反向代理,在自己这一层就终止了 SSL 加密,后端服务完全不需要关心加密问题。