最后更新于:2026年06月

WebVPN 与反向代理
WebVPN 与反向代理实战

在所有已知的代理协议中,WebVPN(基于 HTTPS 网站的代理) 是最隐蔽的一种——因为它看起来和正常访问网站毫无区别。无论是 SNI 检查、DPI 深度包检测,还是端口封锁,WebVPN 都能优雅地绕过。本文将带你理解 WebVPN 的核心原理,掌握 Cloudflare Tunnel 反向代理、WebSocket 隧道、流量混淆三大实战技术。


🧭 WebVPN 是什么?

从传统代理到 WebVPN

PLAINTEXT
传统代理的问题:

客户端 ──► 代理服务器 ──► 目标网站
   │
   ├── TCP/UDP 特征明显
   ├── 端口可能被封锁
   ├── SNI/协议指纹可被检测
   └── 一旦 IP 被封,全盘失效

WebVPN 的革命:

客户端 ──► 普通 HTTPS 网站(CDN)──► 反向代理 ──► 目标网站
   │
   ├── 流量完全隐藏在 HTTPS 之后
   ├── 通过 CDN 中转,IP 永远不暴露
   ├── 即使 CDN 节点被封,还有其他节点
   └── 用户体验几乎与正常访问网站无异

WebVPN 核心思想

让代理流量看起来像访问一个普通的 HTTPS 网站。这背后的关键技术包括:

  1. Cloudflare CDN:通过 Cloudflare 中转所有流量,隐藏真实服务器 IP
  2. WebSocket 隧道:在 HTTPS 协议内建立长连接
  3. 流量混淆:让加密流量看起来与正常 HTTPS 网站请求一致
  4. 域名伪装:使用看似正常的域名和子域名

☁️ Cloudflare Tunnel:免费的"反封神器"

Cloudflare Tunnel 原理

PLAINTEXT
┌──────────────────────────────────────────────────────────────┐
│              Cloudflare Tunnel 工作原理                      │
│                                                              │
│  你的设备 ──► Cloudflare 边缘节点 ──► Tunnel 连接 ──► 你的服务器 │
│                    (全球 CDN)                  (真实服务)     │
│                                                              │
│  关键特性:                                                   │
│  1. 不需要在服务器上开放任何入站端口                          │
│  2. Cloudflare 自动用最近节点连接                             │
│  3. 即使服务器 IP 被封,Tunnel 仍然工作                      │
│  4. 完全免费(Cloudflare Free 套餐)                          │
└──────────────────────────────────────────────────────────────┘

准备工作

1. 注册 Cloudflare 账户

2. 添加域名到 Cloudflare

3. 创建 Tunnel

BASH
# 在 Cloudflare Dashboard 中:
# Zero Trust → Networks → Tunnels → Create a tunnel

# 选择 Cloudflared 类型
# 输入 Tunnel 名称(如 my-proxy-tunnel)
# 复制生成的 token(长字符串)

安装 cloudflared

BASH
# Debian/Ubuntu
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared.deb
sudo apt-get install -f

# CentOS/RHEL
curl -L --output cloudflared.rpm https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm
sudo yum install cloudflared.rpm

# macOS
brew install cloudflared

# 验证安装
cloudflared --version

运行 Tunnel

方式一:使用 token 快速启动(推荐)

BASH
# 使用 Dashboard 生成的 token
cloudflared tunnel run --token eyJhIjoixxxxxxx...

# 验证连接
cloudflared tunnel info my-proxy-tunnel

方式二:使用配置文件(生产环境推荐)

BASH
# 登录 Cloudflare
cloudflared tunnel login

# 创建 Tunnel
cloudflared tunnel create my-proxy-tunnel

# 输出:
# Created tunnel my-proxy-tunnel with id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# Created credentials file: /root/.cloudflared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json

创建配置文件 /etc/cloudflared/config.yml

YAML
tunnel: my-proxy-tunnel
credentials-file: /root/.cloudflared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json

# 入口规则(路由)
ingress:
  # 将 proxy.example.com 的流量转发到本地的 xray 端口
  - hostname: proxy.example.com
    service: http://localhost:10080
    originRequest:
      connectTimeout: 30s
      noHappyEyeballs: true
      httpHostHeader: proxy.example.com
      keepAliveConnections: 16
      keepAliveTimeout: 90s

  # 通配规则
  - service: http_status:404

启动服务:

BASH
# 前台启动(调试)
cloudflared tunnel run my-proxy-tunnel

# 后台运行(systemd)
sudo cloudflared service install
sudo systemctl enable --now cloudflared
sudo systemctl status cloudflared

配置 DNS 路由

BASH
# 在 Cloudflare DNS 中添加 CNAME 记录指向 Tunnel
cloudflared tunnel route dns my-proxy-tunnel proxy.example.com

# 输出:
# Added CNAME proxy.example.com pointing to xxxxxxxx.cfargotunnel.com

或者在 Cloudflare Dashboard 中:

后端服务对接

Cloudflare Tunnel 默认转发 HTTP 流量,但我们的代理服务(如 xray)通常使用其他协议。需要做一些转换:

方案一:通过 Nginx 反向代理中转

NGINX
# /etc/nginx/conf.d/tunnel.conf
server {
    listen 10080;
    server_name proxy.example.com;

    # WebSocket 升级
    location / {
        proxy_pass http://127.0.0.1:10086;  # xray 监听端口
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

方案二:xray VLESS + WebSocket 直连

xray 配置文件:

JSON
{
  "inbounds": [
    {
      "port": 10086,
      "protocol": "vless",
      "settings": {
        "clients": [
          {
            "id": "your-uuid-here",
            "level": 0
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "ws",
        "wsSettings": {
          "path": "/secretpath"
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom"
    }
  ]
}

方案三:使用 Cloudflare 的 WARP+ 出口

可以让经过 Tunnel 的流量从 Cloudflare 节点出口(IP 是 Cloudflare 的):

BASH
# 在 cloudflared 启动参数中添加
cloudflared tunnel run --warp-routing

# 或在配置文件中
# config.yml
warp-routing:
  enabled: true

这样配置后,所有通过 Tunnel 出去的流量都会使用 Cloudflare 的 WARP 网络,目标网站看到的 IP 是 Cloudflare 的而不是你的服务器 IP


🔌 WebSocket 隧道:HTTP 协议内的秘密通道

WebSocket 代理原理

PLAINTEXT
┌──────────────────────────────────────────────────────────────┐
│                WebSocket 隧道原理                            │
│                                                              │
│  客户端                                                       │
│    │                                                        │
│    │  HTTP Upgrade: websocket                               │
│    │  Connection: Upgrade                                   │
│    │  Host: proxy.example.com                               │
│    ▼                                                        │
│  Cloudflare CDN ──HTTP/2/WSS──► 服务器                       │
│                                  │                          │
│                                  │  WebSocket 帧            │
│                                  │  内层是代理协议           │
│                                  ▼                          │
│                              xray/sing-box                   │
│                                  │                          │
│                                  ▼                          │
│                              目标网站                         │
│                                                              │
│  关键:外表看是普通 HTTPS + WebSocket,                     │
│  内层是任意代理协议(VLESS/Trojan/Shadowsocks 等)          │
└──────────────────────────────────────────────────────────────┘

sing-box WebSocket 配置

服务端(sing-box 配置文件):

JSON
{
  "inbounds": [
    {
      "type": "vless",
      "tag": "vless-ws-in",
      "listen": "::",
      "listen_port": 10086,
      "users": [
        {
          "name": "default",
          "uuid": "your-uuid-here"
        }
      ],
      "transport": {
        "type": "ws",
        "path": "/secretpath",
        "headers": {
          "Host": "proxy.example.com"
        }
      },
      "tls": {
        "enabled": false
      }
    }
  ],
  "outbounds": [
    {
      "type": "direct"
    }
  ]
}

客户端配置

Clash Meta 客户端:

YAML
proxies:
  - name: "WS-Proxy"
    type: vless
    server: proxy.example.com
    port: 443
    uuid: your-uuid-here
    network: ws
    ws-opts:
      path: /secretpath
      headers:
        Host: proxy.example.com
    tls: true
    skip-cert-verify: true  # 因为 Cloudflare 会替换证书
    sni: proxy.example.com

sing-box 客户端:

JSON
{
  "outbounds": [
    {
      "type": "vless",
      "tag": "proxy-out",
      "server": "proxy.example.com",
      "server_port": 443,
      "uuid": "your-uuid-here",
      "flow": "",
      "packet_encoding": "xudp",
      "transport": {
        "type": "ws",
        "path": "/secretpath",
        "headers": {
          "Host": "proxy.example.com"
        }
      },
      "tls": {
        "enabled": true,
        "server_name": "proxy.example.com",
        "insecure": true
      }
    }
  ]
}

🎭 流量混淆:让代理更"普通"

方案一:使用 Cloudflare 的 HTTP/2 + gRPC

HTTP/2 和 gRPC 在 Cloudflare 上是原生支持的,能让流量看起来更"正常":

xray gRPC 配置:

JSON
{
  "inbounds": [
    {
      "port": 10086,
      "protocol": "vless",
      "settings": {
        "clients": [{"id": "your-uuid-here"}]
      },
      "streamSettings": {
        "network": "grpc",
        "grpcSettings": {
          "serviceName": "cdn-grpc"
        }
      }
    }
  ]
}

方案二:HTTP/2 伪装

JSON
{
  "inbounds": [
    {
      "port": 10086,
      "protocol": "vless",
      "settings": {
        "clients": [{"id": "your-uuid-here"}]
      },
      "streamSettings": {
        "network": "h2",
        "httpSettings": {
          "host": ["www.bing.com"],
          "path": "/search"
        }
      }
    }
  ]
}

方案三:完整反向代理伪装

最极致的伪装——让你的代理服务看起来和普通网站完全一样。

使用 Caddy + xray 实现:

CADDYFILE
# /etc/caddy/Caddyfile
proxy.example.com {
    # 真实网站路径(伪装)
    reverse_proxy /real-path/* http://127.0.0.1:8080

    # 代理流量路径
    reverse_proxy /secretpath/* http://127.0.0.1:10086 {
        transport http {
            versions h2c
        }
    }

    # 默认网站
    root * /var/www/html
    file_server
}

实现效果:

PLAINTEXT
用户访问 https://proxy.example.com/    ← 普通网站首页
用户访问 https://proxy.example.com/blog  ← 伪装博客
用户访问 https://proxy.example.com/secretpath  ← 代理流量(隐藏在普通网站内)

方案四:使用伪装面板

在 VPS 上部署一个真实的网站(如博客、文档站),让 Cloudflare 反代到该网站:

BASH
# 安装 Typecho/Hugo 等博客
# 配置 Caddy/Nginx 服务于 80/443
# Cloudflare Tunnel 反代到这个网站

# 同时,xray 监听 10086 端口
# WebSocket 路径设为 /ws
# 即使 DPI 检测,也只会看到一个普通博客

🏗️ 完整实战方案

方案 A:Cloudflare Tunnel + VLESS + WebSocket

适用场景:追求稳定与抗封锁能力,愿意使用 Cloudflare 节点作为中转

架构图:

PLAINTEXT
┌──────────────────────────────────────────────────────────┐
│                                                          │
│  客户端 → Cloudflare 边缘节点 → Tunnel → xray VLESS    │
│                                  (WSS)     (WebSocket)   │
│                                            │            │
│                                            ▼            │
│                                         目标网站         │
│                                                          │
└──────────────────────────────────────────────────────────┘

配置文件整合(完整版):

YAML
# /etc/cloudflared/config.yml
tunnel: my-proxy-tunnel
credentials-file: /root/.cloudflared/UUID.json

ingress:
  - hostname: proxy.example.com
    service: http://localhost:10080
    originRequest:
      noTLSVerify: true
      connectTimeout: 30s
      keepAliveConnections: 16
      keepAliveTimeout: 90s

  - service: http_status:404
NGINX
# /etc/nginx/conf.d/tunnel.conf
server {
    listen 10080;

    location /secretpath {
        proxy_pass http://127.0.0.1:10086;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 3600s;
    }
}
JSON
// /etc/xray/config.json
{
  "inbounds": [{
    "port": 10086,
    "protocol": "vless",
    "settings": {
      "clients": [{
        "id": "your-uuid-here",
        "level": 0
      }],
      "decryption": "none"
    },
    "streamSettings": {
      "network": "ws",
      "wsSettings": {
        "path": "/secretpath"
      }
    }
  }],
  "outbounds": [{
    "protocol": "freedom"
  }]
}

方案 B:Cloudflare Tunnel + 直连 WARP 出口

适用场景:希望访问被地域封锁的网站(如美国限定内容)

YAML
# /etc/cloudflared/config.yml
tunnel: my-warp-tunnel
credentials-file: /root/.cloudflared/UUID.json

warp-routing:
  enabled: true

ingress:
  - service: hello-world

启动后:

BASH
# 验证
cloudflared tunnel run my-warp-tunnel
curl --interface 0.0.0.0 ifconfig.me
# 应显示 Cloudflare 的 IP

结合 WARP+(付费,速度更快):

BASH
# 在 Cloudflare Dashboard 中启用 WARP+
# Zero Trust → Settings → WARP Client
# 上传 WireGuard 配置后获得 WARP+ 凭证

方案 C:Cloudflare Workers 反向代理

适用场景:不想暴露真实服务器,所有流量由 Cloudflare 处理

JAVASCRIPT
// Cloudflare Worker(部署在 Workers 上)
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  const url = new URL(request.url)

  // 验证请求
  const authHeader = request.headers.get('Authorization')
  if (authHeader !== 'Bearer your-secret-token') {
    return new Response('Unauthorized', { status: 401 })
  }

  // 转发到真实服务器
  const realServerUrl = 'https://your-server-ip:port' + url.pathname
  return fetch(realServerUrl, {
    method: request.method,
    headers: request.headers,
    body: request.body,
    redirect: 'follow'
  })
}

🛡️ 安全配置

1. Tunnel Token 安全

BASH
# 设置 credentials 文件权限
chmod 600 /root/.cloudflared/UUID.json

# 不要将 token 提交到 Git
echo "*.json" >> /root/.cloudflared/.gitignore

2. 限制 Tunnel 访问

BASH
# 在 Cloudflare Dashboard 中配置 Access 策略
# Zero Trust → Access → Applications → Self-hosted

# 限制特定邮箱/国家/地区访问
# 例如:只允许 your@email.com 访问

3. 路径混淆

CADDYFILE
# 使用难以猜测的长路径
proxy.example.com {
    reverse_proxy /a1b2c3d4e5f6g7h8/* http://127.0.0.1:10086 {
        transport http {
            versions h2c
        }
    }
    root * /var/www/normal-site
    file_server
}

4. 速率限制

BASH
# 在 Cloudflare Dashboard 中配置
# Security → WAF → Rate limiting rules

# 例如:限制每 IP 每分钟最多 100 个请求

📊 性能优化

1. 启用 HTTP/2

NGINX
# Nginx 配置
server {
    listen 10080 http2;
    # ...
}

2. 调整 Tunnel 连接数

YAML
# /etc/cloudflared/config.yml
ingress:
  - hostname: proxy.example.com
    service: http://localhost:10080
    originRequest:
      keepAliveConnections: 32
      keepAliveTimeout: 90s
      httpHostHeader: proxy.example.com
      noHappyEyeballs: true

3. 启用 Cloudflare 缓存

PLAINTEXT
# 在 Cloudflare Dashboard 中
# Caching → Configuration → Browser Cache TTL
# 设为 4 hours 或更长

# 静态资源可以被 CDN 缓存,减少 Tunnel 压力

4. 使用 Argo 加速(付费)

BASH
# Cloudflare Dashboard → Traffic → Argo
# 启用 Argo Smart Routing
# 智能选择最快的 Cloudflare 路径
# 免费试用 1 年,之后 $5/月

🔍 故障排查手册

问题 1:Tunnel 连接不上

BASH
# 检查 cloudflared 进程
ps aux | grep cloudflared

# 查看日志
cloudflared tunnel run my-proxy-tunnel
# 观察是否输出 "Connection" 相关错误

# 常见原因:
# 1. Token 错误
# 2. 服务器无法访问 Cloudflare
# 3. DNS 解析问题
# 4. 防火墙阻止了 cloudflared 出站连接

# 检查出站连接
curl -I https://api.cloudflare.com
# 应该返回 200 OK

问题 2:域名无法解析到 Tunnel

BASH
# 检查 DNS 记录
dig proxy.example.com

# 应返回:
# proxy.example.com.  300  IN  CNAME  xxxxxxxx.cfargotunnel.com.

# 检查 Cloudflare 边缘连接
curl -I https://proxy.example.com
# 应该返回 200/404/502 等(不是 DNS 错误)

问题 3:Tunnel 通了但代理不工作

PLAINTEXT
排查步骤:

1. 验证 Tunnel → Nginx 链路
   curl -I http://127.0.0.1:10080
   # 应返回 200/404

2. 验证 Nginx → xray 链路
   curl -I http://127.0.0.1:10086/secretpath
   # 应返回 400(WebSocket 协议错误,但不是连接错误)

3. 验证 xray 服务
   systemctl status xray
   # 查看 xray 日志
   journalctl -u xray -f

4. 验证客户端配置
   - UUID 是否正确
   - 路径是否与服务端一致
   - SNI 是否为 proxy.example.com

问题 4:速度很慢

PLAINTEXT
排查:

1. 检查 Cloudflare 节点选择
   - 免费版无法指定节点
   - 升级 Argo 可能改善

2. 减少 Tunnel 跳数
   - 使用 WARP-Routing 减少中间环节
   - 或在客户端直接连接到 cloudflared 的本地端口(通过其他方式暴露)

3. 检查后端服务性能
   - VPS CPU/内存是否足够
   - WebSocket 协议开销较大

问题 5:Cloudflare 屏蔽了某些端口

PLAINTEXT
Cloudflare 免费版支持的入站端口有限:
80, 443, 8080, 8443, 2052, 2053, 2086, 2087, 2095, 2096

如果 xray 使用其他端口,需要通过 Nginx 反代到这些端口
或使用 Cloudflare Tunnel 绕过端口限制

🆚 方案对比

方案抗封锁速度配置复杂度成本适用场景
Cloudflare Tunnel⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐免费抗封锁首选
WebSocket 隧道⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐免费与 Cloudflare 配合
gRPC 隧道⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐免费性能要求高
完整反向代理⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐免费极致伪装
Workers 反向代理⭐⭐⭐⭐⭐⭐⭐⭐免费不想暴露 IP

📋 完整部署检查清单

PLAINTEXT
部署前准备:
□ 注册 Cloudflare 账户
□ 域名 NS 解析到 Cloudflare
□ 创建 Tunnel,获取 token

部署步骤:
□ 安装 cloudflared
□ 配置 cloudflared config.yml
□ 启动 cloudflared 服务
□ 配置 DNS 路由
□ 部署后端服务(Nginx + xray)
□ 配置客户端

安全配置:
□ 设置 credentials 文件权限
□ 配置 Cloudflare Access 限制
□ 启用 WAF 速率限制
□ 路径混淆

测试验证:
□ Tunnel 状态检查
□ DNS 解析测试
□ 后端服务联通性
□ 客户端连接测试
□ 速度测试

结语

Cloudflare Tunnel + WebSocket 隧道代表了代理技术的"伪装流派"——它不是通过加密来隐藏,而是通过 “看起来像正常 HTTPS 流量” 来隐藏。配合 VLESS/Trojan 等协议,能构建出几乎无法被识别的代理系统。

总结要点:

组合策略建议:

PLAINTEXT
生产环境(推荐):
  Cloudflare Tunnel + VLESS + WebSocket + 伪装网站

性能优先:
  Cloudflare Tunnel + VLESS + gRPC

极致安全:
  Cloudflare Tunnel + VLESS + Reality + WebSocket(套娃)

预算充足:
  Cloudflare Tunnel + Argo + 优选 IP + WARP+

记住:WebVPN 的核心思想不是"加密",而是"看起来像普通网站"。掌握了 Cloudflare Tunnel,你就不再担心 IP 被封、端口被封、SNI 被识别等问题。

愿你的网络访问如同浏览普通网站一样自由、隐蔽!☁️

版权声明

作者: 易邦

链接: https://blog.e8k.net/posts/webvpn-reverse-proxy/

许可证: 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。