让 Spark Mail 能用 163 邮箱。
163 邮箱有个非标准要求:客户端 LOGIN 成功后必须立刻发送 ID 命令自报身份(RFC 2971),否则 163 返回 Unsafe Login 并断开。Spark 没有为 163 实现这个特殊握手,所以加 163 账号必失败。
本工具在你的服务器上跑一个 IMAP 代理:监听 :993,上游连 imap.163.com,检测到 LOGIN OK 后自动替 Spark 注入 ID 命令(伪装成 Foxmail),其余字节透传。
- 一台 Linux 服务器(本指南假设 Ubuntu 22.04+ 或 Debian 12+),有 root 权限
- 一个你拥有的域名,下文以
mail.example.com为例 - 服务器需要能从公网访问 80 和 993 端口(80 仅证书申请/续期时短暂占用)
如果你用宝塔面板 / nginx / Apache 等已经占着 80 端口的服务,先临时停掉它们,否则证书申请会失败。
sudo lsof -i:80应该没有任何输出。
到你的 DNS 服务商那里,给 mail.example.com 加一条 A 记录指向服务器公网 IP。
如果用 Cloudflare 托管:在 DNS 页面,记录右侧的橙色云朵图标点一下让它变成灰色(DNS only),然后保存。否则 Cloudflare 会拦截 993 端口让连接永远失败。
服务器系统自带 ufw 的话:
sudo ufw allow 80/tcp
sudo ufw allow 993/tcp云厂商(阿里云 / 腾讯云 / AWS / DMIT 等)还要在网页控制台的"安全组"里放行 80 和 993,不然系统层放了也没用。
curl -fsSL https://raw.githubusercontent.com/jizhi0v0/163-imap-proxy/main/install.sh -o install.sh
sudo sh install.sh脚本会问你选 systemd 还是 Docker——不确定就选 1(systemd)。安装完成后服务会自动启动。
把下面整段作为一个整体复制到终端(先把 mail.example.com 改成你自己的域名):
DOMAIN=mail.example.com
sudo apt update && sudo apt install -y certbot
sudo certbot certonly --standalone -d $DOMAIN --agree-tos --register-unsafely-without-email --non-interactive
sudo cp /etc/letsencrypt/live/$DOMAIN/fullchain.pem /etc/163-wrapper/cert.pem
sudo cp /etc/letsencrypt/live/$DOMAIN/privkey.pem /etc/163-wrapper/key.pem
sudo chmod 600 /etc/163-wrapper/key.pem
sudo systemctl restart 163-wrapper
sudo tee /etc/letsencrypt/renewal-hooks/deploy/163-wrapper.sh > /dev/null <<EOF
#!/bin/sh
cp /etc/letsencrypt/live/$DOMAIN/fullchain.pem /etc/163-wrapper/cert.pem
cp /etc/letsencrypt/live/$DOMAIN/privkey.pem /etc/163-wrapper/key.pem
chmod 600 /etc/163-wrapper/key.pem
systemctl restart 163-wrapper
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/163-wrapper.shDocker 模式用户:把
systemctl restart 163-wrapper两处都换成docker restart 163-wrapper。
echo | openssl s_client -connect mail.example.com:993 -servername mail.example.com 2>&1 | grep "Verify return code"看到 Verify return code: 0 (ok) 就成功了。
展开查看(注意:Tailscale 路径会失去 Spark 的 APNs 后台推送,且会反复弹"无法连接服务器"横幅。**先读完末尾的"权衡"小节再决定是否走这条路**)
不想买域名、不想暴露公网 993 端口的话,可以走 Tailscale。每台用邮件的设备(手机、电脑)也装 Tailscale 登同一账号即可——这样你跳过上面的 1、2、4 步,但仍然能拿到真正的 Let's Encrypt 证书(Tailscale 通过 DNS-01 帮你签)。
服务器:
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up按提示登录账号。Mac / iOS / Windows 直接装 Tailscale App 登同一账号。
https://login.tailscale.com/admin/dns → 找到 "HTTPS Certificates" → 点 Enable HTTPS。同一页面顶部能看到你的 tailnet 名,形如 tail-xxxxxx.ts.net。
服务器主机名假设是 mailproxy(tailscale status 第一行能看到),在服务器上执行:
sudo tailscale cert mailproxy.tail-xxxxxx.ts.net
sudo cp mailproxy.tail-xxxxxx.ts.net.crt /etc/163-wrapper/cert.pem
sudo cp mailproxy.tail-xxxxxx.ts.net.key /etc/163-wrapper/key.pem
sudo chmod 600 /etc/163-wrapper/key.pem
sudo systemctl restart 163-wrapper证书 90 天有效期,加个 cron 自动续(注意文件名不能带后缀,Debian/Ubuntu 的 run-parts 会跳过含 . 的文件名):
sudo tee /etc/cron.monthly/163-wrapper-cert > /dev/null <<'EOF'
#!/bin/sh
set -e
cd /etc/163-wrapper
tailscale cert mailproxy.tail-xxxxxx.ts.net
mv mailproxy.tail-xxxxxx.ts.net.crt cert.pem
mv mailproxy.tail-xxxxxx.ts.net.key key.pem
chmod 600 key.pem
systemctl restart 163-wrapper
EOF
sudo chmod +x /etc/cron.monthly/163-wrapper-cert跟下文一样,把 IMAP Server 填成 mailproxy.tail-xxxxxx.ts.net,其他不变。设备只要连着 Tailscale,邮件就能正常收发。
Spark 的"新邮件通知"实际上有两条独立路径:
| 场景 | 通知来源 | 是否需要公网可达 |
|---|---|---|
| App 在前台 / 最近用过(系统还没挂起 App) | 设备本地 IMAP IDLE 监听 wrapper,新邮件直接弹本地通知 | ❌ Tailscale / 局域网都行 |
| App 长时间后台 / 完全退出(iOS 已把 App 挂起) | 必须由 Readdle 云端自己跑 IMAP IDLE → 经 APNs 唤醒 App | ✅ Readdle 服务器必须能从公网连到 wrapper |
也就是说,Tailscale 路线下你前台用 Spark 时一切正常,但 iOS 把 App 完全挂起后只能等你下次打开手动刷一下才看到新邮件。这个限制对任何非公网部署都成立(局域网 / 家庭服务器 / Tailscale 都一样)——APNs 推送的本质就是 Readdle 云端必须能 IDLE 你的 IMAP 服务器。
只有"公网 VPS + 域名 + LE 证书"那条路能让 Readdle 云端连到 wrapper、保留 APNs 推送,代价是 163 授权码会被存到他们机房用于维持长连接(Readdle 官方说明)。
按你对离线推送的依赖度自己选:
- 必须随时收到新邮件提醒 → 公网 VPS 路径
- 接受"打开 App 才同步" → Tailscale / 本地路径,凭据不出私网
注意:Tailscale / 本地路径下,Readdle 云端 IDLE 长期失败,Spark 会反复弹 "Can't reach the server" 横幅,甚至提示重新登录账户——这是云端把账户判成"故障"了,骚扰频率较高。最干净的解法是在 Spark 设置里把这个账户的推送通知关掉,云端就不再尝试 IDLE,横幅消失;代价是彻底失去离线推送(反正本来也收不到)。
163 网页端先做一次:登录 → 设置 → POP3/SMTP/IMAP → 同时打开 IMAP 和 SMTP 服务 → 生成"客户端授权码"(下面填的"密码"全部用这个授权码,不是登录密码)。
- 顶部菜单:
Spark → Add Account... - 选 "Set Up Account Manually"(不要用 163 预设)
- 选 IMAP
- 按下表填写,点 Sign In:
| 字段 | 值 |
|---|---|
| Email Address | you@163.com |
| Password | 163 授权码 |
| User Name | you@163.com(完整邮箱) |
| IMAP Server | mail.example.com |
| IMAP Port | 993 |
| Use SSL | ✅ 开启 |
| SMTP Server | smtp.163.com |
| SMTP Port | 587 |
| Use SSL | ✅ 开启(STARTTLS) |
| SMTP User Name | you@163.com |
| SMTP Password | 163 授权码 |
Settings → Mail Accounts → Add Mail Account → Other- 同样选 IMAP,按上表填写
Spark 卡在加载或一直转圈:在你电脑/手机上执行 nc -vz mail.example.com 993(Mac 终端 / iOS 用任意带 nc 的 App),如果连不通说明端口没真的对外开放——回到第 2 步检查防火墙和云厂商安全组。
Verify return code 不是 0:证书没装好。在服务器上 sudo ls /etc/163-wrapper/ 看 cert.pem 和 key.pem 是否都存在;不在就重跑第 4 步。
看日志(systemd 模式):
sudo journalctl -u 163-wrapper -f按 Ctrl-C 退出。看到 client connected + ID injected successfully 就是 wrapper 工作正常。
重置授权码:163 网页端可以随时重置,新旧授权码互不影响;客户端那边对应改成新的即可。
-
默认
log_level: info,只记录连接事件(远端 IP、ID injected successfully等),不含任何凭据。 -
log_level: debug时会打印每条 IMAP 帧用于排错,但LOGIN/AUTHENTICATE的密码字段会自动屏蔽成<REDACTED>。 -
systemd 下日志由 journald 自动按磁盘空间轮转,默认上限
min(磁盘 10%, 4GB)。想严格限制:编辑/etc/systemd/journald.conf加SystemMaxUse=200M后sudo systemctl restart systemd-journald。 -
Docker 默认 json-file 日志驱动不轮转,长跑会涨;建议在
docker-compose.yml里给服务加:logging: driver: json-file options: { max-size: "10m", max-file: "3" }