acme.sh 换用 webroot 模式后踩到的权限坑
acme.sh 默认可以用 standalone 模式签证书:临时起一个 80 端口的服务完成验证,签完就关。但这要求 80 端口当时是空闲的,如果机器上已经有 Nginx,就得先停掉,很别扭。
webroot 模式更省事:把验证文件写进站点根目录,让现有的 Nginx 去提供服务。
acme.sh --issue --webroot /var/www/site -d example.com --keylength ec-256
坑一:验证目录必须真的能被访问到
acme.sh 会把文件写到 /var/www/site/.well-known/acme-challenge/。如果你的 Nginx 里给这个域名写的是:
location / { return 301 https://$host$request_uri; }
那验证请求也会被 301 到 https,而 Let's Encrypt 只走 http。正确写法是把 challenge 路径单独拿出来,放在跳转之前:
location /.well-known/acme-challenge/ {
root /var/www/site;
default_type text/plain;
}
location / { return 301 https://$host$request_uri; }
Nginx 的 location 匹配里,^~ 和精确匹配 = 优先于普通前缀匹配,所以这种写法是安全的,不依赖书写顺序。
坑二:目录权限
如果站点目录是 root:root 755,而 Nginx worker 以 www-data 或 nginx 身份运行,读 .well-known 下面的文件是没问题的(文件默认 644)。
但如果这一层里有一级目录是 700,worker 就进不去,返回 403。验证失败时先看 Nginx 的 error.log,里面会明确写 Permission denied 还是 No such file。
坑三:重载后立刻测
改完配置要等 reload 真正完成再测。快速验证:
echo test > /var/www/site/.well-known/acme-challenge/t.txt
curl -H "Host: example.com" http://127.0.0.1/.well-known/acme-challenge/t.txt
返回 test 才算通。很多人在这里拿到 404,其实是 reload 还没生效就去测了。
续期
签完之后用 --install-cert 把证书装到固定路径,并指定 reload 命令。acme.sh 的 cron 每天跑一次,到期前一个月会自动续。