acme.client 初始化必须显式设置directoryurl和key,否则会panic;本地测试用staging地址,生产换正式地址;account须通过register或getaccount获取,不可手动构造;http-01挑战需精确暴露/.well-known/acme-challenge/路径并设content-type:text/plain;证书链必须包含中间ca,否则tls握手失败。

acme.Client 初始化时必须指定 DirectoryURL
不设 DirectoryURL 会直接 panic,因为 Go 的 crypto/acme 包不提供默认值。本地测试用 "https://acme-staging-v02.api.letsencrypt.org/directory",生产必须换为 "https://acme-v02.api.letsencrypt.org/directory"。漏掉这步,client.AuthorizeOrder 等后续调用全报 Post "": unsupported protocol scheme ""。
常见错误是直接 new 一个 acme.Client 后就调用方法,没配 DirectoryURL 和 Key:
client := &acme.Client{} // ❌ 缺少 DirectoryURL 和 Key
authz, err := client.AuthorizeOrder(ctx, order)
正确写法要显式传入:
client := &acme.Client{
DirectoryURL: "https://acme-staging-v02.api.letsencrypt.org/directory",
Key: privateKey, // *ecdsa.PrivateKey 或 *rsa.PrivateKey
}
acme.Account 不能复用内存对象,必须从 Register 或 GetAccount 获取
手动构造 acme.Account 结构体(比如只填 URI)去调用 client.GetAuthorization 会返回 400 Bad Request: no account key in request —— ACME 协议要求每个请求都带 JWS 签名,而签名依赖 Account 的 Key 字段,该字段不会被序列化进 URI,也不能靠反推补全。
务必通过以下任一方式获取有效 Account:
-
client.Register(首次注册,含 ToS 接受) -
client.GetAccount(已有账户,传入之前保存的 Account.URI)
注意:Account.URI 是长期有效的,建议存到磁盘(如 JSON 文件),每次启动读取后调用 client.GetAccount 刷新状态,避免因服务器端密钥轮转导致签名失败。
一款AI开发辅助工具,主要用于从 AI 编程会话日志(Clawdbot、Claude Code、Codex)中提取对话记录。该功能用于在用户要求导出提示词历史、会话日志或 `.jsonl` 格式的会话文件时使用,适合需要提升相关任务效率的用户。
HTTP-01 挑战需自行暴露 /.well-known/acme-challenge/ 路径
Go 的 acme.Client 不起 HTTP 服务,它只生成 token + keyAuth 并等待你把 keyAuth 放到对应路径下。如果用 http.ServeMux,必须确保路径精确匹配:
http.HandleFunc("/.well-known/acme-challenge/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/.well-known/acme-challenge/"+token {
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(keyAuth))
return
}
http.NotFound(w, r)
})
容易踩的坑:
- 路径末尾多加斜杠(如
"/.well-known/acme-challenge")导致 404 - 没设
Content-Type: text/plain,某些 ACME 服务器校验失败 - 没处理 HEAD 请求(Let’s Encrypt 会发 HEAD 预检)
- 本地开发时用 127.0.0.1 测试,但 ACME 服务器无法访问,必须用公网可解析域名
证书链顺序错乱会导致 TLS 握手失败
client.CreateCert 返回的 derBytes 是纯证书(不含中间 CA),而大多数 TLS 库(如 http.Server.TLSConfig.Certificates)要求传入完整链:leaf → intermediate → root(root 可省略)。漏掉中间证书,Chrome 会报 ERR_CERT_AUTHORITY_INVALID。
正确做法是拼接:
certBundle := append(derBytes, []byte(intermediatePEM)...) // intermediatePEM 来自 Let's Encrypt 官方或 client.FetchCertificate
更稳妥的方式是用 client.FetchCertificate(传入 cert.URL),它返回已包含中间证书的完整链;或者从 client.GetCertificate(v2.15+)拿到 *x509.Certificate 后手动构建链。
别依赖操作系统或 Go 标准库自动补全中间证书——ACME 场景下它们通常不生效。










