之前一直使用 Caddy 作为Web服务器软件,但是自从升级Caddy2后配置大改,官方文档也不详细,就萌生了弃用Caddy的想法,刚好发现nginx1.15.2 版本新增了 $ssl_preread_protocol变量,通过该变量可以使用 stream反向代理时预先判断连接是否为SSL/TLS协议或者为非SSL/TLS协议,从而实现同一个端口来转发不同的业务,卧槽这不就是我想要的么,443端口复用啊。

使用 Docker 部署 Nginx

自己简单使用建议Docker Compose,省资源,迁移方便,只要整个目录打包迁移,然后docker-compose up -d一下就行。
这是我的 Docker Compose 配置文件

同时运行 Nginx 和 PHP 7.4,Nginx 依赖于 php 容器

services:
    nginx:
        image: nginx:latest
        container_name: "nginx"
        restart: always
        environment:
           - TZ=Asia/Shanghai
        depends_on:
           - "php"
        ports:
            - "80:80"
            - "443:443"
        volumes:
          # 本地路径:Docker容器内路径
          # 虚拟主机配置文件存放路径
           - /data/web/webconf:/etc/nginx/conf.d:ro
          # 映射整个 web 目录到 Docker 容器里
           - /data/web:/data/web
          # Nginx 核心配置文件
           - /data/web/nginx-frontend.conf:/etc/nginx/nginx.conf:ro
        networks:
           - mynet
    php:
        image: php:7.4-fpm
        container_name: "php"
        restart: always
        ports:
            - "127.0.0.1:9000:9000"
        environment:
            - TZ=Asia/Shanghai
            - LANG=zh_CN.UTF-8
        volumes:
            - /data/web:/data/web
        networks:
            - mynet

networks:
    mynet:

保存为 /data/docker-compose.yml

编写 Nginx 配置文件

别着急运行容器,没有/data/web/nginx-frontend.conf这个配置文件nginx容器运行不了的。

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

# 流量转发核心配置,主要增加这里,其他都是 Nginx 默认配置复制过来的
stream {
    # 这里就是 SNI 识别,将域名映射成一个配置名,把xxx.com换成你的Trojan域名
    map $ssl_preread_server_name $backend_name {
        xxx.com trojan;
    # 域名都不匹配情况下的默认值
        default web;
    }

    # web,配置转发详情,转发到 444 端口,web 服务使用 444 端口接受 ssl 请求。
    upstream web {
        server 127.0.0.1:444;
    }

    # 匹配到 xxx.com 之后具体转发配置
    upstream trojan {
        # 注意修改 trojan:443
        server trojan:443;
    }

    # 监听 443 并开启 ssl_preread
    server {
        listen 443 reuseport;
        listen [::]:443 reuseport;
        proxy_pass  $backend_name;
        ssl_preread on;
    }
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

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

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;
    gzip on;
    include     /etc/nginx/conf.d/*.conf;
}

额外说明

配置好后在/data目录里执行一下docker-compose up -d就会自动运行nginx 和 php,这里不列出 Trojan 的配置,你懂的,自行上网寻找。

之后新建 Nginx 虚拟主机配置请放在/data/web/webconf目录里,

复用端口后虚拟主机SSL监听端口变成了444,下面给出虚拟机配置文件的参考

server {
    listen 80;
    listen [::]:80;
    server_name xxx;
    #SSL Configuration
    listen 444 ssl;
    ssl_certificate /data/web/webcert/xxx.crt;
    ssl_certificate_key /data/web/webcert/xxx.key;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;
    if ($scheme = http) {
      return 301 https://$server_name$request_uri;
    }
    location / {
      # 具体配置
    }
}

最后

这样配置好后你就可以畅快的在同一台VPS上使用Trojan和Web了,不过nginx不能自动获取SSL证书,建议使用acme.sh配合DNS验证来获取SSL证书,这里不说了,请查阅 https://acme.sh