You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Docker、Nginx、AWS ECS的Let's Encrypt验证频繁失败

我遇到过类似的ECS+Let's Encrypt验证问题,结合你的情况给你拆解分析下:

一、测试与定位问题的具体步骤

先一步步排查,缩小问题范围:

  • 验证ELB流量分发:多次执行 curl -v http://your-domain/.well-known/acme-challenges/test.txt,看返回的响应是否来自不同的服务器IP(可以从Server头或IP地址判断)。同时查看ELB的访问日志,确认请求是不是随机分发到了所有ECS实例。如果是轮询分发,那只有一台实例有验证文件的话,大部分请求会404。
  • 检查容器间的文件同步:CI/CD部署后,登录所有ECS实例,分别docker exec到每个Nginx容器里,查看$SSL_ROOT/.well-known/acme-challenges目录下是否存在验证文件。如果只有一台容器有,那问题就出在文件没有同步。
  • 核对Nginx配置:确保你的Nginx配置里对验证路径的规则没有被其他规则覆盖,比如:
    location ^~ /.well-known/acme-challenge/ {
        root $SSL_ROOT;
        default_type text/plain;
        allow all;
        # 禁用任何可能的缓存或rewrite
        expires -1;
        rewrite ^(.*)$ $1 break;
    }
    
    这条规则一定要放在其他location规则之前,避免被比如Rails的try_files或HTTPS跳转规则拦截。
  • 抓包跟踪验证请求:在ECS实例上执行 sudo tcpdump -i any port 80 and host acme-v02.api.letsencrypt.org,然后触发dehydrated验证,看请求是否真的到达了容器,以及容器返回的状态码是什么。
  • 检查CI/CD脚本的执行范围:确认你的CI/CD是只在单台ECS实例上运行了dehydrated,还是所有实例都执行了。如果是前者,那只有那台实例的容器有验证文件。

二、为何仅单台服务器能成功验证?

核心原因就是验证文件没有同步到所有ECS容器,加上ELB的随机分发策略:

  • 你的CI/CD流程大概率只在某一台ECS实例上执行了dehydrated脚本,生成的验证文件只存在于该实例的Nginx容器中。
  • ELB默认用轮询分发请求,Let's Encrypt会从多个全球节点发起验证请求,这些请求会被ELB分到不同的ECS实例。只有当所有请求都打到有验证文件的那台实例时,验证才会成功——这种概率极低,所以你才会看到偶尔1/20次的成功。
  • 日志里的部分200就是请求打到了那台正确的实例,而其他请求返回404/403,最终Let's Encrypt判定验证失败。

三、可落地的重构解决方案

给你几个不同复杂度的方案,按需选择:

方案1:用EFS共享验证文件(最通用)

在AWS ECS中挂载EFS到所有Nginx容器的$SSL_ROOT目录:

  • 创建一个EFS文件系统,配置ECS任务定义,把EFS挂载到容器的$SSL_ROOT路径。
  • 修改dehydrated脚本,直接在EFS目录下生成验证文件——这样所有容器都能访问到同一个目录里的文件,不管ELB分发到哪台,都能返回正确的验证内容。

方案2:路由验证请求到特定实例(轻量改造)

调整ELB的路由规则,把验证请求单独转发到一台指定的ECS实例:

  • 在ELB中添加一条路径匹配规则:/.well-known/acme-challenges/*,转发到一个标记了acme-validator标签的ECS服务/任务。
  • CI/CD脚本只在这个标记的任务上执行dehydrated,确保所有验证请求都打到有文件的实例。

方案3:改用DNS-01验证(最省心,适合Cloudflare用户)

既然你用Cloudflare管理DNS,直接切换到DNS-01验证方式:

  • 修改dehydrated的配置,启用Cloudflare DNS插件,利用Cloudflare的API自动添加TXT记录完成验证。
  • 这种方式不需要依赖HTTP流量,完全绕开了ELB和Nginx的分发问题,多实例场景下最可靠。

方案4:CI/CD同步执行到所有容器(无需额外AWS资源)

调整你的CI/CD流程,让dehydrated脚本在每一个运行中的Nginx容器里都执行一遍:

  • 用AWS CLI的aws ecs list-tasks获取所有运行中的任务ID,然后遍历每个任务,执行aws ecs execute-command来运行dehydrated脚本,确保每个容器都生成验证文件。

四、Cloudflare是否会导致该问题?

大概率会有影响,要检查这几个点:

  • 缓存规则:Cloudflare默认可能缓存静态文件,包括/.well-known/acme-challenges/下的内容。需要在Cloudflare的页面规则里添加一条:/.well-known/acme-challenges/*,设置Cache Level: Bypass,禁用缓存。
  • WAF拦截:Cloudflare的WAF可能会把Let's Encrypt的验证请求当成恶意请求拦截,导致返回403。去Cloudflare的防火墙日志里查有没有来自Let's Encrypt IP段的请求被拦截,有的话添加允许规则。
  • SSL与跳转设置:如果你的域名用了Cloudflare的橙色代理,要确保SSL模式是Full或Full (strict),并且没有强制把HTTP请求跳转到HTTPS——因为http-01验证必须用80端口的HTTP请求,强制跳转的话会导致验证失败。

内容的提问来源于stack exchange,提问作者iangolden

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:07:59