强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 模式对比

特性WebrootStandalone
需要停止 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 模式最佳实践

  1. 使用独立的 ACME 验证目录: 推荐 /var/www/certbot,与网站内容分离
  2. 配置 snippet 复用: 将 ACME 配置放在 Nginx snippet 中,多个站点共享
  3. 确保路径不被重定向: ACME 验证路径必须直接响应,不能被 301/302 重定向
  4. 检查文件权限: Web 服务器用户必须有读取权限
  5. 定期测试: 使用 --dry-run 验证续期是否正常

扩展阅读