CloudFront 证书过期: AWS ACM 免费证书和 GoDaddy 导入证书怎么选

一条 CloudFront 上的 API 突然报:

curl https://api.example.com/v1/song/generate
curl: (60) SSL certificate problem: certificate has expired

第一反应可能是源站挂了、接口错了,或者客户端证书库旧了。但这种错误发生在 TLS 握手阶段,请求还没真正进到业务服务。它说的是:客户端连到 CDN 入口时,CDN 返回的 viewer 证书已经过期。

这篇只讨论一个很具体的问题:域名已经走 AWS CloudFront 时,证书应该用 AWS ACM 的公网证书,还是继续用 GoDaddy 这类第三方证书再导入 ACM。

环境和问题

本文示例环境如下,替换成你自己的域名和分发即可:

项目 示例值
CDN AWS CloudFront
域名 api.example.com
证书覆盖 *.example.comexample.com
CloudFront viewer 证书区域 us-east-1
报错 curl: (60) SSL certificate problem: certificate has expired

CloudFront 的证书有两个常见来源:

  1. 在 AWS Certificate Manager 里申请 ACM 公网证书,然后直接绑定 CloudFront。
  2. 在 GoDaddy 或其他 CA 购买证书,把证书、私钥和中间证书链导入 ACM,再绑定 CloudFront。

两条路线都能让 HTTPS 正常工作,区别主要在运维成本和证书生命周期。

CloudFront 证书选型

一、先确认是不是证书过期

先不要急着查应用日志。curl: (60) 这种错误发生在 TLS 校验阶段,可以直接看服务端返回的证书:

echo | openssl s_client \
  -servername api.example.com \
  -connect api.example.com:443 \
  -showcerts 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName

如果证书过期,会看到类似:

subject=CN=*.example.com
issuer=...
notBefore=Jun 27 08:59:45 2025 GMT
notAfter=Jul  8 06:18:48 2026 GMT
X509v3 Subject Alternative Name:
    DNS:*.example.com, DNS:example.com

这里关键看 notAfter。当前时间已经晚于 notAfter,客户端就会拒绝连接。

如果你想确认是不是 CloudFront 边缘节点还没全部切完,可以逐个检查 DNS 返回的 IP:

for ip in $(dig +short api.example.com @8.8.8.8 | grep -E '^[0-9.]'); do
  echo "--- $ip"
  echo | openssl s_client \
    -servername api.example.com \
    -connect ${ip}:443 \
    -showcerts 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -serial -fingerprint -sha256
done

如果所有 IP 都返回新证书,说明 CloudFront 边缘侧已经一致。如果还有节点返回旧证书,就需要等分发部署完成,或者继续检查 CloudFront 绑定证书是否已经切到新 ARN。

二、ACM 免费证书和 GoDaddy 证书到底差在哪

从浏览器和 TLS 加密角度看,二者都可以是可信证书。差异不在“谁更安全地加密”,而在“谁负责续期、私钥在哪里、能不能跨平台部署”。

维度 AWS ACM 公网证书 GoDaddy 证书导入 ACM
加密效果 浏览器信任,正常 TLS 浏览器信任,正常 TLS
费用 用于 CloudFront、ELB、API Gateway 等 AWS 集成服务时,非导出公网证书无额外证书费用 证书购买和续费收费
续期 DNS 验证记录保留时,ACM 可托管续期 需要人工续费,再重新导入 ACM
私钥 默认不可导出,私钥不落到运维人员和服务器上 私钥可下载,需要自己保管
部署范围 只能用于 AWS 集成服务,不能拿去装自建 nginx 可部署到 AWS、阿里云、自建服务器和第三方网关
CloudFront 适配 直接选择 ACM 证书 需要先导入 ACM,再绑定 CloudFront
证书链风险 AWS 托管,少处理 fullchain/chain 需要正确拆分 leaf 证书和中间证书链
运维风险 较高,容易漏续期、漏导入、漏切换

一句话:

  • 只给 CloudFront 这类 AWS 入口用,优先 ACM 公网证书。
  • 同一张证书还要装到非 AWS 服务器,GoDaddy 这类可导出证书更灵活。

三、为什么 CloudFront 更适合 ACM 托管证书

CloudFront 的 viewer 证书本来就要求挂在 AWS 侧。用 ACM 申请的公网证书时,证书生命周期也留在 AWS 里:

  1. 在 ACM us-east-1 申请证书。
  2. *.example.comexample.com 做 DNS 验证。
  3. 在 CloudFront distribution 里选择这张 ACM 证书。
  4. 后续 ACM 托管续期。

对生产入口来说,最重要的是第四点。证书过期不是复杂故障,但影响很硬:客户端在 TLS 握手阶段直接失败,业务服务完全没有机会返回错误页。

GoDaddy 导入证书的路径多了几个手工环节:

  1. 在 GoDaddy 续费或重新签发。
  2. 下载证书、私钥、中间证书链。
  3. 按 ACM 要求导入。
  4. 如果是新证书 ARN,还要切 CloudFront viewer certificate。
  5. 等 CloudFront 分发部署。
  6. 验证所有边缘节点返回新证书。

任一步漏掉,最终表现都可能是客户端报 certificate has expired

CloudFront 证书续期路径对比

四、什么时候仍然应该用 GoDaddy 证书

GoDaddy 证书并不是没价值。它的优势是可导出、可跨平台部署。

下面这些场景仍然适合第三方证书:

  1. 同一张 *.example.com 证书要同时装到 AWS CloudFront、阿里云 nginx、自建服务器。
  2. 公司已有统一证书采购、审批和归档流程。
  3. 需要 OV/EV 这类企业身份验证证书。
  4. 证书必须脱离 AWS 使用,未来可能迁移 CDN 或多云混用。

但如果只是 CloudFront viewer HTTPS,导入第三方证书通常不是最省心的方案。它把“证书续期”从 AWS 托管流程变成了人工流程。

五、申请 ACM 证书:方法 1 页面操作 / 方法 2 CLI

如果你的域名只走 CloudFront,建议申请 ACM 公网证书。申请证书有两种方式,二选一即可

  • 方法 1:在 AWS 控制台页面申请,适合偶尔操作。
  • 方法 2:用 AWS CLI 申请,适合自动化或批量操作。

无论用哪种方法,后面都要做 DNS CNAME 验证。

方法 1:页面操作

CloudFront 使用 ACM 证书时,证书要在 us-east-1 区域。

页面路径:

AWS Console
  -> Certificate Manager
  -> Region 选择 US East (N. Virginia) us-east-1
  -> Request certificate
  -> Request a public certificate

证书域名建议至少包含:

*.example.com
example.com

如果你不想用泛域名,也可以只申请具体域名:

api.example.com
www.example.com
platform.example.com

验证方式选择:

Validation method -> DNS validation

提交后,ACM 会生成 CNAME 验证记录。

方法 2:CLI 创建

如果用 AWS CLI,命令是:

aws acm request-certificate \
  --region us-east-1 \
  --domain-name "*.example.com" \
  --subject-alternative-names "example.com" \
  --validation-method DNS \
  --key-algorithm RSA_2048 \
  --idempotency-token example20260708

返回里会有证书 ARN:

{
  "CertificateArn": "arn:aws:acm:us-east-1:<ACCOUNT_ID>:certificate/<CERTIFICATE_ID>"
}

后续用这个 ARN 查询 DNS 验证记录:

aws acm describe-certificate \
  --region us-east-1 \
  --certificate-arn "arn:aws:acm:us-east-1:<ACCOUNT_ID>:certificate/<CERTIFICATE_ID>" \
  --query "Certificate.DomainValidationOptions[*].ResourceRecord"

会得到类似:

[
  {
    "Name": "_xxxx.example.com.",
    "Type": "CNAME",
    "Value": "_yyyy.acm-validations.aws."
  }
]

如果你不用 AWS CLI,也可以在 ACM 控制台证书详情页看到同样的 CNAME。

公共步骤:加 DNS 验证记录

ACM 会给出一组 CNAME 验证记录,把它们加到 DNS 里。

如果域名 DNS 在 GoDaddy,页面路径大致是:

GoDaddy
  -> My Products
  -> Domains
  -> 选择 example.com
  -> DNS
  -> Manage DNS
  -> Add New Record
  -> Type 选择 CNAME

填写时注意:

  • Name 填 ACM 给出的记录名前缀,很多 DNS 面板会自动补根域名。
  • Value 填 ACM 给出的完整目标值。
  • 不要把 https:// 之类协议写进去。
  • 如果 DNS 面板自动补域名,避免把 _xxxx.example.com.example.com 写出来。

这里的关键不是“验证一次就删”,而是保留 DNS 验证记录。后续 ACM 托管续期需要继续确认域名控制权。

可以用 dig 检查 CNAME 是否生效:

dig +short CNAME _xxxx.example.com @8.8.8.8

看到 ACM 给出的 _yyyy.acm-validations.aws. 后,再等待 ACM 状态从 Pending validation 变成 Issued

CLI 轮询状态:

aws acm describe-certificate \
  --region us-east-1 \
  --certificate-arn "arn:aws:acm:us-east-1:<ACCOUNT_ID>:certificate/<CERTIFICATE_ID>" \
  --query "Certificate.Status"

六、替换 CloudFront viewer 证书:方法 1 页面操作 / 方法 2 CLI

证书状态变成 Issued 后,再切 CloudFront。替换证书也有两种方式,二选一即可

  • 方法 1:在 CloudFront 控制台页面替换,适合少量 distribution。
  • 方法 2:用 AWS CLI 替换,适合批量替换或需要留下配置 diff 的场景。

无论用哪种方法,最后都要验证边缘节点返回的新证书。

替换前先确认三件事:

  1. 新证书在 us-east-1
  2. 新证书状态是 Issued
  3. 新证书覆盖 distribution 里的所有 Alternate domain names (CNAMEs),例如 distribution 同时有 api.example.complatform.example.com,证书就必须覆盖这两个名字。

方法 1:页面操作

页面路径:

AWS Console
  -> CloudFront
  -> Distributions
  -> 选择目标 distribution
  -> General
  -> Settings
  -> Edit
  -> Custom SSL certificate

页面操作时重点看三处:

  • Alternate domain names (CNAMEs) 里要包含你的域名,比如 api.example.com
  • Custom SSL certificate 里选择刚才 us-east-1 签发的 ACM 证书。
  • Minimum protocol version 先保留现有生产配置。换证书和升级 TLS policy 是两个变更,不要混在一起做。

保存后等待 distribution 状态回到 Deployed

方法 2:CLI 替换

CloudFront 的 CLI 更新不是一条简单的“set certificate”命令。它要求先取出完整 distribution config 和 ETag,修改 ViewerCertificate 后再把完整配置提交回去。

这一步是生产变更,不能手写一份新的 distribution config。正确做法是:先备份现有配置,再只把 ViewerCertificate 里的证书 ARN 换掉。TLS policy、SNI 设置、CNAME、origin、cache behavior 都先保持原样。

先取出当前配置:

aws cloudfront get-distribution-config \
  --id <DISTRIBUTION_ID> \
  > /tmp/cf-config.json

备份一份原始配置:

cp /tmp/cf-config.json /tmp/cf-config.json.bak

设置变量:

ETAG=$(jq -r '.ETag' /tmp/cf-config.json)
CERT_ARN="arn:aws:acm:us-east-1:<ACCOUNT_ID>:certificate/<CERTIFICATE_ID>"

先把当前 ViewerCertificate 看一眼:

jq '.DistributionConfig.ViewerCertificate' /tmp/cf-config.json

只替换证书 ARN,其他 ViewerCertificate 字段原样保留:

jq --arg cert "$CERT_ARN" '
  .DistributionConfig.ViewerCertificate.ACMCertificateArn = $cert
  | .DistributionConfig.ViewerCertificate.Certificate = $cert
  | .DistributionConfig.ViewerCertificate.CertificateSource = "acm"
  | .DistributionConfig.ViewerCertificate.CloudFrontDefaultCertificate = false
  | .DistributionConfig
' /tmp/cf-config.json > /tmp/distribution-config.json

确认 TLS policy 没被顺手改掉:

jq -r '.DistributionConfig.ViewerCertificate.MinimumProtocolVersion' /tmp/cf-config.json
jq -r '.ViewerCertificate.MinimumProtocolVersion' /tmp/distribution-config.json

也可以把变更前后的 ViewerCertificate 单独 diff 一下:

jq '.DistributionConfig.ViewerCertificate' /tmp/cf-config.json > /tmp/viewer-cert-before.json
jq '.ViewerCertificate' /tmp/distribution-config.json > /tmp/viewer-cert-after.json
diff -u /tmp/viewer-cert-before.json /tmp/viewer-cert-after.json

预期只看到证书 ARN / CertificateSource / CloudFrontDefaultCertificate 相关变化。确认后再提交:

aws cloudfront update-distribution \
  --id <DISTRIBUTION_ID> \
  --if-match "$ETAG" \
  --distribution-config file:///tmp/distribution-config.json

CloudFront 更新后不是立刻全网生效,需要等 distribution 状态回到 Deployed

查询状态:

aws cloudfront get-distribution \
  --id <DISTRIBUTION_ID> \
  --query "Distribution.Status"

如果要批量替换多个 distribution,先逐个确认每个 distribution 的 CNAME 都被新证书覆盖。不要因为都是同一个主域名,就默认一张证书能覆盖所有别名。

公共步骤:验证边缘节点

先看普通 curl:

curl -Iv https://api.example.com/

预期看到:

SSL certificate verify ok.

再看证书日期:

echo | openssl s_client \
  -servername api.example.com \
  -connect api.example.com:443 \
  -showcerts 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -serial -fingerprint -sha256

最后逐个边缘 IP 抽查:

for ip in $(dig +short api.example.com @8.8.8.8 | grep -E '^[0-9.]'); do
  echo "--- $ip"
  echo | openssl s_client \
    -servername api.example.com \
    -connect ${ip}:443 \
    -showcerts 2>/dev/null \
  | openssl x509 -noout -dates -serial -fingerprint -sha256
done

如果每个 IP 的 notAfter 和指纹都一致,说明当前解析到的边缘节点已经一致返回新证书。

七、ACM 免费证书怎么续期

这里要看清楚“免费证书”和“导入证书”的区别。

ACM 签发的公网证书可以托管续期,导入证书不参与 ACM 托管续期。AWS 官方文档里明确写了,imported certificates are not eligible for managed renewal。

现在 ACM 公网证书的续期规则也不是简单的“一年一次”。按 AWS 当前说明:

  • 新签发和续期后的 ACM 公网证书,有效期默认是 198 天。
  • 198 天有效期的公网证书,ACM 会在到期前 45 天开始自动续期。
  • 旧的 395 天有效期证书,到期前 60 天开始续期;续出来的新证书会变成 198 天有效期。
  • DNS 验证方式下,要保留 ACM 的 CNAME 验证记录。
  • 证书需要仍被 AWS 集成服务使用,或者可以被 ACM 正常验证域名控制权。

所以更准确的说法是:

ACM 公网证书有效期约 198 天,AWS 会在到期前 45 天自动尝试续期。

这也是为什么 CloudFront 入口更适合 ACM 托管证书:证书生命周期从“人工日历提醒”变成了“ACM 托管续期 + DNS 验证记录”。

八、如果必须导入 GoDaddy 证书,注意这几个点

有些场景必须用第三方证书,那就把人工风险显式收敛。

证书链要拆对

导入 ACM 时通常需要三类内容:

Certificate body      = leaf 证书
Certificate private key = 私钥
Certificate chain     = 中间证书链

不要把整个 fullchain 原样塞进 Certificate body。更稳的做法是:

# 查看 fullchain 里有几段证书
grep -n "BEGIN CERTIFICATE" fullchain.pem

# 第一段作为 leaf 证书
# 第二段及以后作为 certificate chain

导入后用 openssl s_client 验证,不应该出现:

Verify return code: 21 (unable to verify the first certificate)

应该是:

Verify return code: 0 (ok)

到期监控要覆盖 CDN 入口

不要只监控源站 nginx 的证书。CloudFront viewer certificate 也要监控。

最简单的探测就是:

echo | openssl s_client \
  -servername api.example.com \
  -connect api.example.com:443 \
  -showcerts 2>/dev/null \
| openssl x509 -noout -dates

如果是导入证书,还要记录:

  • 证书来源。
  • ACM ARN。
  • CloudFront distribution ID。
  • 到期时间。
  • 续期负责人。
  • 续期后是否已重新绑定并部署完成。

九、怎么选

我一般按下面这条规则选:

场景 建议
域名只用于 CloudFront / AWS ALB / API Gateway ACM 公网证书
同一张证书要装到阿里云 nginx 或自建服务器 GoDaddy 或其他可导出证书
想减少证书过期事故 ACM 公网证书 + DNS 验证
需要企业身份验证证书 第三方 OV/EV 证书
需要跨云统一证书资产 第三方可导出证书

对 CloudFront API 域名来说,ACM 公网证书通常是默认答案。只有当“这张证书必须离开 AWS 使用”时,才值得承担导入证书的维护成本。

快速参考

CloudFront 绑定 ACM 证书的要点:

  • CloudFront viewer certificate 使用 ACM 时,证书放在 us-east-1
  • 只用于 AWS 集成服务时,优先用 ACM 非导出公网证书。
  • DNS 验证 CNAME 保留,不要验证完就删除。
  • ACM 公网证书当前有效期约 198 天,AWS 会在到期前 45 天自动尝试续期。
  • 导入 GoDaddy 证书后,ACM 不会替你自动续期。
  • curl: (60) SSL certificate problem: certificate has expired 优先查 CDN viewer 证书,不要先查业务服务。
  • 证书链问题看 openssl s_client 的 verify return code。
  • 过期问题看 notAfter

申请 ACM 证书:

aws acm request-certificate \
  --region us-east-1 \
  --domain-name "*.example.com" \
  --subject-alternative-names "example.com" \
  --validation-method DNS \
  --key-algorithm RSA_2048

查看 DNS 验证记录:

aws acm describe-certificate \
  --region us-east-1 \
  --certificate-arn "arn:aws:acm:us-east-1:<ACCOUNT_ID>:certificate/<CERTIFICATE_ID>" \
  --query "Certificate.DomainValidationOptions[*].ResourceRecord"

查看 CloudFront 分发当前证书:

aws cloudfront get-distribution \
  --id <DISTRIBUTION_ID> \
  --query "Distribution.DistributionConfig.ViewerCertificate"

常用证书排查命令:

# 看当前证书有效期
echo | openssl s_client \
  -servername api.example.com \
  -connect api.example.com:443 \
  -showcerts 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName

# 看 curl 是否能通过证书校验
curl -Iv https://api.example.com/

# 抽查当前 DNS 返回的边缘 IP
for ip in $(dig +short api.example.com @8.8.8.8 | grep -E '^[0-9.]'); do
  echo "--- $ip"
  echo | openssl s_client \
    -servername api.example.com \
    -connect ${ip}:443 \
    -showcerts 2>/dev/null \
  | openssl x509 -noout -dates -serial -fingerprint -sha256
done

参考文档:

posted @ 2026-07-08 16:25  Hello_worlds  阅读(3)  评论(0)    收藏  举报