在网站运营和架构设计中,我们时常会遇到需要将访问一个域名的请求,内容实则由另一个域名或服务提供,但希望用户浏览器地址栏的原始域名保持不变的场景,这与常见的 301 或 302 跳转有本质区别,后者会告知浏览器去请求一个新的地址,从而改变 URL,而我们讨论的这种“域名跳转但域名不变”的技术,本质上是一种服务器端的“代理”或“重写”,对外部用户完全透明,Nginx 以其高性能和灵活性,是实现此需求的绝佳工具,本文将深入探讨如何利用 Nginx 实现这一功能,并分析其背后的原理、核心配置及实际应用场景。
理解核心原理:外部重定向与内部代理
要掌握 Nginx 的域名不变跳转,首先必须清晰地区分两种不同的跳转机制。
外部重定向
这是指服务器向浏览器返回一个 3xx 状态码(如 301 Moved Permanently 或 302 Found),并在响应头中携带一个 Location
字段,指明新的地址,浏览器收到这个响应后,会自动放弃当前请求,转而向 Location
头部指定的新地址发起请求,这个过程用户是能感知的,因为地址栏的 URL 会发生改变。
内部代理/重写
这是指 Nginx 在接收到请求后,不返回重定向指令,而是在服务器内部将请求“转发”或“重写”到另一个目标,Nginx 从目标获取响应内容,再由 Nginx 原封不动(或经过处理)地返回给最初的用户,整个过程中,浏览器只与 Nginx 进行了一次交互,它对自己请求的内容实际上由何处提供毫不知情,因此地址栏的 URL 不会发生任何变化。
下表清晰地对比了这两种机制:
特性 | 外部重定向 (301/302) | 内部代理/重写 |
---|---|---|
浏览器行为 | 发起两次请求 | 只发起一次请求 |
地址栏 URL | 会改变为新 URL | 保持原始 URL 不变 |
服务器响应 | 3xx 状态码 + Location 头 | 200 OK 状态码 + 最终内容 |
SEO 影响 | 301 可传递权重,302 不传递 | 权重归于原始 URL |
主要应用 | 域名永久性变更、URL 规范化 | 隐藏后端服务、统一入口、解决跨域 |
使用 proxy_pass
实现反向代理
这是实现“域名不变”最常用、最直接的方法。proxy_pass
指令将请求转发到一个指定的后端服务,该服务可以是另一个域名、一个 IP 地址加端口,或者一个定义好的 upstream 组。
基本配置示例
假设我们希望所有访问 domain-a.com
的请求,其内容都由 domain-b.com
提供,但用户在浏览器中始终看到的是 domain-a.com
。
server { listen 80; server_name domain-a.com; location / { # 核心:将请求代理到 domain-b.com proxy_pass http://domain-b.com; # 推荐设置:传递原始请求的 Host 头信息给后端服务器 # 这对于后端服务器(尤其是虚拟主机)正确识别请求至关重要 proxy_set_header Host $host; # 传递真实客户端 IP 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; } }
proxy_pass
的关键细节
proxy_pass
指令后是否带有 URI(如 ),其行为有重要差异,这是配置时最常见的“陷阱”。
-
不带 URI:
proxy_pass http://domain-b.com;
如果请求 URI 是/path/to/page.html
,Nginx 会将完整的请求 URI(/path/to/page.html
)拼接在proxy_pass
地址后,即向后端请求http://domain-b.com/path/to/page.html
。 -
带 URI:
proxy_pass http://domain-b.com/;
location
匹配到的部分会被proxy_pass
后指定的 URI 替换,请求http://domain-a.com/path/to/page.html
时,location /
匹配到了 ,整个/path/to/page.html
都被替换成了proxy_pass
后的 ,Nginx 实际向后端请求的地址是http://domain-b.com/
,若配置为location /api/ { proxy_pass http://backend.service/; }
,请求/api/users
会被代理到http://backend.service/users
。
路径代理示例
在前后端分离的架构中,这非常有用。www.example.com
负责提供前端静态资源,而所有以 /api
开头的请求都需要代理到后端 API 服务器。
server { listen 80; server_name www.example.com; # 前端静态资源 location / { root /var/www/html; index index.html; } # 后端 API 代理 location /api/ { # 注意末尾的 /,它会让 /api/ 被忽略,直接拼接后续路径 proxy_pass http://api.backend.server/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
通过此配置,当用户访问 www.example.com/api/user/list
时,浏览器 URL 不变,但 Nginx 内部会将请求转发到 http://api.backend.server/user/list
,完美解决了跨域问题,并隐藏了后端服务的真实地址。
使用 rewrite
配合 break
或 last
虽然 proxy_pass
是首选,但在某些需要先对 URI 进行复杂重写再代理的场景下,rewrite
指令也能实现域名不变的效果。
rewrite
指令根据正则表达式重写 URI,其结尾的 flag
决定了后续行为,要实现域名不变,关键是使用 break
或 last
标志,它们会阻止 Nginx 向客户端返回重定向响应。
last
:完成当前location
内的rewrite
规则后,重新对rewrite
后的新 URI 进行location
匹配。break
:完成当前location
内的rewrite
规则后,停止处理所有rewrite
规则,并在当前location
中继续处理其他指令(如proxy_pass
),在代理场景下,break
通常更安全,因为它避免了因location
重新匹配可能导致的意外循环。
配置示例
假设我们想将访问 shop.old-site.com
的请求,在内部重写为 new-shop.service.com
的对应路径。
server { listen 80; server_name shop.old-site.com; location / { # 将原始请求的 URI 重写(实际上这里没变,仅为示意) # 关键是 break 标志,它让 Nginx 停止重写,继续执行本块内的其他指令 rewrite ^/(.*)$ /$1 break; # 代理到新的服务地址 proxy_pass http://new-shop.service.com; proxy_set_header Host new-shop.service.com; # 注意这里 Host 可以设为后端域名 proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
这个例子与直接使用 proxy_pass
效果类似,但它展示了 rewrite
可以作为 URI 预处理的步骤,我们可以把 /old-category/(.*)
重写为 /new-category/$1
,然后再进行代理。
实际应用场景与最佳实践
-
统一品牌入口:公司收购了多个网站,希望用户通过访问旧的域名,无缝体验新站点的内容,同时逐步统一品牌形象,使用
proxy_pass
可以让所有旧域名都指向新站点,而用户的书签和分享链接依然有效。 -
微服务架构网关:在微服务架构中,Nginx 常作为 API 网关,对外暴露一个统一的域名(如
api.myapp.com
),根据请求路径(/user
,/order
,/payment
)将请求代理到不同的后端微服务实例,这极大简化了客户端的调用逻辑。 -
解决跨域访问问题(CORS):这是
proxy_pass
一个非常强大的副作用,当前后端分离部署在不同域名时,浏览器会因为同源策略而阻止跨域 Ajax 请求,通过将 API 请求代理到与前端页面相同的域名下,浏览器会认为这是同源请求,从而完美绕开跨域限制。 -
隐藏后端技术栈:后端服务可能使用 Java(Tomcat)、Python(Django)或 Node.js,其服务端口和地址不宜直接暴露给公网,Nginx 作为反向代理,对外只提供 80/443 端口,隐藏了后端服务的复杂信息,提升了安全性。
关键注意事项与排错
- 日志:当配置不生效时,首先应检查 Nginx 的
error_log
和access_log
。error_log
会告诉你配置语法错误或代理连接失败的原因;access_log
则能让你看到请求的实际转发情况,检查proxy_pass
的 URI 处理是否符合预期。 - 头信息传递:务必使用
proxy_set_header
正确传递Host
和客户端 IP,很多后端应用依赖Host
头来生成正确的链接,X-Forwarded-For
则是获取真实客户端 IP 的标准。 - SSL 证书:如果用户的原始请求是 HTTPS,Nginx 需要配置 SSL 证书来解密请求,代理到后端时,可以根据内部安全策略决定是使用 HTTP 还是 HTTPS,如果后端也要求 HTTPS,需要确保 Nginx 能够验证后端证书(
proxy_ssl_verify on;
)或在受信任的内网环境中关闭验证。
Nginx 的 proxy_pass
是实现“域名跳转但域名不变”这一需求的基石,它不仅仅是一个简单的转发工具,更是构建现代化、高可用、安全 Web 服务架构的核心组件,通过深入理解其工作原理和配置细节,我们可以灵活地应对各种复杂的业务场景,为用户提供无缝、安全的服务体验。