[{"data":1,"prerenderedAt":153},["ShallowReactive",2],{"$fxUC4AN4SkPhKGruZjPsEkLXs7X0bo95WBoNieHl1Pr0":3,"$f3Fq3Vvc1RPyMVhHFov2pUb_iMkT70EUmvvI_ji0wSyQ":51},{"title":4,"date":5,"category":6,"readTime":7,"tags":8,"content":19,"_excerpt":20,"_faqs":21,"id":49,"_path":50},"海外 VPS 的 CDN 加速与网站防探测架构（Cloudflare + nginx 反代 + WebSocket 后端完整方案）","2026-06-15","网络工具","10 分钟",[9,10,11,12,13,14,15,16,17,18],"Cloudflare","nginx","反向代理","WebSocket","CDN 加速","网站防探测","VPS 安全","端口扫描","指纹识别","Origin 证书","\u003Ch1>海外 VPS 的 CDN 加速与网站防探测架构（Cloudflare + nginx 反代 + WebSocket 后端完整方案）\u003C\u002Fh1>\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>核心结论\u003C\u002Fstrong>：海外 VPS 一旦暴露真实 IP，会被全球的端口扫描器（Shodan、Censys、ZoomEye）持续探测，443 端口上的服务指纹会被识别收录。\u003Cstrong>正确做法是把所有流量套上 Cloudflare CDN 隐藏真实 IP，再用 nginx 反向代理做&quot;防探测&quot;——外部扫描器看到的是普通博客 \u002F 静态站点，真正的业务服务（WebSocket \u002F API）只跑在 127.0.0.1 上、永远不对外暴露\u003C\u002Fstrong>。本文给出一套从零到一的完整部署方案，\u003Cstrong>适用所有需要保护 VPS 真实 IP 的场景\u003C\u002Fstrong>（个人项目、SaaS、API 服务、实时通信后端等）。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>\u003Cstrong>3 分钟摘要\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>VPS 真实 IP 暴露后，会被扫描器 24\u002F7 探测，443 端口指纹泄露\u003C\u002Fli>\n\u003Cli>用 Cloudflare CDN \u003Cstrong>隐藏真实 VPS IP\u003C\u002Fstrong>（免费方案）\u003C\u002Fli>\n\u003Cli>真实 WebSocket \u002F API 服务跑在 \u003Ccode>127.0.0.1\u003C\u002Fcode> 的非标准端口，\u003Cstrong>永不对外暴露\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>nginx 终结 TLS + 转发 WebSocket；非业务路径返回假博客\u003C\u002Fli>\n\u003Cli>外部扫描器看到的是 &quot;一个普通的 HTTPS 博客&quot;，无法识别真实服务\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>实测环境\u003C\u002Fstrong>：RackNerd VPS（Ubuntu 22.04）+ Node.js（\u003Ccode>ws\u003C\u002Fcode> 库） + nginx 1.18 + Cloudflare 免费版\n\u003Cstrong>适用场景\u003C\u002Fstrong>：海外 VPS 上跑 WebSocket 后端 \u002F API 服务 \u002F 实时通信，需要隐藏真实 IP、防止指纹探测\n\u003Cstrong>最终效果\u003C\u002Fstrong>：VPS 真实 IP 永不暴露，对外只有一个 Cloudflare CDN 节点，扫描器看到的 443 端口是普通博客\u003C\u002Fp>\n\u003Chr>\n\u003Ch2>一、问题背景\u003C\u002Fh2>\n\u003Cp>从 RackNerd 买了一台美国 VPS，IP \u003Ccode>104.223.xxx.xxx\u003C\u002Fcode>，准备在上面跑一个 WebSocket 实时通知服务。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>异常表现\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>服务刚上线，端口扫描器（Censys \u002F Shodan）几小时内就把 443 端口的指纹收录了\u003C\u002Fli>\n\u003Cli>收到一堆来源不明的 HTTPS 探测请求，识别出 &quot;非标准 WebSocket 服务&quot;\u003C\u002Fli>\n\u003Cli>真实 IP 直接暴露在 DNS A 记录里 → 任何人都能绕过 CDN 直连 VPS\u003C\u002Fli>\n\u003Cli>VPS 频繁收到自动化探测流量，\u003Cstrong>服务指纹被识别 → 容易被针对性攻击或封禁\u003C\u002Fstrong>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>结论\u003C\u002Fstrong>：VPS 真实 IP + 业务端口一旦直接暴露在全球互联网上，\u003Cstrong>被扫描是必然的、被指纹识别是早晚的\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>正确做法\u003C\u002Fstrong>：\u003Cstrong>让所有流量走 Cloudflare CDN，真实 IP 不再出现在 DNS 里\u003C\u002Fstrong>；\u003Cstrong>业务服务只跑在 127.0.0.1 上\u003C\u002Fstrong>，对外不可见；\u003Cstrong>nginx 在公网 443 终结 TLS、转发到内部服务\u003C\u002Fstrong>，同时给扫描器看一个假博客。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2>二、最终架构\u003C\u002Fh2>\n\u003Cpre>\u003Ccode>┌────────────────────────────────────────┐\n│  客户端（浏览器 \u002F 移动 App）            │\n│   业务域名: app.example.com            │\n│   访问 \u002Fapi 或 \u002Fws 走 WebSocket        │\n└─────────────┬──────────────────────────┘\n              │  TLS 443（看起来是普通 HTTPS）\n              ▼\n┌────────────────────────────────────────┐\n│  Cloudflare Edge（全球 CDN 节点）       │\n│   - 隐藏真实 VPS IP                     │\n│   - 全球边缘加速                        │\n│   - 缓解端口扫描 \u002F 指纹识别             │\n└─────────────┬──────────────────────────┘\n              │  TLS 443（CF 回源）\n              ▼\n┌────────────────────────────────────────┐\n│  VPS nginx (0.0.0.0:443)               │\n│   路径 \u002Fws   → 127.0.0.1:10000 → 后端  │\n│   其他路径  → \u002Fvar\u002Fwww\u002Fsite (假博客)    │\n└─────────────┬──────────────────────────┘\n              │\n              ▼\n┌────────────────────────────────────────┐\n│  Node.js WebSocket 后端                 │\n│   监听 127.0.0.1:10000                  │\n│   永不对外暴露                           │\n└────────────────────────────────────────┘\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>三层防护\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>CF CDN\u003C\u002Fstrong> — 客户端只看到 CF 节点，\u003Cstrong>永远看不到 VPS IP\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>nginx 反代\u003C\u002Fstrong> — 端口扫描器看到的是普通博客，\u003Cstrong>看不到真实业务服务\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>127.0.0.1 监听\u003C\u002Fstrong> — 业务服务只对内网可见，\u003Cstrong>公网完全无法直连\u003C\u002Fstrong>\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Chr>\n\u003Ch2>三、前置准备\u003C\u002Fh2>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>项目\u003C\u002Fth>\n\u003Cth>说明\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd>域名\u003C\u002Ftd>\n\u003Ctd>自己的域名（已在 Cloudflare 接入，能改 NS）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>VPS\u003C\u002Ftd>\n\u003Ctd>任意海外 VPS（本文用 RackNerd Ubuntu 22.04）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Node.js 18+\u003C\u002Ftd>\n\u003Ctd>VPS 上已装好（用于跑 WebSocket 后端）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>nginx\u003C\u002Ftd>\n\u003Ctd>VPS 上已装好（TLS 终结 + 反向代理）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>\u003Cstrong>本文用到的占位符\u003C\u002Fstrong>（实际配置时替换成自己的）：\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>占位符\u003C\u002Fth>\n\u003Cth>含义\u003C\u002Fth>\n\u003Cth>示例\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd>\u003Ccode>&lt;域名&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>CF 上接入的域名\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>qzz.io\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>app.&lt;域名&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>业务子域\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>app.qzz.io\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>&lt;VPS_IP&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>VPS 真实 IP\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>104.223.xxx.xxx\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>&lt;WS_PORT&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>内部 WebSocket 端口\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>10000\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Chr>\n\u003Ch2>四、VPS 端配置\u003C\u002Fh2>\n\u003Ch3>4.1 部署 WebSocket 后端（监听 127.0.0.1）\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>用一个最简的 Node.js WebSocket echo 服务作为示例\u003C\u002Fstrong>（生产环境换成你的真实业务服务）：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">mkdir -p \u002Fopt\u002Fwsapp &amp;&amp; cd \u002Fopt\u002Fwsapp\ncat &gt; package.json &lt;&lt;&#39;EOF&#39;\n{\n  &quot;name&quot;: &quot;wsapp&quot;,\n  &quot;version&quot;: &quot;1.0.0&quot;,\n  &quot;type&quot;: &quot;module&quot;,\n  &quot;dependencies&quot;: { &quot;ws&quot;: &quot;^8.18.0&quot; }\n}\nEOF\nnpm install\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode class=\"language-bash\">cat &gt; \u002Fopt\u002Fwsapp\u002Fserver.mjs &lt;&lt;&#39;EOF&#39;\nimport { WebSocketServer } from &#39;ws&#39;\n\n\u002F\u002F 关键:只监听 127.0.0.1,公网完全无法直连\nconst wss = new WebSocketServer({ host: &#39;127.0.0.1&#39;, port: 10000 })\n\nwss.on(&#39;connection&#39;, (ws) =&gt; {\n  console.log(&#39;client connected&#39;)\n  ws.on(&#39;message&#39;, (data) =&gt; {\n    \u002F\u002F 业务逻辑:这里换成你的真实处理\n    ws.send(`echo: ${data.toString()}`)\n  })\n  ws.on(&#39;close&#39;, () =&gt; console.log(&#39;client disconnected&#39;))\n})\n\nconsole.log(&#39;WebSocket server running on ws:\u002F\u002F127.0.0.1:10000&#39;)\nEOF\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>用 systemd 管理\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">cat &gt; \u002Fetc\u002Fsystemd\u002Fsystem\u002Fwsapp.service &lt;&lt;&#39;EOF&#39;\n[Unit]\nDescription=WebSocket Backend\nAfter=network.target\n\n[Service]\nType=simple\nUser=root\nWorkingDirectory=\u002Fopt\u002Fwsapp\nExecStart=\u002Fusr\u002Fbin\u002Fnode \u002Fopt\u002Fwsapp\u002Fserver.mjs\nRestart=always\nRestartSec=3\n\n[Install]\nWantedBy=multi-user.target\nEOF\n\nsystemctl daemon-reload\nsystemctl enable --now wsapp\nsystemctl status wsapp\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>验证\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">ss -tlnp | grep 10000\n# 期望看到:127.0.0.1:10000  (必须是 127.0.0.1,不是 0.0.0.0)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>4.2 生成 Cloudflare Origin Certificate\u003C\u002Fh3>\n\u003Cp>CF 控制台 → \u003Cstrong>SSL\u002FTLS\u003C\u002Fstrong> → \u003Cstrong>Origin Server\u003C\u002Fstrong> → \u003Cstrong>Create Certificate\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Hostname：填 \u003Ccode>app.&lt;域名&gt;\u003C\u002Fcode>（比如 \u003Ccode>app.qzz.io\u003C\u002Fcode>）\u003C\u002Fli>\n\u003Cli>Validity：\u003Ccode>15 years\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>点 \u003Cstrong>Create\u003C\u002Fstrong>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>复制生成的两段内容：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>Certificate\u003C\u002Fcode> 框内容（含 \u003Ccode>-----BEGIN CERTIFICATE-----\u003C\u002Fcode>）→ 存到 VPS \u003Ccode>\u002Fetc\u002Fssl\u002Fcf\u002Fcert.pem\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode>Private key\u003C\u002Fcode> 框内容（含 \u003Ccode>-----BEGIN PRIVATE KEY-----\u003C\u002Fcode>）→ 存到 VPS \u003Ccode>\u002Fetc\u002Fssl\u002Fcf\u002Fkey.pem\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>在 VPS 上执行：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">mkdir -p \u002Fetc\u002Fssl\u002Fcf\nnano \u002Fetc\u002Fssl\u002Fcf\u002Fcert.pem    # 粘贴证书内容,Ctrl+O 保存,Ctrl+X 退出\nnano \u002Fetc\u002Fssl\u002Fcf\u002Fkey.pem     # 粘贴私钥内容\nchmod 644 \u002Fetc\u002Fssl\u002Fcf\u002Fcert.pem\nchmod 600 \u002Fetc\u002Fssl\u002Fcf\u002Fkey.pem\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>4.3 配置 Cloudflare 加密模式和 WebSocket\u003C\u002Fh3>\n\u003Cp>CF 控制台：\u003C\u002Fp>\n\u003Cp>\u003Cstrong>SSL\u002FTLS → Overview\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>加密模式选 \u003Cstrong>\u003Ccode>完整 (严格)\u003C\u002Fcode>\u003C\u002Fstrong>（不要选&quot;灵活&quot;，否则 CF 到源站不加密，nginx 收不到 TLS 握手）\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>Network\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>WebSockets\u003C\u002Fstrong> → \u003Cstrong>\u003Ccode>On\u003C\u002Fcode>\u003C\u002Fstrong>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>4.4 在 Cloudflare 添加 DNS 记录\u003C\u002Fh3>\n\u003Cp>CF 控制台 → \u003Cstrong>DNS\u003C\u002Fstrong> → \u003Cstrong>Records\u003C\u002Fstrong> → \u003Cstrong>+ Add record\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>字段\u003C\u002Fth>\n\u003Cth>值\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd>Type\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>A\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Name\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>app\u003C\u002Fcode>（子域名前缀，最终域名是 \u003Ccode>app.&lt;域名&gt;\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>IPv4 address\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>&lt;VPS_IP&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Proxy status\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>\u003Ccode>Proxied\u003C\u002Fcode>（橙色云朵）\u003C\u002Fstrong> ← 必须选这个\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>TTL\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>Auto\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>保存后验证（Windows PowerShell）：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-powershell\">nslookup app.&lt;域名&gt;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>期望返回 CF 的 IP\u003C\u002Fstrong>（\u003Ccode>104.21.x.x\u003C\u002Fcode> \u002F \u003Ccode>172.67.x.x\u003C\u002Fcode> 等），\u003Cstrong>不是 VPS IP\u003C\u002Fstrong>。如果返回 VPS IP，说明橙色云朵没开。\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>⚠️ \u003Cstrong>这一步是关键\u003C\u002Fstrong>：DNS 显示 CF IP 而不是 VPS IP，意味着端口扫描器通过 DNS 查不到真实 VPS，\u003Cstrong>真实 IP 被有效隐藏\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch3>4.5 验证：先单独测 CF → VPS → 后端链路\u003C\u002Fh3>\n\u003Cp>在写 nginx 之前，先确认 CF → VPS → 10000 端口的链路通：\u003C\u002Fp>\n\u003Cp>\u003Cstrong>临时直接用 nginx listen 10000 转发测试\u003C\u002Fstrong>（如果不想折腾，可以跳过这一步直接到 4.6）：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 用 curl 从 VPS 内部测试 WebSocket 后端\ncurl.exe --include \\\n  --no-buffer \\\n  --header &quot;Connection: Upgrade&quot; \\\n  --header &quot;Upgrade: websocket&quot; \\\n  --header &quot;Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==&quot; \\\n  --header &quot;Sec-WebSocket-Version: 13&quot; \\\n  http:\u002F\u002F127.0.0.1:10000\u002F\n# 期望:HTTP\u002F1.1 101 Switching Protocols\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>4.6 安装 nginx\u003C\u002Fh3>\n\u003Cp>VPS 上（如果还没装）：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">apt update &amp;&amp; apt install -y nginx\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cblockquote>\n\u003Cp>⚠️ 注意是 \u003Ccode>apt install\u003C\u002Fcode> 不是 \u003Ccode>install\u003C\u002Fcode>（常见笔误）。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch3>4.7 写&quot;防探测&quot;展示站\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>关键思路\u003C\u002Fstrong>：让端口扫描器访问你的域名时，看到的是一个\u003Cstrong>普通博客\u003C\u002Fstrong>——而不是 WebSocket 服务。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">mkdir -p \u002Fvar\u002Fwww\u002Fsite\ncat &gt; \u002Fvar\u002Fwww\u002Fsite\u002Findex.html &lt;&lt;&#39;EOF&#39;\n&lt;!DOCTYPE html&gt;\n&lt;html lang=&quot;en&quot;&gt;\n&lt;head&gt;\n    &lt;meta charset=&quot;UTF-8&quot;&gt;\n    &lt;title&gt;My Notes&lt;\u002Ftitle&gt;\n    &lt;style&gt;\n        body { font-family: -apple-system, sans-serif; max-width: 720px; margin: 40px auto; padding: 0 20px; color: #333; }\n        h1 { border-bottom: 1px solid #eee; padding-bottom: 10px; }\n        .post { margin: 20px 0; }\n        .date { color: #999; font-size: 0.9em; }\n    &lt;\u002Fstyle&gt;\n&lt;\u002Fhead&gt;\n&lt;body&gt;\n    &lt;h1&gt;My Notes&lt;\u002Fh1&gt;\n    &lt;div class=&quot;post&quot;&gt;\n        &lt;h2&gt;Hello, world&lt;\u002Fh2&gt;\n        &lt;p class=&quot;date&quot;&gt;2026-01-15&lt;\u002Fp&gt;\n        &lt;p&gt;Just a personal blog. Nothing to see here.&lt;\u002Fp&gt;\n    &lt;\u002Fdiv&gt;\n    &lt;div class=&quot;post&quot;&gt;\n        &lt;h2&gt;About&lt;\u002Fh2&gt;\n        &lt;p&gt;This is my personal site. Drop me a line if you want to chat.&lt;\u002Fp&gt;\n    &lt;\u002Fdiv&gt;\n&lt;\u002Fbody&gt;\n&lt;\u002Fhtml&gt;\nEOF\n\ncat &gt; \u002Fvar\u002Fwww\u002Fsite\u002Frobots.txt &lt;&lt;&#39;EOF&#39;\nUser-agent: *\nDisallow: \u002Fadmin\u002F\nDisallow: \u002Fprivate\u002F\nAllow: \u002F\nEOF\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>4.8 写 nginx 配置\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">cat &gt; \u002Fetc\u002Fnginx\u002Fconf.d\u002Fapp.conf &lt;&lt;&#39;EOF&#39;\n# 默认 server:处理无 SNI \u002F 端口扫描请求(防止指纹识别)\nserver {\n    listen 443 ssl http2 default_server;\n    server_name _;\n\n    ssl_certificate     \u002Fetc\u002Fssl\u002Fcf\u002Fcert.pem;\n    ssl_certificate_key \u002Fetc\u002Fssl\u002Fcf\u002Fkey.pem;\n    ssl_protocols       TLSv1.2 TLSv1.3;\n\n    root  \u002Fvar\u002Fwww\u002Fsite;\n    index index.html;\n\n    location \u002F {\n        try_files $uri $uri\u002F \u002Findex.html;\n    }\n}\n\n# 真实业务:匹配 app.&lt;域名&gt;\nserver {\n    listen 443 ssl http2;\n    server_name app.&lt;域名&gt;;\n\n    ssl_certificate     \u002Fetc\u002Fssl\u002Fcf\u002Fcert.pem;\n    ssl_certificate_key \u002Fetc\u002Fssl\u002Fcf\u002Fkey.pem;\n    ssl_protocols       TLSv1.2 TLSv1.3;\n    ssl_ciphers         HIGH:!aNULL:!MD5;\n\n    root  \u002Fvar\u002Fwww\u002Fsite;\n    index index.html;\n\n    # WebSocket 路径 → 后端服务\n    location \u002Fws {\n        proxy_pass http:\u002F\u002F127.0.0.1:10000;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection &quot;upgrade&quot;;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_read_timeout 3600s;\n    }\n\n    # 默认:返回防探测展示站\n    location \u002F {\n        try_files $uri $uri\u002F \u002Findex.html;\n    }\n}\n\n# HTTP → HTTPS 重定向\nserver {\n    listen 80;\n    server_name app.&lt;域名&gt;;\n    return 301 https:\u002F\u002F$host$request_uri;\n}\n\n# 80 端口 default_server(防止端口扫描暴露)\nserver {\n    listen 80 default_server;\n    server_name _;\n    root \u002Fvar\u002Fwww\u002Fsite;\n    index index.html;\n    location \u002F {\n        try_files $uri $uri\u002F \u002Findex.html;\n    }\n}\nEOF\n\n# 删掉 nginx 默认站(避免冲突)\nrm -f \u002Fetc\u002Fnginx\u002Fsites-enabled\u002Fdefault\n\n# 检查 + 启动\nnginx -t\nsystemctl enable --now nginx\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>4.9 验证\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 1. 端口监听\nss -tlnp | grep -E &#39;:80|:443|:10000&#39;\n# 期望:\n#   nginx 在 *:80、*:443\n#   wsapp 在 127.0.0.1:10000(必须是 127.0.0.1,不是 0.0.0.0)\n\n# 2. 域名访问展示站\ncurl.exe -sI https:\u002F\u002Fapp.&lt;域名&gt;\u002F\n# 期望:HTTP\u002F2 200\n\n# 3. 看展示站内容\ncurl.exe -s https:\u002F\u002Fapp.&lt;域名&gt;\u002F | findstr &quot;My Notes&quot;\n# 期望:能找到 My Notes 字样\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2>五、客户端连接示例\u003C\u002Fh2>\n\u003Ch3>5.1 浏览器 WebSocket 客户端\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-html\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;body&gt;\n  &lt;script&gt;\n    const ws = new WebSocket(&#39;wss:\u002F\u002Fapp.&lt;域名&gt;\u002Fws&#39;)\n    ws.onopen = () =&gt; {\n      console.log(&#39;connected&#39;)\n      ws.send(&#39;hello from browser&#39;)\n    }\n    ws.onmessage = (e) =&gt; console.log(&#39;received:&#39;, e.data)\n    ws.onerror = (e) =&gt; console.error(&#39;error:&#39;, e)\n  &lt;\u002Fscript&gt;\n&lt;\u002Fbody&gt;\n&lt;\u002Fhtml&gt;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>5.2 Node.js WebSocket 客户端\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-javascript\">import WebSocket from &#39;ws&#39;\n\nconst ws = new WebSocket(&#39;wss:\u002F\u002Fapp.&lt;域名&gt;\u002Fws&#39;)\n\nws.on(&#39;open&#39;, () =&gt; {\n  console.log(&#39;connected&#39;)\n  ws.send(&#39;hello from node&#39;)\n})\n\nws.on(&#39;message&#39;, (data) =&gt; {\n  console.log(&#39;received:&#39;, data.toString())\n})\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>5.3 验证全链路\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 用 websocat 测(推荐)\nnpm install -g websocat\nwebsocat wss:\u002F\u002Fapp.&lt;域名&gt;\u002Fws\n# 试着发:hello world\n# 期望:收到 echo: hello world\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>浏览器访问：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>https:\u002F\u002Fapp.&lt;域名&gt;\u002F\u003C\u002Fcode> → 应该看到展示站\u003C\u002Fli>\n\u003Cli>\u003Ccode>https:\u002F\u002Fapp.&lt;域名&gt;\u002Fws\u003C\u002Fcode> → WebSocket 连接成功\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr>\n\u003Ch2>六、故障排查\u003C\u002Fh2>\n\u003Ch3>6.1 浏览器报 \u003Ccode>HTTP 521\u003C\u002Fcode>\u003C\u002Fh3>\n\u003Cp>CF 连不上 VPS。\u003Cstrong>在 VPS 上\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">ss -tlnp | grep 443\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cul>\n\u003Cli>看不到任何 443 监听 → nginx 没起来 → \u003Ccode>systemctl restart nginx\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>看到其他服务在 443 → 杀掉那个服务后重启 nginx\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>6.2 浏览器报 \u003Ccode>HTTP 502\u003C\u002Fcode>\u003C\u002Fh3>\n\u003Cp>nginx 起来了但连不上后端：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">ss -tlnp | grep 10000\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>看不到 \u003Ccode>127.0.0.1:10000\u003C\u002Fcode> → 后端没在 10000 监听 → \u003Ccode>systemctl status wsapp\u003C\u002Fcode> 看日志\u003C\u002Fp>\n\u003Ch3>6.3 WebSocket 连接显示 Timeout\u003C\u002Fh3>\n\u003Cp>按顺序检查：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cp>\u003Cstrong>域名解析\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-powershell\">nslookup app.&lt;域名&gt;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>必须是 CF IP，不能是 VPS IP。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\u003Cp>\u003Cstrong>CF SSL 模式\u003C\u002Fstrong>：必须是 \u003Ccode>完整 (严格)\u003C\u002Fcode>，不能是 \u003Ccode>灵活\u003C\u002Fcode>。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\u003Cp>\u003Cstrong>CF WebSockets\u003C\u002Fstrong>：必须是 \u003Ccode>On\u003C\u002Fcode>。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\u003Cp>\u003Cstrong>nginx 配置\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">nginx -t\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>配置语法必须通过。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\u003Cp>\u003Cstrong>路径是否对\u003C\u002Fstrong>：客户端 \u003Ccode>\u002Fws\u003C\u002Fcode> 和 nginx \u003Ccode>location \u002Fws\u003C\u002Fcode> 必须完全一致。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3>6.4 nginx 启动报 \u003Ccode>bind() to 0.0.0.0:443 failed (98: Unknown error)\u003C\u002Fcode>\u003C\u002Fh3>\n\u003Cp>443 端口被占。先确认没有其他服务在 443：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">ss -tlnp | grep 443\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>看到其他服务 → 杀掉该服务 → \u003Ccode>systemctl restart nginx\u003C\u002Fcode>\u003C\u002Fp>\n\u003Ch3>6.5 展示站能访问但 \u003Ccode>\u002Fws\u003C\u002Fcode> 报 400\u003C\u002Fh3>\n\u003Cp>nginx 转发到后端的配置出问题：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>路径 \u003Ccode>\u002Fws\u003C\u002Fcode> 必须跟 nginx \u003Ccode>location \u002Fws\u003C\u002Fcode> 完全一致\u003C\u002Fli>\n\u003Cli>nginx 配置里 \u003Ccode>Connection &quot;upgrade&quot;\u003C\u002Fcode> 引号必须是英文双引号\u003C\u002Fli>\n\u003Cli>nginx 转发 HTTP\u002F1.1 给后端，后端必须支持 WebSocket 协议升级\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>6.6 Windows PowerShell \u003Ccode>curl\u003C\u002Fcode> 用不了\u003C\u002Fh3>\n\u003Cp>PowerShell 的 \u003Ccode>curl\u003C\u002Fcode> 是 \u003Ccode>Invoke-WebRequest\u003C\u002Fcode> 的别名，参数不一样。\u003Cstrong>用真 curl\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-powershell\">curl.exe -sI https:\u002F\u002Fapp.&lt;域名&gt;\u002F\ncurl.exe -s https:\u002F\u002Fapp.&lt;域名&gt;\u002F | findstr &quot;My Notes&quot;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>或装 Git for Windows 自带 \u003Ccode>curl.exe\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Ch3>6.7 后端服务意外停止\u003C\u002Fh3>\n\u003Cp>\u003Ccode>systemd\u003C\u002Fcode> 配置了 \u003Ccode>Restart=always\u003C\u002Fcode>，服务崩溃会自动重启。手动重启：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">systemctl restart wsapp\njournalctl -u wsapp -f    # 看实时日志\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2>七、日常维护\u003C\u002Fh2>\n\u003Ch3>7.1 看 WebSocket 流量\u003C\u002Fh3>\n\u003Cp>VPS 上：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">tail -f \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log | findstr \u002Fws\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>持续输出 \u003Ccode>GET \u002Fws HTTP\u002F1.1 101 ...\u003C\u002Fcode> 的日志。\u003Cstrong>\u003Ccode>101\u003C\u002Fcode> = WebSocket 升级成功\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Ch3>7.2 改展示站内容\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">nano \u002Fvar\u002Fwww\u002Fsite\u002Findex.html\nsystemctl reload nginx\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>7.3 加新 WebSocket 服务\u003C\u002Fh3>\n\u003Col>\n\u003Cli>部署新后端：端口 \u003Ccode>10001\u003C\u002Fcode>，监听 \u003Ccode>127.0.0.1\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>nginx 加新转发：\u003Cpre>\u003Ccode class=\"language-nginx\">location \u002Fws2 {\n    proxy_pass http:\u002F\u002F127.0.0.1:10001;\n    proxy_http_version 1.1;\n    proxy_set_header Upgrade $http_upgrade;\n    proxy_set_header Connection &quot;upgrade&quot;;\n    proxy_set_header Host $host;\n    proxy_set_header X-Real-IP $remote_addr;\n    proxy_read_timeout 3600s;\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fli>\n\u003Cli>\u003Ccode>systemctl reload nginx\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>客户端连接 \u003Ccode>wss:\u002F\u002Fapp.&lt;域名&gt;\u002Fws2\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3>7.4 重启服务\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">systemctl restart nginx\nsystemctl restart wsapp\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>7.5 防火墙\u003C\u002Fh3>\n\u003Cp>UFW 通常 inactive 即可，需要开：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">ufw allow 443\u002Ftcp\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>不需要\u003C\u002Fstrong>开 10000 端口的对外规则——后端只在 127.0.0.1 监听，公网访问会被内核直接拒绝。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Chr>\n\u003Ch2>八、关键点回顾\u003C\u002Fh2>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>关键点\u003C\u002Fth>\n\u003Cth>错误做法\u003C\u002Fth>\n\u003Cth>正确做法\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd>CF DNS 记录\u003C\u002Ftd>\n\u003Ctd>灰色云朵（DNS only）\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>橙色云朵（Proxied）\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>CF SSL 模式\u003C\u002Ftd>\n\u003Ctd>灵活\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>完整 (严格)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>后端监听地址\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>0.0.0.0:10000\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>\u003Ccode>127.0.0.1:10000\u003C\u002Fcode>\u003C\u002Fstrong>（永远不对外）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>CF Origin Cert\u003C\u002Ftd>\n\u003Ctd>用 Let&#39;s Encrypt\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>用 CF Origin Cert（15 年）\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>真实 IP 暴露\u003C\u002Ftd>\n\u003Ctd>DNS A 记录直接指向 VPS\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>必须走 CF Proxied 隐藏 IP\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>nginx 公网代理路径\u003C\u002Ftd>\n\u003Ctd>留空 Path\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>\u003Ccode>\u002Fws\u003C\u002Fcode> 走内部服务，\u003Ccode>\u002F\u003C\u002Fcode> 走展示站\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Windows curl\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>curl -sI URL\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>\u003Ccode>curl.exe -sI URL\u003C\u002Fcode>\u003C\u002Fstrong>（带 .exe 后缀）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>防火墙\u003C\u002Ftd>\n\u003Ctd>开放 10000 端口\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>不开放 10000，后端只在 127.0.0.1\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Chr>\n\u003Ch2>九、最终目录结构\u003C\u002Fh2>\n\u003Cpre>\u003Ccode>VPS 文件结构:\n\u002Fetc\u002Fssl\u002Fcf\u002F\n├── cert.pem              # CF Origin 证书\n└── key.pem               # CF Origin 私钥\n\u002Fvar\u002Fwww\u002Fsite\u002F\n├── index.html            # 防探测展示站\n└── robots.txt\n\u002Fetc\u002Fnginx\u002Fconf.d\u002F\n└── app.conf              # nginx 反代配置\n\u002Fopt\u002Fwsapp\u002F\n├── package.json          # Node.js 依赖\n└── server.mjs            # WebSocket 后端服务\n\u002Fetc\u002Fsystemd\u002Fsystem\u002F\n└── wsapp.service         # systemd 服务管理\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2>十、写在最后\u003C\u002Fh2>\n\u003Cp>整套方案的核心思路：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>永远不要直连 VPS IP\u003C\u002Fstrong>（被扫描器收录、指纹识别、被定向攻击是必然的）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>所有公网流量套上 Cloudflare CDN\u003C\u002Fstrong>（隐藏 IP + 全球加速）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>业务服务只跑在 127.0.0.1\u003C\u002Fstrong>（公网完全不可达，攻击者即便知道 VPS IP 也连不到）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>nginx 终结 TLS + 转发到内部\u003C\u002Fstrong>（端口扫描器看到的是静态博客）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>展示站 + 业务服务并行\u003C\u002Fstrong>（同一域名不同路径，让流量看起来&quot;正常&quot;）\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>这样就实现了：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>VPS 真实 IP 永不暴露 ✅\u003C\u002Fli>\n\u003Cli>全球任何地区用户访问都很快（CDN 加速）✅\u003C\u002Fli>\n\u003Cli>端口扫描器看到的 443 端口是普通博客 ✅\u003C\u002Fli>\n\u003Cli>业务服务（WebSocket \u002F API）只在内部运行 ✅\u003C\u002Fli>\n\u003Cli>日常维护只需要改 nginx + 重启后端服务 ✅\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr>\n\u003Ch2>常见问题 FAQ\u003C\u002Fh2>\n\u003Ch3>为什么 VPS 一上线就被扫描？\u003C\u002Fh3>\n\u003Cp>VPS 的 IP 段通常被\u003Cstrong>商业扫描器\u003C\u002Fstrong>（Shodan、Censys、ZoomEye、BinaryEdge）\u003Cstrong>全网段持续扫描\u003C\u002Fstrong>。新分配的 IP 上线后\u003Cstrong>几小时内\u003C\u002Fstrong>就会被收录到这些搜索引擎的数据库。\u003Cstrong>这不是有人针对你，是自动化的全网扫描\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>解决方法\u003C\u002Fstrong>：让真实 IP 不出现在 DNS 里（CF Proxied 模式），扫描器查不到 IP，就无从扫描。\u003C\u002Fp>\n\u003Ch3>Cloudflare 免费版够用吗？\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>够用\u003C\u002Fstrong>。本方案用的就是 CF 免费版：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>DNS 解析：免费\u003C\u002Fli>\n\u003Cli>CDN 加速：免费（全球 300+ 节点）\u003C\u002Fli>\n\u003Cli>Origin Certificate：免费 15 年有效期\u003C\u002Fli>\n\u003Cli>不限流量（公平使用政策）\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>唯一限制：免费版\u003Cstrong>不支持 Spectrum（TCP\u002FUDP 转发）\u003C\u002Fstrong>，但 WebSocket 走 HTTPS 443 端口完全够用。\u003C\u002Fp>\n\u003Ch3>一定要用 RackNerd VPS 吗？其他 VPS 行不行？\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>可以\u003C\u002Fstrong>。本方案对 VPS 厂商没有要求，只要满足：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>系统：Ubuntu 20.04+ \u002F Debian 11+ \u002F CentOS 7+\u003C\u002Fli>\n\u003Cli>能 SSH 登录\u003C\u002Fli>\n\u003Cli>公网带宽 ≥ 1Mbps\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>推荐 VPS 厂商\u003C\u002Fstrong>（按性价比）：RackNerd、CloudCone、BandwagonHost（搬瓦工）、HostDare、Vultr（按小时计费）。\u003C\u002Fp>\n\u003Ch3>nginx &quot;防探测&quot;展示站有什么作用？\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>防指纹识别\u003C\u002Fstrong>。全球端口扫描器会对 Cloudflare CDN 后面的 IP 发起 HTTPS 探测请求：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>如果 443 端口返回的是 \u003Cstrong>WebSocket 服务特征\u003C\u002Fstrong> → 标记为&quot;可疑服务&quot;，被收录到公开数据库\u003C\u002Fli>\n\u003Cli>如果 443 端口返回的是 \u003Cstrong>普通博客 \u002F 静态站\u003C\u002Fstrong> → 当作正常网站，不被关注\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>nginx 展示站就是让&quot;非业务访问者&quot;看到的是博客，业务访问者访问 \u003Ccode>\u002Fws\u003C\u002Fcode> 路径才走内部服务。\u003Cstrong>和正常网站无差别，扫描器无法识别\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Ch3>业务面板要不要暴露到公网？\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>绝对不要\u003C\u002Fstrong>。任何管理面板（数据库管理、监控、运维工具）必须：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>监听 \u003Ccode>127.0.0.1\u003C\u002Fcode>（仅本机访问）\u003C\u002Fli>\n\u003Cli>端口改非标准（如 \u003Ccode>2053\u003C\u002Fcode>、\u003Ccode>31456\u003C\u002Fcode>）\u003C\u002Fli>\n\u003Cli>设置强密码 + 改默认用户名\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>通过 SSH 端口转发访问：\u003Ccode>ssh -L 2053:127.0.0.1:2053 root@VPS_IP\u003C\u002Fcode>，然后浏览器开 \u003Ccode>http:\u002F\u002F127.0.0.1:2053\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Ch3>这套架构安全吗？\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>对常见威胁（端口扫描、指纹识别、IP 暴露、DDoS 基础防护）非常有效\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>VPS IP 永远不暴露 → 扫描难度极大\u003C\u002Fli>\n\u003Cli>展示站防探测 → 业务指纹不泄露\u003C\u002Fli>\n\u003Cli>Cloudflare CDN 缓冲 → 真实流量被分散到 CF 全球节点\u003C\u002Fli>\n\u003Cli>业务服务 127.0.0.1 监听 → 即便知道 IP 也连不到\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>降低风险的额外建议\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>在 VPS 上做合规业务\u003C\u002Fli>\n\u003Cli>服务不要对外大量分享\u003C\u002Fli>\n\u003Cli>定期轮换 Origin 证书和 WebSocket 路径\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>一个域名能部署多个服务吗？\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>可以\u003C\u002Fstrong>。在 Cloudflare DNS 里加多个 A 记录（如 \u003Ccode>app1\u003C\u002Fcode>、\u003Ccode>app2\u003C\u002Fcode>、\u003Ccode>app3\u003C\u002Fcode>），都指向同一 VPS IP（都走 Proxied）。每个子域配不同 nginx \u003Ccode>server_name\u003C\u002Fcode>，分别反向代理到不同的内部端口：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>app1.&lt;域名&gt;\u003C\u002Fcode> → \u003Ccode>127.0.0.1:10000\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode>app2.&lt;域名&gt;\u003C\u002Fcode> → \u003Ccode>127.0.0.1:10001\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode>app3.&lt;域名&gt;\u003C\u002Fcode> → \u003Ccode>127.0.0.1:10002\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>每个子域独立管理，互不影响。\u003C\u002Fp>\n\u003Ch3>Origin 证书和 Let&#39;s Encrypt 有什么区别？\u003C\u002Fh3>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>维度\u003C\u002Fth>\n\u003Cth>Let&#39;s Encrypt\u003C\u002Fth>\n\u003Cth>CF Origin Cert\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd>签发方\u003C\u002Ftd>\n\u003Ctd>Let&#39;s Encrypt CA\u003C\u002Ftd>\n\u003Ctd>Cloudflare CA\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>有效期\u003C\u002Ftd>\n\u003Ctd>90 天\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>15 年\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>自动续签\u003C\u002Ftd>\n\u003Ctd>✅\u003C\u002Ftd>\n\u003Ctd>❌（但 15 年几乎等于永久）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>验证方式\u003C\u002Ftd>\n\u003Ctd>公网验证\u003C\u002Ftd>\n\u003Ctd>Cloudflare 内部验证\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>浏览器访问\u003C\u002Ftd>\n\u003Ctd>✅\u003C\u002Ftd>\n\u003Ctd>❌（仅 CF 回源用）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>\u003Cstrong>本场景必须用 CF Origin Cert\u003C\u002Fstrong>：浏览器访问经过 CF CF，CF 验证 Origin 证书即可，不需要公网信任的 CA 证书。\u003C\u002Fp>\n\u003Ch3>如何监控整个架构的健康度？\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 1. 看 CF → VPS 链路\nsystemctl status nginx\n\n# 2. 看后端服务\nsystemctl status wsapp\njournalctl -u wsapp --since &quot;1 hour ago&quot;\n\n# 3. 看 WebSocket 流量\ntail -f \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log | findstr \u002Fws\n\n# 4. 看 443 端口\nss -tlnp | grep 443\n\n# 5. 看后端端口\nss -tlnp | grep 10000\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>进阶可以用 Prometheus + Grafana 做监控大盘，但小项目上面这些命令够用。\u003C\u002Fp>\n\u003Chr>\n\u003Cblockquote>\n\u003Cp>本文只做技术方案分享，所有配置均为公开的 Cloudflare + nginx 标准用法。请在合法范围内使用，部署合规业务。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n","3 分钟摘要： - VPS 真实 IP 暴露后，会被扫描器 24\u002F7 探测，443 端口指纹泄露 - 用 Cloudflare CDN 隐藏真实 VPS IP（免费方案） - 真实 WebSocket \u002F API 服务跑在 127.0.0.1 的非标准端口，永不对外暴露 - nginx 终结 TLS",[22,25,28,31,34,37,40,43,46],{"question":23,"answer":24},"为什么 VPS 一上线就被扫描？","VPS 的 IP 段通常被商业扫描器（Shodan、Censys、ZoomEye、BinaryEdge）全网段持续扫描。新分配的 IP 上线后几小时内就会被收录到这些搜索引擎的数据库。这不是有人针对你，是自动化的全网扫描。\n解决方法：让真实 IP 不出现在 DNS 里（CF Proxied 模式），扫描器查不到 IP，就无从扫描。",{"question":26,"answer":27},"Cloudflare 免费版够用吗？","够用。本方案用的就是 CF 免费版：\nDNS 解析：免费\nCDN 加速：免费（全球 300+ 节点）\nOrigin Certificate：免费 15 年有效期\n不限流量（公平使用政策）\n唯一限制：免费版不支持 Spectrum（TCP\u002FUDP 转发），但 WebSocket 走 HTTPS 443 端口完全够用。",{"question":29,"answer":30},"一定要用 RackNerd VPS 吗？其他 VPS 行不行？","可以。本方案对 VPS 厂商没有要求，只要满足：\n系统：Ubuntu 20.04+ \u002F Debian 11+ \u002F CentOS 7+\n能 SSH 登录\n公网带宽 ≥ 1Mbps\n推荐 VPS 厂商（按性价比）：RackNerd、CloudCone、BandwagonHost（搬瓦工）、HostDare、Vultr（按小时计费）。",{"question":32,"answer":33},"nginx \"防探测\"展示站有什么作用？","防指纹识别。全球端口扫描器会对 Cloudflare CDN 后面的 IP 发起 HTTPS 探测请求：\n如果 443 端口返回的是 WebSocket 服务特征 → 标记为\"可疑服务\"，被收录到公开数据库\n如果 443 端口返回的是 普通博客 \u002F 静态站 → 当作正常网站，不被关注\nnginx 展示站就是让\"非业务访问者\"看到的是博客，业务访问者访问 \u002Fws 路径才走内部服务。和正常网站无差别，扫描器无法识别。",{"question":35,"answer":36},"业务面板要不要暴露到公网？","绝对不要。任何管理面板（数据库管理、监控、运维工具）必须：\n监听 127.0.0.1（仅本机访问）\n端口改非标准（如 2053、31456）\n设置强密码 + 改默认用户名\n通过 SSH 端口转发访问：ssh -L 2053:127.0.0.1:2053 root@VPS_IP，然后浏览器开 http:\u002F\u002F127.0.0.1:2053。",{"question":38,"answer":39},"这套架构安全吗？","对常见威胁（端口扫描、指纹识别、IP 暴露、DDoS 基础防护）非常有效：\nVPS IP 永远不暴露 → 扫描难度极大\n展示站防探测 → 业务指纹不泄露\nCloudflare CDN 缓冲 → 真实流量被分散到 CF 全球节点\n业务服务 127.0.0.1 监听 → 即便知道 IP 也连不到\n降低风险的额外建议：\n在 VPS 上做合规业务\n服务不要对外大量分享\n定期轮换 Origin 证书和 WebSocket 路径",{"question":41,"answer":42},"一个域名能部署多个服务吗？","可以。在 Cloudflare DNS 里加多个 A 记录（如 app1、app2、app3），都指向同一 VPS IP（都走 Proxied）。每个子域配不同 nginx server_name，分别反向代理到不同的内部端口：\napp1.\u003C域名> → 127.0.0.1:10000\napp2.\u003C域名> → 127.0.0.1:10001\napp3.\u003C域名> → 127.0.0.1:10002\n每个子域独立管理，互不影响。",{"question":44,"answer":45},"Origin 证书和 Let's Encrypt 有什么区别？","| 维度 | Let's Encrypt | CF Origin Cert |\n|---|---|---|\n| 签发方 | Let's Encrypt CA | Cloudflare CA |\n| 有效期 | 90 天 | 15 年 |\n| 自动续签 | ✅ | ❌（但 15 年几乎等于永久） |\n| 验证方式 | 公网验证 | Cloudflare 内部验证 |\n| 浏览器访问 | ✅ | ❌（仅 CF 回源用） |\n本场景必须用 CF Origin Cert：浏览器访问经过 CF CF，CF 验证 Origin 证书即可，不需要公网信任的 CA 证书。",{"question":47,"answer":48},"如何监控整个架构的健康度？","``bash\n1. 看 CF → VPS 链路\nsystemctl status nginx\n2. 看后端服务\nsystemctl status wsapp\njournalctl -u wsapp --since \"1 hour ago\"\n3. 看 WebSocket 流量\ntail -f \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log | findstr \u002Fws\n4. 看 443 端口\nss -tlnp | grep 443\n5. 看后端端口\nss -tlnp | grep 10000\n``\n进阶可以用 Prometheus + Grafana 做监控大盘，但小项目上面这些命令够用。\n---\n> 本文只做技术方案分享，所有配置均为公开的 Cloudflare + nginx 标准用法。请在合法范围内使用，部署合规业务。","cdn-anti-scan-vps-architecture","\u002Fwriting\u002Fcdn-anti-scan-vps-architecture\u002F",[52,71,73,87,98,110,120,132,143],{"id":53,"title":54,"date":55,"category":56,"readTime":57,"tags":58,"_excerpt":69,"_path":70},"kan-da-zuo-xiao-futures-trading","期货「看大做小」趋势交易法：从前辈点拨到实操悟道的完整路径（日线 + 30分钟 + RSI 临界点入场）","2026-06-18","交易心得","9 分钟",[59,60,61,62,63,64,65,66,67,68],"期货交易","看大做小","多周期共振","趋势交易","日线","30分钟","5分钟","RSI指标","入场点","顺势交易","3 分钟摘要： - 看大做小 = 大周期定方向 + 小周期找入场 + RSI 等临界点 - 周期组合按交易风格选：波段 = 日线 + 30分钟 \u002F 5分钟（本文重点） - 大周期判方向三件套：均线排列、高低点结构、关键支撑阻力 - 小周期入场三件套：回调企稳、K 线反转形态、RSI 临界点 - 越是","\u002Fwriting\u002Fkan-da-zuo-xiao-futures-trading\u002F",{"id":49,"title":4,"date":5,"category":6,"readTime":7,"tags":72,"_excerpt":20,"_path":50},[9,10,11,12,13,14,15,16,17,18],{"id":74,"title":75,"date":76,"category":77,"readTime":78,"tags":79,"_excerpt":85,"_path":86},"telegram-86-login-recovery","86 手机号无法登录 Telegram 终极解决方案：绕过付费验证码 + 旧设备验证找回账号","2026-06-14","工具教程","8 分钟",[80,81,82,83,84],"Telegram","86手机号","账号找回","二次验证","短信收费","3 分钟摘要： - 86 号码 + 邮箱 + 二次验证密码 = 找回账号的三要素，缺一不可 - 用第三方客户端 Telega（绕过 Google Play 校验版本）登录，触发邮箱 + 2FA 通道 - 登录成功后再用官方 Telegram 登录、更新到最新版本 - 整个过程不需要原手机短信、不需要","\u002Fwriting\u002Ftelegram-86-login-recovery\u002F",{"id":88,"title":89,"date":90,"category":91,"readTime":78,"tags":92,"_excerpt":96,"_path":97},"indie-developer-journey","从零到一：我的独立开发之路","2026-05-15","创业人生",[93,94,95],"独立开发","创业","个人成长","2023年初，我做出了一个重要决定：离开稳定的工作，成为一名独立开发者。这个决定并非一时冲动，而是经过深思熟虑的结果。","\u002Fwriting\u002Findie-developer-journey\u002F",{"id":99,"title":100,"date":101,"category":102,"readTime":103,"tags":104,"_excerpt":108,"_path":109},"vue3-composition-api","Vue 3 Composition API 完全指南","2026-04-28","技术教程","12 分钟",[105,106,107],"Vue","JavaScript","前端","Composition API 是 Vue 3 引入的一组基于函数的 API，它允许我们使用函数来组织组件逻辑，而不是选项对象。","\u002Fwriting\u002Fvue3-composition-api\u002F",{"id":111,"title":112,"date":113,"category":102,"readTime":7,"tags":114,"_excerpt":118,"_path":119},"react-native-performance","React Native 性能优化实战","2026-04-15",[115,116,117],"React Native","性能","移动端","在开发 React Native 应用时，我们经常会遇到以下性能问题：","\u002Fwriting\u002Freact-native-performance\u002F",{"id":121,"title":122,"date":123,"category":56,"readTime":124,"tags":125,"_excerpt":130,"_path":131},"futures-trading-seven-rules","期货交易的七条铁律：从亏损到稳定盈利的思考","2026-03-20","5 分钟",[59,56,126,127,128,129],"交易思维","止损","趋势跟踪","心法","做期货这几年，踩过的坑、爆过的仓、熬过的夜，最终都沉淀成这七条心得。期货交易本质上不是预测行情，而是管理自己。以下每一条都是真金白银换来的，建议反复读、慢慢悟。","\u002Fwriting\u002Ffutures-trading-seven-rules\u002F",{"id":133,"title":134,"date":135,"category":136,"readTime":124,"tags":137,"_excerpt":141,"_path":142},"git-workflow-best-practices","Git 工作流最佳实践","2026-03-10","开发工具",[138,139,140],"Git","团队协作","版本控制","这是一个经典的分支模型：","\u002Fwriting\u002Fgit-workflow-best-practices\u002F",{"id":144,"title":145,"date":146,"category":102,"readTime":147,"tags":148,"_excerpt":151,"_path":152},"typescript-tips","TypeScript 实用技巧","2026-02-25","7 分钟",[149,107,150],"TypeScript","编程技巧","使用类型守卫来缩小类型范围：","\u002Fwriting\u002Ftypescript-tips\u002F",1781768629597]