Certbot 证书自动化教程 / 第 4 章:Webroot 模式
第 4 章:Webroot 模式
4.1 Webroot 模式概述
Webroot(网站根目录)模式是 Certbot 最常用的验证方式之一。它利用已有的 Web 服务器来响应 Let’s Encrypt 的验证请求,无需停止现有服务,也不会造成任何停机时间。
工作原理
┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Let's Encrypt│ │ Web 服务器 │ │ Certbot │
│ 服务器 │ │ (Nginx/Apache) │ │ │
└──────┬───────┘ └────────┬─────────┘ └────────┬─────────┘
│ │ │
│ 1. Certbot 生成验证文件 │
│ │ <───────────────────────│
│ │ 写入验证文件到 │
│ │ /var/www/certbot/ │
│ │ .well-known/ │
│ │ acme-challenge/<token> │
│ │ │
│ 2. Let's Encrypt 请求验证 │
│ GET /.well-known/ │ │
│ acme-challenge/token│ │
│ ───────────────────> │ │
│ <─────────────────── │ │
│ 返回验证文件内容 │ │
│ │ │
│ 3. 验证通过 │ │
│ ─────────────────────────────────────────────>│
│ │ │
与 Standalone 模式对比
| 特性 | Webroot | Standalone |
|---|---|---|
| 需要停止 Web 服务器 | ❌ 不需要 | ✅ 需要 |
| 需要已运行的 Web 服务器 | ✅ 需要 | ❌ 不需要 |
| 停机时间 | 无 | 短暂 |
| 配置复杂度 | 中等 | 低 |
| 适用场景 | 生产环境 | 首次申请 |
4.2 基本使用
前提条件
- 已有 Web 服务器(Nginx/Apache)在运行
- Web 服务器配置了正确的域名
.well-known/acme-challenge/路径可被外部访问
创建 Webroot 目录
# 创建验证文件目录
sudo mkdir -p /var/www/certbot/.well-known/acme-challenge
# 设置权限(Nginx 用户通常为 www-data)
sudo chown -R www-data:www-data /var/www/certbot
sudo chmod -R 755 /var/www/certbot
基本命令
# 使用 webroot 模式申请证书
sudo certbot certonly --webroot \
-w /var/www/certbot \
-d example.com \
--agree-tos \
--email admin@example.com \
--non-interactive
多域名不同 Webroot
# 不同域名对应不同的 webroot 路径
sudo certbot certonly \
--webroot -w /var/www/site1 -d site1.com \
--webroot -w /var/www/site2 -d site2.com \
--agree-tos \
--email admin@example.com
命令参数详解
| 参数 | 说明 | 示例 |
|---|---|---|
certonly | 仅获取证书,不安装 | - |
--webroot | 使用 Webroot 模式 | - |
-w / --webroot-path | 指定网站根目录 | -w /var/www/certbot |
-d | 指定域名 | -d example.com |
--agree-tos | 同意服务条款 | - |
--email | 注册邮箱 | --email admin@example.com |
4.3 Nginx 配置
为了让 Nginx 能够正确响应 ACME 验证请求,需要配置一个专门的 location 块。
方式一:添加 location 块
# /etc/nginx/sites-available/example.com
server {
listen 80;
server_name example.com www.example.com;
# ACME 验证路径
location /.well-known/acme-challenge/ {
root /var/www/certbot;
allow all;
}
# 其他请求(后续可改为 HTTPS 重定向)
location / {
root /var/www/html;
index index.html;
}
}
方式二:全局 ACME 配置
# /etc/nginx/snippets/acme-challenge.conf
location /.well-known/acme-challenge/ {
root /var/www/certbot;
allow all;
default_type "text/plain";
}
# 在 server 块中引用
server {
listen 80;
server_name example.com;
include /etc/nginx/snippets/acme-challenge.conf;
# ... 其他配置
}
方式三:多个站点共享
# /etc/nginx/snippets/acme-challenge.conf
location /.well-known/acme-challenge/ {
root /var/www/certbot;
allow all;
default_type "text/plain";
try_files $uri =404;
}
# 站点 1
server {
listen 80;
server_name site1.com;
include /etc/nginx/snippets/acme-challenge.conf;
root /var/www/site1;
}
# 站点 2
server {
listen 80;
server_name site2.com;
include /etc/nginx/snippets/acme-challenge.conf;
root /var/www/site2;
}
验证 Nginx 配置
# 测试配置语法
sudo nginx -t
# 重新加载配置
sudo systemctl reload nginx
# 测试 ACME 路径是否可访问
curl -I http://example.com/.well-known/acme-challenge/test
# 期望返回 404(路径正确但无文件),而不是 403 或其他错误
4.4 Apache 配置
方式一:使用 Alias
# /etc/apache2/sites-available/example.com.conf
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/html
# ACME 验证路径
Alias /.well-known/acme-challenge/ /var/www/certbot/.well-known/acme-challenge/
<Directory "/var/www/certbot/.well-known/acme-challenge/">
Options None
AllowOverride None
Require all granted
</Directory>
</VirtualHost>
方式二:使用 .htaccess
# 确保主配置允许 .htaccess 覆盖
<Directory /var/www/html>
AllowOverride All
</Directory>
# /var/www/html/.htaccess
# 确保 ACME 路径可访问
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^.well-known/acme-challenge/ - [L]
</IfModule>
验证 Apache 配置
# 测试配置语法
sudo apache2ctl configtest
# 或
sudo apachectl configtest
# 重新加载配置
sudo systemctl reload apache2
# 测试 ACME 路径
curl -I http://example.com/.well-known/acme-challenge/test
4.5 其他 Web 服务器配置
Caddy
Caddy 2 内置 ACME 支持,通常不需要 Certbot。如果仍需使用:
example.com {
root * /var/www/html
file_server
handle /.well-known/acme-challenge/* {
root * /var/www/certbot
file_server
}
}
Lighttpd
# /etc/lighttpd/lighttpd.conf
$HTTP["url"] =~ "^/\.well-known/acme-challenge/" {
server.document-root = "/var/www/certbot/"
}
Node.js (Express)
const express = require('express');
const app = express();
// ACME 验证路径
app.use('/.well-known/acme-challenge',
express.static('/var/www/certbot/.well-known/acme-challenge'));
app.listen(80);
4.6 完整部署示例
场景:已有 Nginx 站点首次启用 HTTPS
#!/bin/bash
# file: enable-https.sh
DOMAIN="example.com"
WEBROOT="/var/www/certbot"
EMAIL="admin@example.com"
# Step 1: 创建 webroot 目录
echo "创建 ACME 验证目录..."
sudo mkdir -p "$WEBROOT/.well-known/acme-challenge"
sudo chown -R www-data:www-data "$WEBROOT"
# Step 2: 配置 Nginx 的 ACME 路径
echo "配置 Nginx..."
sudo tee /etc/nginx/snippets/acme-challenge.conf > /dev/null << 'EOF'
location /.well-known/acme-challenge/ {
root /var/www/certbot;
allow all;
default_type "text/plain";
try_files $uri =404;
}
EOF
# Step 3: 测试并重新加载 Nginx
echo "测试 Nginx 配置..."
sudo nginx -t || exit 1
sudo systemctl reload nginx
# Step 4: 测试 ACME 路径
echo "测试 ACME 路径..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
"http://$DOMAIN/.well-known/acme-challenge/test")
if [ "$HTTP_CODE" = "404" ]; then
echo "✓ ACME 路径正常(返回 404)"
else
echo "✗ ACME 路径异常(返回 $HTTP_CODE)"
exit 1
fi
# Step 5: 申请证书
echo "申请证书..."
sudo certbot certonly --webroot \
-w "$WEBROOT" \
-d "$DOMAIN" \
-d "www.$DOMAIN" \
--agree-tos \
--email "$EMAIL" \
--non-interactive
if [ $? -eq 0 ]; then
echo "✓ 证书申请成功"
sudo certbot certificates --cert-name "$DOMAIN"
else
echo "✗ 证书申请失败"
exit 1
fi
echo "完成!"
4.7 Nginx HTTPS 配置
证书申请成功后,配置 Nginx 使用证书启用 HTTPS:
# /etc/nginx/sites-available/example.com.conf
# HTTP → HTTPS 重定向
server {
listen 80;
server_name example.com www.example.com;
# ACME 验证路径(续期时需要)
location /.well-known/acme-challenge/ {
root /var/www/certbot;
allow all;
}
# 其他请求重定向到 HTTPS
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS 配置
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL 证书
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL 优化
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# 站点配置
root /var/www/html;
index index.html;
}
# 测试并重载
sudo nginx -t && sudo systemctl reload nginx
4.8 排错指南
常见错误
错误 1:404 Not Found
Challenge failed for domain example.com
http-01 challenge for example.com
排查步骤:
# 1. 确认 webroot 路径正确
ls -la /var/www/certbot/.well-known/acme-challenge/
# 2. 手动创建测试文件
echo "test" | sudo tee /var/www/certbot/.well-known/acme-challenge/test-file
# 3. 测试是否可以访问
curl http://example.com/.well-known/acme-challenge/test-file
# 4. 检查 Nginx 配置
sudo nginx -T | grep -A5 "acme-challenge"
错误 2:403 Forbidden
Unauthorized: 403 Forbidden
解决方案:
# 检查文件权限
ls -la /var/www/certbot/.well-known/
# 修复权限
sudo chown -R www-data:www-data /var/www/certbot
sudo chmod -R 755 /var/www/certbot
# 检查 SELinux(CentOS/RHEL)
sudo setsebool -P httpd_read_user_content 1
# 或
sudo chcon -R -t httpd_sys_content_t /var/www/certbot
错误 3:重定向导致验证失败
Redirect problem: http://example.com/.well-known/acme-challenge/token -> https://...
解决方案:
# 确保 ACME 路径不被重定向
location /.well-known/acme-challenge/ {
root /var/www/certbot;
allow all;
}
# 其他请求才重定向
location / {
return 301 https://$host$request_uri;
}
错误 4:多站点冲突
Certbot could not determine the webroot for example.com
解决方案:
# 明确指定每个域名的 webroot
sudo certbot certonly \
--webroot -w /var/www/site1 -d site1.com \
--webroot -w /var/www/site2 -d site2.com
4.9 Webroot 模式最佳实践
- 使用独立的 ACME 验证目录: 推荐
/var/www/certbot,与网站内容分离 - 配置 snippet 复用: 将 ACME 配置放在 Nginx snippet 中,多个站点共享
- 确保路径不被重定向: ACME 验证路径必须直接响应,不能被 301/302 重定向
- 检查文件权限: Web 服务器用户必须有读取权限
- 定期测试: 使用
--dry-run验证续期是否正常