文档 / 核心概念 / 域名与 SSL

域名与 SSL

sh0 使用 Caddy 作为反向代理,这意味着每个域名通过 Let's Encrypt 自动获得 HTTPS。无需手动管理证书,无需 cron 任务,无需配置文件——开箱即用。

域名如何工作

当您为应用分配域名时,sh0 自动配置 Caddy 以:

  1. 监听该域名的传入请求(端口 80 和 443)。
  2. 从 Let's Encrypt 获取并安装 SSL 证书。
  3. 将所有 HTTP 流量重定向到 HTTPS。
  4. 将 HTTPS 流量反向代理到正确的 Docker 容器。
  5. 在证书过期前自动续期。
Flow diagram showing: Internet request to domain.com goes to Caddy (ports 80/443), which terminates SSL and proxies to the Docker container on its internal port
Caddy handles SSL termination and proxying

整个过程是自动的。一旦您添加域名并将其 DNS 指向您的服务器,其他一切都是自动的。

默认子域名

在 sh0 Cloud 上部署的每个应用都会获得一个 *.sh0.app 下的免费子域名。这对以下用途很有帮助:

  • 无需配置 DNS 的快速测试
  • 预发布环境
  • Pull Request 的预览部署

子域名基于您的应用名称:如果您的应用名为 my-api,它将可在 my-api.sh0.app 访问。

Note
默认子域名功能完整,支持 SSL。非常适合开发和测试。对于生产环境,您通常会使用自己的自定义域名。

关闭 *.sh0.app

sh0 启动时会向 sh0 云代理(proxy.sh0.app)注册其标识、公网 IP 地址和版本,以获取 *.sh0.app 地址。若不希望服务器接入该代理,请在 sh0 服务首次启动前,在其环境中设置 SH0_CLOUD_REGISTRATION=off。

在新服务器上,安装脚本会替您完成:

New server
curl -fsSL https://get.sh0.dev | bash -s -- --no-cloud

在已安装的服务器上,添加一个 systemd drop-in 并重启 sh0:

Existing server
mkdir -p /etc/systemd/system/sh0.service.d
printf '[Service]\nEnvironment=SH0_CLOUD_REGISTRATION=off\n' \
  > /etc/systemd/system/sh0.service.d/cloud-registration.conf
systemctl daemon-reload && systemctl restart sh0

然后运行 sh0 doctor:云注册一行应显示“disabled”。可接受的值为 off、0、false 和 no。

Warning
关闭注册会停止心跳,但目前不会从代理中移除此前获得的地址。此后您的应用只能通过您自己的域名访问。

添加自定义域名

要为您的应用添加自定义域名:

  1. 导航到您的应用并打开 Domains 标签。
  2. 点击 Add Domain。
  3. 输入您的域名(例如 app.example.com 或 example.com)。
  4. 保存并配置您的 DNS(见下文)。
Add Domain dialog showing a text input for the domain name, with examples of valid formats: 'example.com', 'app.example.com', 'api.example.com'
Adding a custom domain

您可以为单个应用添加多个域名。所有域名将服务于同一个应用。这对以下场景很有用:

  • 同时提供 example.com 和 www.example.com
  • 支持指向同一服务的多个域名
  • 在自定义域名旁保留默认子域名作为备用

DNS 配置

通过在注册商或 DNS 提供商处添加 DNS 记录,将域名指向您的 sh0 服务器:

类型名称值用途
A@您的服务器 IP根域名 (example.com)
Aapp您的服务器 IP子域名 (app.example.com)
CNAMEwwwexample.comwww 重定向
Example DNS Records
# For app.example.com
Type: A
Name: app
Value: 203.0.113.50
TTL: 300

# For example.com (root domain)
Type: A
Name: @
Value: 203.0.113.50
TTL: 300
DNS 传播
DNS 变更通常在几分钟内生效,但可能需要长达 24 小时。您可以使用 dig app.example.com 或 nslookup app.example.com 验证 DNS 配置是否正确。

自动 SSL

SSL 证书完全由 Caddy 管理。无需配置,无需上传证书,无需维护续期脚本。

Let's Encrypt 如何工作

当您添加域名时,以下自动发生:

  1. 验证 -- Caddy 发起 ACME HTTP-01 验证以证明域名所有权。
  2. 签发 -- Let's Encrypt 验证通过并签发证书(通常几秒内完成)。
  3. 安装 -- Caddy 安装证书并开始提供 HTTPS 流量。
  4. 续期 -- Caddy 在过期前 30 天自动续期证书。
端口 80 必须开放
HTTP-01 验证要求端口 80 可从互联网访问。如果端口 80 被防火墙阻止,Caddy 无法获取证书。请确保配置了 ufw allow 80/tcp(或等效命令)。
Domain list showing three domains with their SSL status: 'app.example.com' with a green lock icon and 'Valid until Dec 2026', 'api.example.com' with a green lock, and 'staging.example.com' with a yellow pending icon
SSL certificate status for each domain

通配符域名

通配符域名(例如 *.example.com)允许任何子域名路由到您的应用。这对以下场景很有用:

  • 每个租户获得一个子域名的多租户应用
  • 每个分支拥有唯一子域名的预览部署
  • 基于地理位置或语言的路由
通配符 SSL 需要 DNS 验证
通配符证书需要 DNS-01 验证而非 HTTP-01。这意味着您需要在 sh0 设置中配置 DNS 提供商 API 密钥,以便 Caddy 可以创建 TXT 记录进行验证。支持的提供商包括 Cloudflare、Route53、DigitalOcean 等。
Configure DNS Provider for Wildcards
# In sh0 Settings > SSL, configure your DNS provider
# Example for Cloudflare:
DNS_PROVIDER=cloudflare
CLOUDFLARE_API_TOKEN=your-api-token

SSL 证书状态

每个应用的域名部分显示每个已配置域名的 SSL 状态:

状态含义
活跃证书有效,正在提供 HTTPS 流量。
等待中尚无证书。自 v1.6.25 起证书按需签发:在该域名的第一次 TLS 握手时才申请证书,因此在有人真正连接之前,域名会一直处于“等待中”。将鼠标悬停在徽标上即可查看仍在等待的原因。
续期中证书正在续期(过期前 30 天)。
失败证书配置失败。请检查 DNS 和防火墙设置。

自 v1.6.25 起新增两条规则。已激活的域名若其证书从磁盘上消失,会在连续两次检查后回到等待中,绝不会因单次读取而降级。此外,证书到期日期现在直接从证书本身读取,到期告警终于有了可用的值。

故障排除

如果您的域名无法工作或 SSL 证书配置失败,请检查以下常见问题:

DNS 未指向您的服务器

运行 dig +short your-domain.com 并验证其返回您服务器的 IP 地址。如果不是,请更新 DNS 记录并等待传播。

端口 80 被阻止

Let's Encrypt 需要端口 80 来验证域名所有权。确保防火墙允许端口 80 的入站 TCP 流量。使用 sudo ufw status 检查。

其他服务正在使用端口 80/443

如果 Nginx、Apache 或其他 Web 服务器正在运行,可能与 Caddy 冲突。停止其他服务:sudo systemctl stop nginx。

超出速率限制

Let's Encrypt 有速率限制(每周每域名 50 个证书)。如果达到限制,请等待或使用暂存环境进行测试。Caddy 日志会指示速率限制错误。

Debugging Commands
# Check if DNS resolves correctly
dig +short app.example.com

# Test if port 80 is accessible from outside
curl -I http://app.example.com

# Check Caddy logs for certificate errors
journalctl -u sh0 | grep -i "certificate\|tls\|acme"