今天访问博客,突然报证书过期,原本是写了定期执行证书更新的任务的,可看了一下不知为何貌似没有生效,只好手动执行。
证书颁发机构要验证域名的所有权,因此脚本会向站点的一个路径下写入一个文件,然后通过网络来请求它,如果成功就认为站点确实是证书申请者所有。但本次的执行结果说写入文件成功,但访问失败了。之前是遇到过这个问题问题的,我有一次给 nginx 增加了一条放在 .drop 文件里的规则,所有以点号(.)起始的 URL 请求全部忽略,这直接导致了证书颁发过程中的验证失败,因为验证请求的 URL 是以“.well-known”打头的,后来做了修订,把这个问题解决掉了,最后的规则像下面这样:
1 2 3 4 5 |
# Rather than just denying .ht* in the config, why not deny # access to all .invisible files location ~ /\. { deny all; access_log off; log_not_found off; } # But, allow this one location ^~ /.well-known/ {} |
这次问题再次出现,开始没有想清楚原因,后来一想明白了。因为我强制开启了 HTTPS,原本的 HTTP 请求被重新定向到 HTTPS,但 HTTPS 又恰逢证书过期而不能正常服务,从而形成了这个局面。即然这样,思路就有了,把 HTTP 的 server 块配置调整一下,对于 .well-known 相关的 URL 不进行重定向就好。写的时候发现有实操困难,如何在 nginx 的配置里写一条否定匹配的规则呢?搜索之后发现结论是:不能(其实这个结论从上面的示例配置上就能看出来),最后的配置如下:
1 2 3 4 |
location ^~ /.well-known/acme-challenge/ {} location / { return 301 https://$server_name$request_uri; } |
重启 nginx 后发现还有问题,访问指定的文件返回的是 404。排查了半天,才发现是当初做 HTTP 转 HTTPS 的时候,把几乎所有的配置都挪走了,包括 root 这个文档根目录的配置,加回来就好了。
最后完美实现了证书更新过程中 HTTP 请求的正常响应,同时保证了其他的请求则全部重定向为 HTTPS 请求。
参考文章:Nginx 配置 location 匹配规则总结。