Ghost与Xray共存的配置问题

Xray最标准的配置是直接监听443端口,然后通过配置对传入的流量分流,通过fallback机制,将非vless协议流量回落到指定的端口处理。

在官方的Ghost配置中,使用NginX接收请求,并通过反向代理传递给Ghost,Ghost只监听本地端口,这时有两种方案。

方案一:在Ghost中配置URL为http开头,然后NginX接收明文请求(可以直接监听80端口)即可。整个博客是非加密的。

方案二:Ghost官方推荐,在Ghost中配置URL为https开头,NginX负责接收TLS数据包,解密然后传递给Ghost处理,这种配置下全站为TLS加密。

Xray与Ghost配置在同一台服务器上时,Xray监听了443端口,负责处理TLS数据包,非vless协议流量会解密,然后转发给NginX。如果我们采用方案一,NginX监听80端口,直接访问http开头的blog URL时,数据包会发送到服务器的80端口,然后交给Ghost处理,blog是正常访问的;手动指定https时,TLS数据包先发送给Xray然后解密,回落到80端口给NginX,理论上也应该能正常访问。但因为Ghost配置为非加密模式,网页内的资源URL(如图片等)会为http开头,而客户端的浏览器因为页面本身的URL是https开头,基于安全性要求,遇到页面中http开头的资源URL会直接阻挡,从而导致网页显示不完整,图片等无法加载。

如果我们将Ghost配置为加密模式,即将blog URL直接设置为https开头,Ghost返回页面内的资源URL自然是https开头的。但在实际测试中,NginX因为配置为监听明文请求,Ghost会认为传入的是非加密流量,自动要求浏览器重定向到https开头的URL上。但浏览器发送的请求实际上传给了Xray,Xray回落到NginX上还是明文请求,从而进入重定向循环,浏览器报错。如果采用方案二,Xray回落流量为非加密,NginX监听的是加密请求,同样会出错。

实际上不管方案一还是方案二,Ghost的本地转发URL都是http开头的,即接收明文请求。那Ghost是如何判断传入NginX的流量类型的呢?Ghost官方提供的NginX配置模板是这样的:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name <%= url %>;
    root <%= webroot %>; # Used for acme.sh SSL verification (https://acme.sh)

    ssl_certificate <%= fullchain %>;
    ssl_certificate_key <%= privkey %>;
    include <%= sslparams %>;

    location <%= location %> {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://127.0.0.1:<%= port %>;
        <% if (location !== '/') { %>proxy_redirect off;<% } %>
    }

    location ~ /.well-known {
        allow all;
    }

    client_max_body_size 50m;
}

可见,location中,有一行proxy_set_header X-Forwarded-Proto $scheme;MDN中这样介绍:

The X-Forwarded-Proto (XFP) header is a de-facto standard header for identifying the protocol (HTTP or HTTPS) that a client used to connect to your proxy or load balancer. Your server access logs contain the protocol used between the server and the load balancer, but not the protocol used between the client and the load balancer. To determine the protocol used between the client and the load balancer, the X-Forwarded-Proto request header can be used.

X-Forwarded-Proto传递了协议头。在上面的配置文件中可见传递的是 $scheme ,我们将这一字段直接改为 https ,即向ghost传达,传入NginX的流量是https的,避免ghost在配置URL为https开头时的重定向循环问题。我们的NginX配置如下:

server {
	# 两个端口分别监听http1.1和h2c
    listen localhost:8080 default_server;
    listen localhost:8081 default_server http2;
	# HSTS设置
    add_header Strict-Transport-Security "max-age=63072000" always;
    server_name 博客域名;
    gzip on;
    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # 传递协议头https
        proxy_set_header X-Forwarded-Proto https;
        proxy_pass http://127.0.0.1:2368;

    }

    location ~ /.well-known {
        allow all;
    }

    client_max_body_size 50m;
}

# 80端口接收请求后重定向到https
server {
    listen 80;
    return 301 https://$host$request_uri;
}

同时,Ghost的URL配置为https开头,Xray配置流量回落到8080与8081端口后,即可实现Xray与Ghost的共存。