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.com、example.com |
| CloudFront viewer 证书区域 | us-east-1 |
| 报错 | curl: (60) SSL certificate problem: certificate has expired |
CloudFront 的证书有两个常见来源:
- 在 AWS Certificate Manager 里申请 ACM 公网证书,然后直接绑定 CloudFront。
- 在 GoDaddy 或其他 CA 购买证书,把证书、私钥和中间证书链导入 ACM,再绑定 CloudFront。
两条路线都能让 HTTPS 正常工作,区别主要在运维成本和证书生命周期。

一、先确认是不是证书过期
先不要急着查应用日志。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 里:
- 在 ACM
us-east-1申请证书。 - 给
*.example.com和example.com做 DNS 验证。 - 在 CloudFront distribution 里选择这张 ACM 证书。
- 后续 ACM 托管续期。
对生产入口来说,最重要的是第四点。证书过期不是复杂故障,但影响很硬:客户端在 TLS 握手阶段直接失败,业务服务完全没有机会返回错误页。
GoDaddy 导入证书的路径多了几个手工环节:
- 在 GoDaddy 续费或重新签发。
- 下载证书、私钥、中间证书链。
- 按 ACM 要求导入。
- 如果是新证书 ARN,还要切 CloudFront viewer certificate。
- 等 CloudFront 分发部署。
- 验证所有边缘节点返回新证书。
任一步漏掉,最终表现都可能是客户端报 certificate has expired。

四、什么时候仍然应该用 GoDaddy 证书
GoDaddy 证书并不是没价值。它的优势是可导出、可跨平台部署。
下面这些场景仍然适合第三方证书:
- 同一张
*.example.com证书要同时装到 AWS CloudFront、阿里云 nginx、自建服务器。 - 公司已有统一证书采购、审批和归档流程。
- 需要 OV/EV 这类企业身份验证证书。
- 证书必须脱离 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 的场景。
无论用哪种方法,最后都要验证边缘节点返回的新证书。
替换前先确认三件事:
- 新证书在
us-east-1。 - 新证书状态是
Issued。 - 新证书覆盖 distribution 里的所有
Alternate domain names (CNAMEs),例如 distribution 同时有api.example.com和platform.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
参考文档:
- AWS Certificate Manager pricing:ACM 非导出公网证书无额外费用;可导出公网证书按签发和续期收费。
- Managed certificate renewal in AWS Certificate Manager:ACM 可托管续期 Amazon 签发的证书,导入证书不符合托管续期条件。
- DNS validation and renewal validation:说明 ACM 通过 DNS CNAME 验证和续期验证证书。
- Check the renewal status of a certificate:说明公网证书到期前自动续期的时间窗口。
- Requirements for using SSL/TLS certificates with CloudFront:CloudFront 推荐使用 ACM 证书,viewer 证书需要在
us-east-1;第三方证书需要正确提供中间证书链,并自行监控到期和重新导入。 - Import certificates into AWS Certificate Manager:导入第三方证书到 ACM 的入口文档。
- AWS CLI acm request-certificate:ACM 申请证书 CLI 参数说明。
- AWS CLI cloudfront update-distribution:CloudFront 更新分发配置 CLI 参数说明。

浙公网安备 33010602011771号