一个 Nginx 反向代理问题

332 天前
 shiyuu

家里的内网有多个应用,现在 nginx 部署在一台单独的服务器上 192.168.2.180 现在想改造一下访问方式变成访问:

https://192.168.2.180:9444/chat 可以访问到部署的 chatgpt: http://192.168.2.4:50021

https://192.168.2.180:9444/pve 可以访问到 PVE 虚拟机 https://192.168.2.2:8006/

https://192.168.2.180:9444/adguard/ 可以访问到 adguard 的应用 http://192.168.2.200/

但是按照下面的配置,访问到的页面不全,页面各种缺失。 是不是这样的方法不适用??

worker_processes auto;
events {
  worker_connections 1024;
}

http {
  server {
    listen 9444 ssl;
    server_name 192.168.2.180;
    ssl_certificate /etc/nginx/ssl.crt;
    ssl_certificate_key /etc/nginx/ssl.key;
    location /chat/ {
      proxy_pass http://192.168.2.4:50021/;
      proxy_set_header Host $proxy_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    location /pve/ {
      proxy_pass https://192.168.2.2:8006/;
      proxy_set_header Host $proxy_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Accept-Encoding "";
      sub_filter_types *;
    }
    location /adguard/ {
      proxy_pass http://192.168.2.200/;
      proxy_set_header Host $proxy_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Accept-Encoding "";
      sub_filter_types *;
    }
  }
}
4267 次点击
所在节点    NGINX
32 条回复
busier
332 天前
不要随便转换路径!

本来是 / ,你非要转换成 /chat/ 、 /pve/ 、 /adguard/

页面 html 以及 javascript 对资源的引用又不会自动转换!
shiyuu
332 天前
@busier
去掉了 /也是一样的不行

比如 /chat 这个目录,加载这些页面的元素就

会变成 https://192.168.2.4:9444/assets/index-44aaddda.js

而不是 https://192.168.2.4:9444/chat/assets/index-44aaddda.js
SKYNE
332 天前
exiaohao
332 天前
`location` 不能这样写,3 楼大佬正解

一句话解释
1) 楼主要理解 `^/some-path$` 和 `^/some-path/*` 的区别
2) 另外就是访问到比如 /chat 后,对真实的后段怎么把 /chat 去掉
icaolei
332 天前
得看部署的项目本身是否支持动态路径,如果不支持的话只能考虑重写了。
chenluo0429
332 天前
应该说是绝大部分前端项目都不支持动态路径,资源往往是以基于根目录的绝对路径引用的。
匹配未知目录 rewrite 到特定目录也只能处理一个目录下的服务,期望多个二级目录来切分服务,除非你愿意 fork 代码去修改,否则是无法实现你的目的。
二级域名可能更合适一点
blankmiss
332 天前
坑定不适用 为什么不搞个二级域名
laoyutang
332 天前
不行的,你这玩法我之前就试过了,项目前端的 publicpath 你根本控制不了,除非你找源码来改。考虑下用 server_name 分流
qwertty01
332 天前
可以参考我得配置,用域名,https 也能搞定

```
server {
listen 443 ssl;
# listen [::]:443;
server_name test.test.com;
ssl_certificate /etc/nginx/certs/test.com.pem;
ssl_certificate_key /etc/nginx/certs/test.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;

access_log /var/log/nginx/https.access.log main;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}

error_page 404 /404.html;

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

upstream registry {
server 192.168.31.123:5000;
}
upstream image {
server 192.168.31.222:8080;
}
upstream esxi {
server 192.168.31.77:443;
}
upstream rmi {
server 192.168.31.88:443;
}



server {
listen 443 ssl;
# listen [::]:443;
server_name registry.test.com;
ssl_certificate /etc/nginx/certs/test.com.pem;
ssl_certificate_key /etc/nginx/certs/test.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;

access_log /var/log/nginx/https.access.log main;

location / {
proxy_pass http://registry;
}

error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

server {
listen 443 ssl;
# listen [::]:443;
server_name image.test.com;
ssl_certificate /etc/nginx/certs/test.com.pem;
ssl_certificate_key /etc/nginx/certs/test.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;

access_log /var/log/nginx/https.access.log main;

location / {
proxy_pass http://image;
}

error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

server {
listen 443 ssl;
# listen [::]:443;
server_name esxi.test.com;
ssl_certificate /etc/nginx/certs/test.com.pem;
ssl_certificate_key /etc/nginx/certs/test.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;

access_log /var/log/nginx/host.access.log main;

location / {
proxy_pass https://esxi;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}


error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
error_page 404 /404.html;
}

server {
listen 443 ssl;
# listen [::]:443;
server_name rmi.test.com;
ssl_certificate /etc/nginx/certs/test.com.pem;
ssl_certificate_key /etc/nginx/certs/test.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;

access_log /var/log/nginx/host.access.log main;

location / {
proxy_pass https://rmi;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}


error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
error_page 404 /404.html;
}

```
awinds
332 天前
用不同端口转发不同应用吧
JayZXu
332 天前
用子路径的形式,目前大多数前端都不支持
js 和静态资源的调用路径在打包之前就得定义好。

建议使用不同端口号对应不同服务,
或者用 server_name 绑定不同域名,本地改 hosts 来实现基于 server_name 的转发
yujizmq
332 天前
@shiyuu 虽然这个方法有点脏,但应该可以解决,参考:

sub_filter_once off;
sub_filter_types text/html text/css application/javascript;
sub_filter ' href="/' ' href="/chat/';
sub_filter ' src="/' ' src="/chat/';
sub_filter ' action="/' ' action="/chat/';
sub_filter '"assets/' '"chat/assets/';
sub_filter '"assets/' '"chat/assets/';
sub_filter '"/assets/' '"/chat/assets/';
# #sub_filter 'baseURL:"/api"' 'baseURL:"/chat/api"';
sub_filter 'url:"/' 'url:"/chat/';
sub_filter '/sw.js' '/chat/sw.js';
jifengg
332 天前
楼主,建议你别用这种二级目录的方式,因为你转发的都基本上不是自己编译打包的项目,太难通用了。

建议通过域名来转发。
如果自己拥有一个域名,可以把不同的二级域名解析到 192.168.2.180 ,nginx“分别配置 server”,写好 server_name ,进行转发;
如果没有,在机器本地 hosts 随便配置个域名指向 192.168.2.180 也是 ok 的。
clf
332 天前
你没法保证三个服务客户端( web 网页)请求的 url 都能正常分流。

比如第一个应用如果前端路由打包的时候 base 就是 / ,那么请求的 js 资源可能都是 /assist/xxx.js ,如果 3 个项目都是 /assist 的前缀,你根本没法分流。更别提调用的后端接口了。
adoal
332 天前
现在流行的前后端分离项目,最终部署出来的前端静态资源都是“编译”过的,项目内引用路径是写死的,放到反代子路径下,如果你不能自己重新 build 的话,那除了用 sub_filter 来做 dirty hack 之外真没啥好办法。

其实在 good old CGI 时代,前后端不分离的系统,写得体贴的系统会根据 web server 或者反代传过来的 SCRIPT_NAME + PATH_INFO 来自适应地“意识到自己运行在子路径下”从而调整页面内路径引用的生成规则。缺点时静态资源的路径每次都要计算,性能有影响,当然对大部分系统来说没必要担心这个性能。
adoal
332 天前
adoal
332 天前
再比如 Flask 框架官方文档的解决办法 https://flask.palletsprojects.com/en/2.0.x/deploying/fastcgi/
Macv1994
332 天前
二级域名不就行了吗?
huajia2005
332 天前
只是本地访问的话,改 host 配二级域名,然后用 server_name 进行转发,大部分项目而且不是自己的项目的话,静态资源都会有路径问题
IvanLi127
332 天前
这操作不是完全通用,很多项目不一定支持子目录部署,因为没做适配。你想搞这个的话,就三种情况:

- 项目使用相对路径,直接反向代理配置就好了
- 项目支持在编译时通过环境变量配置 BASE 路径,拉源码配一下重新构建前端项目
- 项目直接硬编码路径了,那就得拉源码一个个改了,改完提 PR❤️

我也想把我部署的服务都弄成子目录形式,这样 frp 比较方便,现在用的 frp 便宜是便宜,就是一个通道只能绑三个域名 QAQ

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/944337

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX