本文约 4500 字,阅读时间 12 分钟
适合:需要调用微信公众号 API、企业微信 API、或任何需要 IP 白名单的服务,但本地是家庭宽带动态 IP 的开发者
用一台 30 元 / 月的固定 IP 云服务器做 SSH 隧道 + Squid 正向代理,把所有调用微信 API 的请求从这台服务器出口,白名单精确到 /32(一个 IP),从此动态 IP 无忧。
你想用脚本自动发布微信公众号文章。微信公众号后台要求:调用 API 必须先把你的服务器 IP 加到白名单。
打开后台一看,白名单输入框只接受 单个 IP(如 1.2.3.4),或者 带掩码的 IP 段(如 1.2.3.0/24)。
你查一下自己的出口 IP:
curl ifconfig.me
把这个 IP 加进去。第二天再发,提示:
{"errcode":40164,"errmsg":"invalid ip xxx.xxx.xxx.xxx, not in whitelist"}
家庭宽带 IP 一夜之间变了。
国内家庭宽带(电信、联通、移动)默认都是 动态 IP,通常每 24 小时或重启光猫时就会变化。运营商出口 IP 段非常大:
- 广东电信:
14.153.0.0/16、183.23.0.0/16等数十个段 - 总共可能轮换数千个 IP
| 方案 | 问题 |
|---|---|
| 加整段 /16 | 白名单单条记录最大支持 /24,且把 6.5 万个 IP 全加进白名单 = 等于没有白名单 |
| 每天手动改白名单 | 你愿意每天爬起来改?而且微信白名单 24 小时只能修改 N 次 |
| 申请运营商固定 IP | 家用宽带不给办,企业宽带 300+/ 月 |
| VPN 回家 | VPN 服务器也得有固定 IP,绕了一圈回到原点 |
既然家庭 IP 会变,那就 借一个永远不变的 IP当跳板:
┌──────────────┐ SSH 加密隧道 ┌─────────────────┐ HTTP 请求 ┌──────────────────┐
│ 你的电脑 │ ──────────────────> │ 固定 IP 云服务器 │ ──────────────> │ 微信公众号 API │
│ (动态 IP) │ 端口转发 3128 │ (Squid 代理) │ 出口 = 固定 IP │ (白名单已加固定 IP)│
└──────────────┘ └─────────────────┘ └──────────────────┘
| 角色 | 比喻 |
|---|---|
| 你的电脑 | 游击队(住址经常变) |
| 固定 IP 服务器 | 固定联络站(永远在同一个门牌号) |
| 微信公众号 API | 大门守卫(只认联络站的门牌号) |
游击队想进大门?先到联络站集合,由联络站统一带进去。
| 组件 | 作用 | 替代方案 |
|---|---|---|
| SSH 隧道 | 把本地端口安全加密转发到服务器 | WireGuard、frp、ngrok |
| Squid | 在服务器上充当 HTTP/HTTPS 正向代理 | tinyproxy、nginx forward proxy |
选 SSH + Squid 的理由:
- 零运维:SSH 是系统自带,Squid 一条命令装好
- 零成本:除了服务器,没有任何软件费用
- 强加密:SSH 全链路 AES 加密
- 可观测:Squid 有详细访问日志
推荐配置:
| 服务商 | 配置 | 价格 | 推荐度 |
|---|---|---|---|
| 阿里云轻量 | 2 核 2G/3M 带宽 / 华东 | ¥24/ 月 | ⭐⭐⭐⭐⭐ |
| 腾讯云轻量 | 2 核 2G/4M 带宽 / 上海 | ¥28/ 月 | ⭐⭐⭐⭐⭐ |
| 华为云 | 2 核 2G/1M 带宽 | ¥35/ 月 | ⭐⭐⭐ |
关键要求:
- ✅ 必须是 固定公网 IP(轻量应用服务器默认就是固定 IP)
- ✅ 系统选 Ubuntu 22.04 或 Debian 12
- ✅ 地域选国内(华东 / 华南),访问微信 API 延迟最低
购买后记下:
服务器 IP:xxx.xxx.xxx.xxx
登录密码:xxxxxxxx
SSH 端口:22
💡 避坑:不要买 ECS 包年包月(贵),轻量应用服务器(Lighthouse)就够了。也可以蹲首年活动,阿里云常有 ¥99/ 年(合 ¥8/ 月)。
2.1 本地生成 SSH 密钥(已有的可跳过):
ssh-keygen -t ed25519 -C "wechat-proxy" -f ~/.ssh/wechat_proxy
一路回车,不设密码(方便自动化重连)。
2.2 上传公钥到服务器:
ssh-copy-id -i ~/.ssh/wechat_proxy.pub root@你的服务器 IP
2.3 配置本地 SSH 别名,编辑 ~/.ssh/config:
Host wechat-proxy
HostName 你的服务器 IP
User root
IdentityFile ~/.ssh/wechat_proxy
ServerAliveInterval 60
ServerAliveCountMax 3
ServerAliveInterval 60 是关键:每 60 秒发一个心跳包,防止运营商 NAT 超时断连。
2.4 测试连接:
ssh wechat-proxy
能免密直接登录就成功。
登录服务器后执行:
apt update && apt install -y squid
cp /etc/squid/squid.conf /etc/squid/squid.conf.bak
cat > /etc/squid/squid.conf << 'EOF'
http_port 127.0.0.1:3128
acl localnet src 127.0.0.1/32
acl SSL_ports port 443
acl Safe_ports port 80
acl Safe_ports port 443
acl CONNECT method CONNECT
http_access allow localnet
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access deny all
forwarded_for delete
via off
request_header_access X-Forwarded-For deny all
access_log /var/log/squid/access.log
coredump_dir /var/spool/squid
EOF
systemctl restart squid
systemctl enable squid
ss -tlnp | grep 3128
关键安全设置说明:
http_port 127.0.0.1:3128:只在 localhost 监听,外网根本碰不到这个端口acl localnet src 127.0.0.1/32:只允许本地访问forwarded_for delete:删掉X-Forwarded-For头,微信看不出你在用代理
4.1 手动测试:
ssh -N -L 3128:127.0.0.1:3128 wechat-proxy &
这条命令的含义:把 本地 3128 端口 → 通过 SSH 加密 → 转发到 服务器的 127.0.0.1:3128(也就是 Squid)。
4.2 验证代理是否生效:
curl https://api.ipify.org
curl -x http://127.0.0.1:3128 https://api.ipify.org
第二条返回云服务器 IP,说明 ✅ 链路打通。
4.3 设置开机自启(推荐用 systemd 用户服务,断了自动重连):
创建 ~/.config/systemd/user/wechat-tunnel.service:
[Unit]
Description=WeChat API SSH Tunnel
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/ssh -N -L 3128:127.0.0.1:3128 wechat-proxy
Restart=always
RestartSec=10
[Install]
WantedBy=default.target
启用:
systemctl --user daemon-reload
systemctl --user enable --now wechat-tunnel
systemctl --user status wechat-tunnel
如果想让服务在用户未登录时也跑:
sudo loginctl enable-linger $USER
5.1 在微信公众号后台加白名单:
- 登录 mp.weixin.qq.com
- 设置与开发 → 基本配置 → IP 白名单
- 添加 你的云服务器 IP(单个 IP,/32 精度)
5.2 修改脚本走代理:
如果是 Node.js / Bun 用 fetch:
import {ProxyAgent, setGlobalDispatcher} from 'undici'
const proxyAgent = new ProxyAgent('http://127.0.0.1:3128')
setGlobalDispatcher(proxyAgent)
// 后续所有 fetch 自动走代理
const res = await fetch('https://api.weixin.qq.com/cgi-bin/token?...')
如果是 Python 用 requests:
import requests
proxies = {
'http': 'http://127.0.0.1:3128',
'https': 'http://127.0.0.1:3128',
}
r = requests.get('https://api.weixin.qq.com/cgi-bin/token', proxies=proxies)
如果是 shell 脚本:
export https_proxy=http://127.0.0.1:3128
export http_proxy=http://127.0.0.1:3128
curl https://api.weixin.qq.com/cgi-bin/token?...
5.3 测试发布一篇文章:
调一下获取 access_token 的接口,能正常返回就 ✅ 全链路通了。
逐项打勾,全部通过即上线就绪:
- [] 云服务器已购买,固定 IP 已记录
- [] SSH 免密登录成功(
ssh wechat-proxy不需要输密码) - [] Squid 在服务器
127.0.0.1:3128监听 - [] 本地 SSH 隧道开机自启(systemctl status 显示 active)
- []
curl -x http://127.0.0.1:3128 https://api.ipify.org返回服务器 IP - [] 微信公众号后台已加白名单
- [] 脚本走代理成功调用微信 API
症状:跑半天后 curl 报 Connection refused。
原因:运营商 NAT 超时回收连接。
解决:
~/.ssh/config加ServerAliveInterval 60- systemd 服务设
Restart=always,断了自动拉起
症状:systemctl status squid 显示 failed。
排查:
journalctl -u squid --no-pager | tail -50
squid -k parse
症状:curl -x 卡住没响应。
排查:
tail -f /var/log/squid/access.log
如果 Squid 收到请求但转发慢,是云服务器到微信 API 的网络问题,换地域。
症状:服务器 CPU 飙升,看日志全是暴力破解 SSH。
解决:
vi /etc/ssh/sshd_config
apt install -y fail2ban
systemctl enable --now fail2ban
记得云厂商安全组同步开放新端口、关闭 22。
症状:除了微信,企业微信、飞书、钉钉也需要白名单。
方案:
- 方案 A(推荐):所有项目共用这一台代理服务器,把这一个 IP 加到每个平台的白名单
- 方案 B:每个平台开一条独立 SSH 隧道(不同本地端口)
| 项 | 月成本 | 年成本 |
|---|---|---|
| 阿里云轻量 2 核 2G | ¥24 | ¥288(活动价 ¥99) |
| Squid + SSH 软件 | ¥0 | ¥0 |
| 维护时间 | 几乎为 0 | 几乎为 0 |
| 合计 | ¥24 | ¥99 ~ ¥288 |
相比每月手动改白名单浪费的时间,这点钱真不算什么。
这套架构搭好后还能干很多事:
- 作为爬虫出口:固定 IP 反爬虫友好,不容易被封
- 作为公司统一出口:团队多人共用同一出口 IP
- 作为 GitHub/Docker 镜像加速:服务器拉取国外资源更快
- 作为反向跳板:通过这台服务器 SSH 到内网机器
- ❌ 不要用 Squid 做出墙代理(违规)
- ❌ 不要在 Squid 上开公网认证(容易被扫破)
- ❌ 不要把服务器密码写进脚本(一定用 SSH 密钥)
这套方案的本质是:用一个 ” 位置固定 ” 的中间人,解决 ” 位置漂移 ” 的问题。
类似的思路在很多场景都能用:
- 家庭宽带反向暴露服务 → frp 内网穿透(同样原理)
- 多云多地域统一出口 → 全球加速 / SD-WAN(企业级版本)
- AI Agent 调用海外 API → 海外 VPS 中转(同样架构)
INTP 风格的原则:用一次性的工程投入(搭建中转),消除每天重复的运维负担(手动改白名单)。30 元 / 月,换一辈子省心。
本文方案已在生产环境稳定运行。如果你也被 IP 白名单折腾过,欢迎评论区交流踩过的坑。
相关阅读:
- 微信公众号 API 文档:https://developers.weixin.qq.com/doc/
- Squid 官方文档:http://www.squid-cache.org/Doc/
- SSH 隧道完全指南:
man ssh+-L / -R / -D参数