Skip to content

HTTPS 与域名

服务器自己申请和续期免费的 Let's Encrypt 证书——不需要 certbot、不需要 nginx、不需要 cron。你只要把域名指向服务器、在配置里写上域名就完事:启动时自动签发,到期前一个月左右自动续期。

1. 把域名指向服务器

给你的域名(比如 code.example.com)建一条 DNS A 记录(IPv6 加 AAAA),指向 VPS 的公网 IP。验证:

bash
dig +short code.example.com     # 必须输出你服务器的 IP

2. 放行 80 和 443 端口

两个端口都必须公网可达:

  • 80——Let's Encrypt 用它验证域名归属(之后也承担 http→https 跳转)。80 不通就签不出证书。
  • 443——浏览器访问的 HTTPS 端口。

检查云厂商的防火墙/安全组,以及本机防火墙(ufwfirewalld)。另外确认没有旧的 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-server

4. 验证

签发只要几秒钟。然后:

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