需求背景
目前接手的一个项目,在开发初期并没有进行配置统一管理,线上域名散布在项目文件中各处。
这种做法虽然开发初期看似方便,但对后期维护造成了很大影响:
- 不便于本地调试:需要频繁手动替换线上地址为本地地址;
- 易出错:开发完成后推送代码前需要手动替换为线上域名,极易漏改;
- 不易协作:不同开发者使用不同端口或后端地址,需反复修改代码。
因为是后续接手而不是从头开发,对于先前文件并不熟悉,因此也不好擅自重构。
因此,为了降低开发环境的维护成本,提高对接效率,并尽量不干扰已有项目结构,采用了本地反向代理 + hosts 映射的方式,模拟线上环境,统一域名入口,便于开发调试。
实现目标
对线上域名进行代理。将域名从线上环境代理到本地开发环境。
可以支持 WebSocket。
在无需进行替换的前提下,实现稳定的本地开发体验。
前置条件
配置本地域名
需要修改本地 hosts 文件,将线上域名指向更改为指向本地 127.0.0.1 地址,以便本地使用 openresty 转发请求。
# 修改 hosts 文件
127.0.0.1 custom.domain.local
# 刷新 dns 配置 使得自定义本地域名生效
ipconfig /flushdns
# 尝试 ping 通
ping custom.domain.local
# 可 ping 通代表设置成功
正在 Ping custom.domain.local [127.0.0.1] 具有 32 字节的数据:
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128
设置 nginx 配置
server {
# 监听 80 端口
listen 80;
# 监听域名
server_name custom.domain.local;
# 设置对应服务
location / {
# 设置本地 8080 端口服务
proxy_pass http://127.0.0.1:8080;
}
}
如果无法访问,需要检查是否设置了网络代理。
在网络代理处设置本地自定义域名不经过代理即可。
配置证书
可以使用 mkcert 这个工具生成 SSL 证书。
mkcert -install # 生成本地根证书
mkcert app.custom.local # 生成 app.custom.local 域名证书
mkcert api.custom.local # 生成 api.custom.local 域名证书
运行以上命令后,将生成对应的证书文件。
为了方便统一管理,建议将这些证书放在一个文件夹内。
设置 SSL
server {
listen 80;
listen 443 ssl;
server_name custom.domain.local;
ssl_certificate 证书路径;
ssl_certificate_key 证书私钥路径;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location / {
# 设置本地 8080 端口服务
proxy_pass http://127.0.0.1:8080;
}
}
配置 WSS 代理
WSS 协议是 WS 协议的 SSL 版本,可以将两者的关系看做 HTTPS 和 HTTP 。
location /ws {
access_by_lua_block {
ngx.log(ngx.INFO, "WebSocket 请求 xx.xx.xx/ws 来自 ", ngx.var.remote_addr)
}
proxy_pass http://xx.xx.xx.xx:xxxx/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
配置以上内容,当访问 wss://xx.xx.xx/ws
时就会自动转发到本地。
为什么代理 http://
而不是 ws://
。
这是因为 ws 协议在初始阶段使用 http 协议发起的
首先客户端发起一个 http 请求用来告诉服务器要将这个连接升级为 ws 协议
服务器若返回特定响应,则连接升级为 ws 协议。
这就是为什么要设置以下内容的原因。
这几项配置用于告诉 nginx 如何处理 WS 连接。
proxy_http_version 1.1; # 支持长连接
proxy_set_header Upgrade $http_upgrade; # 开启协议升级
proxy_set_header Connection "upgrade"; # 明确说明是协议升级
proxy_set_header Host $host; # 可选,用于保持原始域名
拆分配置文件
为了后续维护和方便调整,这里将配置拆分为单独文件。
在 nginx.conf
文件中的 http
块末尾引入其他配置。
# nginx.conf
http {
include ./conf/custom/*.conf; # 导入 custom 目录下的所有配置
}
这里使用通配符 *
而不是单独指名,是因为这样后续可以在文件夹内自由增加配置文件,而不需要每次增删都去修改配置文件。减少了心智负担。
下面是配置文件全貌:
# custom.local.conf
# app.custom.local -> http://localhost:3000
server {
listen 80;
listen 443 ssl;
server_name app.custom.local;
ssl_certificate ./conf/certs/app.custom.local.pem;
ssl_certificate_key ./conf/certs/app.custom.local-key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
access_by_lua_block {
ngx.log(ngx.INFO, "访问 app.custom.local: ", ngx.var.request_uri, " from ", ngx.var.remote_addr)
}
add_header Cache-Control no-store;
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
# api.custom.local -> http://localhost:8080
# wss://api.custom.local/ws -> ws://localhost:3002/ws
server {
listen 80;
listen 443 ssl;
server_name api.custom.local;
# 替换为你自己生成或申请的证书路径(使用 mkcert 或其他工具)
ssl_certificate ./conf/certs/api.custom.local.pem;
ssl_certificate_key ./conf/certs/api.custom.local-key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
access_by_lua_block {
ngx.log(ngx.INFO, "访问 api.custom.local: ", ngx.var.request_uri, " from ", ngx.var.remote_addr)
}
proxy_pass http://localhost:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /ws {
access_by_lua_block {
ngx.log(ngx.INFO, "WebSocket 请求 api.custom.local/ws 来自 ", ngx.var.remote_addr)
}
proxy_pass http://localhost:3002/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
问题排查
无法访问域名
- 未修改 hosts
- 修改 hosts 将域名指向本地 127.0.0.1 即可。
- 修改 hosts 未生效
ipconfig /flushdns
刷新 DNS 。
- 检查是否使用了网络代理。
- 关闭代理或增加规则,确保远程地址是本地 127.0.0.1 的 80 或 443 端口。