前言
注:本文是站在个人站长的角度,更多的是要实现在已有多个网站的服务器上额外加科学上网服务,而不是在新的服务器上安装 Xray 并顺带一个伪装站点。也就是说,站点为主,Xray 为辅,而不是 Xray 为主【不是先有 Xray,后有站点】。
如果你有一台服务器,既要用来放网站又想用来运行 Xray 服务进行科学上网,应该是很多「国内」草根站长的需求。
如果网站方面,仅仅是 nginx 监听 80 端口,也就是仅使用 http 协议,那么两者不会有冲突。但是,如果你想要网站也支持(其实,更多的时候应该是强制)HTTPS,那么就有冲突了,因为 Xray 绑定了 443 端口,nginx 就无法再绑定 443 端口了,反之亦然。
那怎么办呢?肯定不能让任一方使用非 443 端口,这样既不专业也不安全。那么,就只能让其中一个服务把 443 端口全包揽了,无论是科学上网还是站点,都先经由其中一个服务,并由它来分流,然后分流之后的请求再各自到对应的地方(监听端口)。
所以,无非是选择让 Xray 还是 nginx 来监听 443 端口,也就是说有两种方案。下面分别举例介绍这两种方案的对应配置。【下面示例中的域名、端口、路径等记得改成自己的】
方案一:nginx 监听 443 端口并分流给后端的 Xray 服务和其他 nginx 站点
这个方案是直觉上的第一方案,因为在需要科学上网之前,nginx 以及其他站点配套服务早就存在了,我不想为了额外加一个 Xray 服务而过多的打乱之前的站点配置。
修改 /etc/nginx/nginx.conf
要实现 nginx 监听 443 端口并分流,非常简单,最新版的 nginx 有现成的模块(
ngx_stream_ssl_preread_module,👈 nginx 官方文档有详细说明和示例(多种分流方式))。我们需要在 nginx.conf
里添加下面的分流配置代码:
stream {
map $ssl_preread_server_name $name {
xraydomain.tld xray; # xray 服务使用的域名,这里只能单独列出,不能并排,所以有几个域名就得写上几个,多个站点依次列出即可
www.xraydomain.tld xray; # xray 服务使用的域名
domain1.tld domain1; # 正常 HTTPS 站点 1
www.domain1.tld domain1; # 正常 HTTPS 站点 1
domain2.tld domain2; # 正常 HTTPS 站点 2
www.domain2.tld domain2; # 正常 HTTPS 站点 2
}
upstream xray {
server 127.0.0.1:1021; # 分流到 Xray 监听端口
}
upstream domain1 {
server 127.0.0.1:1022; # 分流到站点 1 端口
}
upstream domain2 {
server 127.0.0.1:1023; # 分流到站点 2 端口
}
server {
listen 443 reuseport;
# listen [::]:443 reuseport; # IPV6,我不需要,所以注释掉了
proxy_pass $name;
proxy_protocol on; # 为了获取真实 IP,开启 proxy_protocol,对应的在 default.conf 也需要设置一下
ssl_preread on;
}
}
修改 /etc/nginx/conf.d/default.conf
为了获取真实 IP,修改一下 default.conf
,顶部添加下面代码:
set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;
Xray 配置文件,/usr/local/etc/xray/config.json
Xray 的配置如下,需要注意两个地方,端口不再写 443 了,改成上面 nginx 分流过来的端口,同样的对应 proxy_protocol
,把 acceptProxyProtocol
改成 true
。
另外,这里用的是 websocket 方案,其他都类似。
{
"log": {
"loglevel": "error"
},
"inbounds": [
{
"port": 1021,
"protocol": "vless",
"streamSettings": {
"network": "tcp",
"security": "xtls",
"tcpSettings": {
"acceptProxyProtocol": true
},
"xtlsSettings": {
"minVersion": "1.2",
"alpn": ["h2", "http/1.1"],
"certificates": [
{
"certificateFile": "/path/to/xraydomain.crt",
"keyFile": "/path/to/xraydomain.key"
}
]
}
},
"settings": {
"clients": [
{
"id": "U-U-I-D",
"flow": "xtls-rprx-direct",
"level": 0,
}
],
"decryption": "none",
"fallbacks": [
{
"name": "xraydomain.tld",
"dest": 10211,
"xver": 1
},
{
"name": "xraydomain.tld",
"alpn": "h2",
"dest": 10212,
"xver": 1
},
{
"name": "www.xraydomain.tld",
"dest": 10211,
"xver": 1
},
{
"name": "www.xraydomain.tld",
"alpn": "h2",
"dest": 10212,
"xver": 1
},
{
"path": "/vlessws",
"dest": 1234,
"xver": 1
}
]
}
},
{
"port": 1234,
"listen": "127.0.0.1",
"protocol": "vless",
"settings": {
"clients": [
{
"id": "U-U-I-D",
"level": 0,
}
],
"decryption": "none"
},
"streamSettings": {
"network": "ws",
"security": "none",
"wsSettings": {
"acceptProxyProtocol": true,
"path": "/vlessws"
}
}
}
],
"outbounds": [
{
"protocol": "freedom"
}
]
}
nginx 正常 HTTPS 站点的配置,例如站点 1,/etc/nginx/sites-available/domain1.tld
需要说明一点,看第 8 和 16 行,按理说这里应该是 listen 127.0.0.1:1022 ssl http2 proxy_protocol;
,与上面 nginx.conf
对应,可是我在自己的 VPS 上测试,这样写会出错,可能与我的系统环境有关,我的环境中 nginx 实际监听的是 0.0.0.0:443
,分流过来的也是 0.0.0.0:1022
,如果改成 127.0.0.1
会出错,所以我直接写成了 1022
。
server {
listen 80;
server_name domain1.tld www.domain1.tld;
return 301 https://domain1.tld$request_uri;
}
server {
listen 1022 ssl http2 proxy_protocol;
server_name www.domain1.tld;
ssl_certificate /path/to/domain1.crt;
ssl_certificate_key /path/to/domain1.key;
return 301 https://domain1.tld$request_uri;
}
server {
listen 1022 ssl http2 proxy_protocol;
server_name domain1.tld;
ssl_certificate /path/to/domain1.crt;
ssl_certificate_key /path/to/domain1.key;
# 注意,下面还有更多的 sll 相关配置以及站点其他配置,
# 这里省略了,自己站点之前怎么配置的,接着用就行,无需改动。
}
nginx Xray 伪装站点的配置,/etc/nginx/sites-available/xraydomain.tld
server {
listen 80;
server_name xraydomain.tld www.xraydomain.tld;
return 301 https://xraydomain.tld$request_uri;
}
server {
listen 10211 proxy_protocol;
listen 10212 proxy_protocol http2;
server_name xraydomain.tld www.xraydomain.tld;
# 注意,下面还有更多的站点配置,root、error_log 等等这里省略了,请根据自己的情况配置。
}
方案二:Xray 监听 443 端口并分流给后端的 nginx 站点(包括伪装站)
这个方案是让 Xray 在前端,负责监听 443 端口,并使用 SNI 机制配合 fallbacks 规则实现分流,nginx 站点(包括伪装站)在后面接应。所以,如果有很多个站点的话,都得罗列到 Xray 的 config.json
里,包括域名证书、fallbacks 等,最终这个文件会很长,注意不要出错。
主要是不确定 fallbacks 里面的域名能不能并列着写,如果可以的话,还能减少一些行数,否则就会像下面这样,站点越多越长。
另外,这里用的是 websocket 方案,其他都类似。
Xray 负责监听 443 端口并分流,/usr/local/etc/xray/config.json
{
"log": {
"loglevel": "error"
},
"inbounds": [
{
"port": 443,
"protocol": "vless",
"streamSettings": {
"network": "tcp",
"security": "xtls",
"xtlsSettings": {
"minVersion": "1.2",
"alpn": ["h2", "http/1.1"],
"certificates": [
{
"certificateFile": "/path/to/xraydomain.crt",
"keyFile": "/path/to/xraydomain.key"
},
{
"certificateFile": "/path/to/domain1.crt",
"keyFile": "/path/to/domain1.key"
},
{
"certificateFile": "/path/to/domain2.crt",
"keyFile": "/path/to/domain2.key"
}
]
}
},
"settings": {
"clients": [
{
"id": "U-U-I-D",
"flow": "xtls-rprx-direct",
"level": 0,
}
],
"decryption": "none",
"fallbacks": [
{
"name": "xraydomain.tld",
"dest": 10211,
"xver": 1
},
{
"name": "xraydomain.tld",
"alpn": "h2",
"dest": 10212,
"xver": 1
},
{
"name": "www.xraydomain.tld",
"dest": 10211,
"xver": 1
},
{
"name": "www.xraydomain.tld",
"alpn": "h2",
"dest": 10212,
"xver": 1
},
{
"path": "/vlessws",
"dest": 1234,
"xver": 1
},
{
"name": "domain1.tld",
"dest": 10221,
"xver": 1
},
{
"name": "domain1.tld",
"alpn": "h2",
"dest": 10222,
"xver": 1
},
{
"name": "www.domain1.tld",
"dest": 10221,
"xver": 1
},
{
"name": "www.domain1.tld",
"alpn": "h2",
"dest": 10222,
"xver": 1
},
{
"name": "domain2.tld",
"dest": 10231,
"xver": 1
},
{
"name": "domain2.tld",
"alpn": "h2",
"dest": 10232,
"xver": 1
},
{
"name": "www.domain2.tld",
"dest": 10231,
"xver": 1
},
{
"name": "www.domain2.tld",
"alpn": "h2",
"dest": 10232,
"xver": 1
}
]
}
},
{
"port": 1234,
"listen": "127.0.0.1",
"protocol": "vless",
"settings": {
"clients": [
{
"id": "U-U-I-D",
"level": 0,
}
],
"decryption": "none"
},
"streamSettings": {
"network": "ws",
"security": "none",
"wsSettings": {
"acceptProxyProtocol": true,
"path": "/vlessws"
}
}
}
],
"outbounds": [
{
"protocol": "freedom"
}
]
}
修改 nginx default 配置,/etc/nginx/conf.d/default.conf
为了获取真实 IP,修改一下 default.conf
,顶部添加下面代码:
set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;
Xray 伪装站点的配置,/etc/nginx/sites-available/xraydomain.tld
server {
listen 80;
server_name xraydomain.tld www.xraydomain.tld;
return 301 https://xraydomain.tld$request_uri;
}
server {
listen 127.0.0.1:10211 proxy_protocol;
listen 127.0.0.1:10212 proxy_protocol http2;
server_name xraydomain.tld www.xraydomain.tld;
# 注意,下面还有更多的站点配置,root、error_log 等等这里省略了,请根据自己的情况配置。
}
nginx 正常 HTTPS 站点的配置,例如站点 2,/etc/nginx/sites-available/domain2.tld
需要注意的是,nginx 需要用两个端口分别来监听 HTTP/1.1 和 HTTP/2,前面的 Xray 分流已经给区分开了,带着 "alpn": "h2",
的即是 HTTP/2协议。
server {
listen 80;
server_name domain2.tld www.domain2.tld;
return 301 https://domain2.tld$request_uri;
}
server {
listen 127.0.0.1:10231 proxy_protocol;
listen 127.0.0.1:10232 http2 proxy_protocol;
server_name www.domain2.tld;
ssl_certificate /path/to/domain2.crt;
ssl_certificate_key /path/to/domain2.key;
return 301 https://domain2.tld$request_uri;
}
server {
listen 127.0.0.1:10231 proxy_protocol;
listen 127.0.0.1:10232 http2 proxy_protocol;
server_name domain2.tld;
ssl_certificate /path/to/domain2.crt;
ssl_certificate_key /path/to/domain2.key;
# 注意,下面还有更多的 sll 相关配置以及站点其他配置,
# 这里省略了,自己站点之前怎么配置的,接着用就行,无需改动。
}