HTTPS 与域名
服务器自己申请和续期免费的 Let's Encrypt 证书——不需要 certbot、不需要 nginx、不需要 cron。你只要把域名指向服务器、在配置里写上域名就完事:启动时自动签发,到期前一个月左右自动续期。
1. 把域名指向服务器
给你的域名(比如 code.example.com)建一条 DNS A 记录(IPv6 加 AAAA),指向 VPS 的公网 IP。验证:
bash
dig +short code.example.com # 必须输出你服务器的 IP2. 放行 80 和 443 端口
两个端口都必须公网可达:
- 80——Let's Encrypt 用它验证域名归属(之后也承担 http→https 跳转)。80 不通就签不出证书。
- 443——浏览器访问的 HTTPS 端口。
检查云厂商的防火墙/安全组,以及本机防火墙(ufw、firewalld)。另外确认没有旧的 nginx/caddy/apache 占着这两个端口。
3. 在配置里打开
编辑 /etc/code.yaml(或用配置界面):
yaml
tlsDomains: ["code.example.com"] # ← 这就是开关
tlsEmail: "you@example.com" # 可选:到期提醒
cookieSecure: true # HTTPS 登录必需多个域名也行:tlsDomains: ["code.example.com", "c.example.org"]——每个都会拿到自己的证书,每个都必须解析到这台服务器。
然后重启(或在配置界面里 Ctrl+S):
bash
sudo systemctl restart code-server4. 验证
签发只要几秒钟。然后:
bash
# 看证书签发日志
sudo journalctl -u code-server | grep -i "tls: certificate"
# 期望看到: tls: certificate ok host=code.example.com remaining_days=89
# 在任何地方验证 HTTPS
curl -sI https://code.example.com | head -1浏览器打开 https://code.example.com——有锁图标、无警告。确认没问题后,可以把 hsts: true 打开,强制所有流量走 HTTPS。
续期机制(你什么都不用做)
- 证书和 Let's Encrypt 账户密钥缓存在
<dataDir>/cert/cache(默认/var/lib/code/data/cert/cache)。 - 后台每天检查两次,距到期约 30 天内自动续期,新证书在线热替换——不重启、不中断。
- 重启和升级会复用缓存的证书,不会无谓地重签。
保住证书缓存
证书缓存在 dataDir 里,升级时自动保留。但如果你清掉了这个目录、或容器没做持久化(这是最容易踩的坑),每次重启都会重新申请证书,很快撞上 Let's Encrypt 的限流(同域名每周约 5 张),会被锁几天。务必持久化 dataDir。见 Docker 部署。
排错
| 症状 | 可能原因 | 处理 |
|---|---|---|
| 日志里验证失败、没有证书 | 80 端口没放行,或 DNS 还没指过来 | 防火墙放行 80/443;dig +short <域名> 检查;DNS 生效可能要几分钟 |
启动报 bind: permission denied | 安装包自带的服务单元被替换了(它本来带端口授权) | 重装包,或改用高端口 + 前置代理 |
address already in use | 旧的 nginx/caddy 还占着 80/443 | 停掉并禁用它 |
| 浏览器访问 https 报 TLS/连接错误 | 证书还没签出来(或签发失败) | 按第 4 步查日志;某域名的证书不存在时服务器会直接拒绝其 HTTPS 连接 |
日志出现 too many certificates | 证书缓存没持久化,重签太多次 | 持久化 dataDir,等限流窗口过去 |
| 登录后马上被弹出 | cookieSecure: true 但用的是 http:// | 用 https:// 地址访问(或开 hsts: true 强制跳转) |
没有域名?/ TLS 在别处终结?
- 只有 IP、没有域名:
tlsDomains留空,用http://<ip>/访问,cookieSecure: false。在可信网络里快速试用可以;别在公网上长期这么用。 - 已有反向代理 / 隧道做 TLS(nginx、Caddy、frp、Cloudflare):
tlsDomains留空,listen设成环回高端口如127.0.0.1:5080,让代理指向它(必须透传 WebSocket);公网侧是 HTTPS,所以cookieSecure保持true。