部署环境:Ubuntu 24.04

1. 前置准备

确保你的域名已正确解析到服务器公网 IP,并且 Nginx 已安装且配置了基本的 HTTP (80端口) 服务或指向了正确的网站根目录(Webroot)。

2. 签发证书 (Webroot 模式)

推荐使用 webroot 模式配合 Let’s Encrypt 官方源,该模式无需停止 Nginx,验证最稳定。

1
2
3
4
5
# 强制使用 Let's Encrypt 源,指定网站根目录进行验证
acme.sh --issue -d yourdomain.com \
  --webroot /path/to/your/webroot \
  --server letsencrypt \
  --force

注:看到 Verify successCert success 即表示签发成功。

3. 安装证书并配置自动重载

不要直接使用 ~/.acme.sh/ 目录下的文件,应使用 --install-cert 将证书复制到 Nginx 的专属目录,并绑定 Nginx 的重载命令,以实现未来续期时的全自动化。

1
2
3
4
5
6
7
8
# 1. 创建存放证书的目录
sudo mkdir -p /etc/nginx/ssl/

# 2. 安装证书并配置 reloadcmd
acme.sh --install-cert -d yourdomain.com \
  --key-file       /etc/nginx/ssl/yourdomain.com.key \
  --fullchain-file /etc/nginx/ssl/yourdomain.com.pem \
  --reloadcmd      "sudo nginx -s reload"

4. 配置 Nginx 启用 HTTPS

在 Nginx 配置文件中指定证书路径,并开启 HTTP/2 和现代 TLS 协议。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    ssl_certificate /etc/nginx/ssl/yourdomain.com.pem;
    ssl_certificate_key /etc/nginx/ssl/yourdomain.com.key;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    
    # 强制隐藏版本号
    server_tokens off;
    
    # ... 其他 root 和 location 配置 ...
}

5. 验证与续期测试

测试 Nginx 配置语法,并强制触发一次续期,验证全链路是否畅通。

1
2
3
4
5
6
7
8
# 测试nginx语法是否正确
sudo nginx -t

# 重启nginx服务,也可以使用nginx -s reload
sudo systemctl reload nginx

# 强制重新申请证书,来测试自动续期机制
acme.sh --renew -d yourdomain.com --force

6.补充测试命令

1
2
3
4
5
6
7
8
# 强制重新申请证书,来测试自动续期机制
acme.sh --renew -d yourdomain.com --force

# 列出 acme.sh 管理的所有证书 和过期时间
acme.sh --list

# 列出当前用户本机的所有定时任务,查看是否正常配置
crontab -l

7. 示例nginx配置

server {
    # 仅监听 443 端口,开启 HTTP/2
    listen 443 ssl http2;
    server_name yourdomain.com;

    # 1. 证书路径
    ssl_certificate /etc/nginx/ssl/yourdomain.com.pem;
    ssl_certificate_key /etc/nginx/ssl/yourdomain.com.key;

    # 2. 现代 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 on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # 3. 隐藏版本号 & 禁止目录浏览
    server_tokens off;
    autoindex off;

    # 4. 安全响应头
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # 5. 网站根目录
    root /var/www/myblog/public; 
    index index.html;

    # 6. 静态资源缓存 & Gzip
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 7d;
        add_header Cache-Control "public, no-transform";
        access_log off;
    }

    # 7. 核心路由
    location / {
        try_files $uri $uri/ =404;
    }
    
    # 自定义 404
    error_page 404 /404.html;
}

# 可选:如果你希望有人输入 http://yourdomain.com 时直接断开连接(极简且安全)
# server {
#     listen 80;
#     server_name yourdomain.com;
#     return 444; # Nginx 特有状态码,直接关闭连接,不返回任何 HTTP 响应
# }

踩坑记录

💣 坑1:acme.sh 缓存残留导致找不到证书文件

  • 报错现象The domain seems to already have an ECC cert... 随后提示 cat: .../fullchain.cer: No such file or directoryCannot find config file
  • 原因分析:之前中断的签发或错误的清理导致 acme.sh 内部记录与实际文件系统脱节。
  • 解决方案:彻底清理残留并强制重新签发。
    1
    2
    3
    
    acme.sh --remove -d yourdomain.com --ecc
    rm -rf ~/.acme.sh/yourdomain.com*
    # 重新签发时务必加上 --force
    

💣 坑2:SSL 证书加载失败 (PEM 格式错误 / 权限拒绝)

  • 报错现象nginx -t 提示 PEM_read_bio_X509_AUX() failed ... Expecting: TRUSTED CERTIFICATEPermission denied
  • 原因分析
    1. --key-file (私钥) 和 --fullchain-file (证书链) 的路径填反了。
    2. Nginx 运行用户(如 www-data)对证书目录没有读取权限。
  • 解决方案:确保路径对应正确,并修复目录权限(目录 755,pem 644,key 600)。

💣 坑3:80 端口被 Nginx 占用导致 Standalone 模式失败

  • 报错现象:无法使用 --standalone,尝试使用 --nginx 模式时报 Cannot find config file
  • 原因分析standalone 需要独占 80 端口;而 acme.sh--nginx 模式对非标准的 Nginx 配置文件结构解析能力较弱,容易“眼瞎”。
  • 解决方案:改用 --webroot 模式,直接指定 Nginx 的静态文件根目录进行验证,绕开 Nginx 进程本身的干扰。

💣 坑4:ZeroSSL 默认 CA 导致验证无限超时

  • 报错现象:日志显示 Pending... 多次后,提示 The retryafter=86400 value is too large... will not retry anymore
  • 原因分析acme.sh 默认切换到了 ZeroSSL CA,其验证机制在某些网络环境下会返回长达 24 小时的重试等待,导致脚本主动放弃。
  • 解决方案:在签发命令中显式指定使用 Let’s Encrypt:--server letsencrypt

💣 坑5:Nginx 安全规则误杀 ACME 验证路径 (403 Forbidden)

  • 报错现象:验证时提示 Invalid response from ... /.well-known/acme-challenge/...: 403
  • 原因分析:为了安全,Nginx 配置了 location ~ /\. 来拦截所有以 . 开头的隐藏文件(如 .git)。这不幸误杀了以 .well-known 开头的 ACME 验证路径。
  • 解决方案:利用 Nginx 的 ^~ 前缀匹配优先级,在拦截规则上方添加“绿色通道”:
    1
    2
    3
    4
    5
    6
    7
    
    # 优先级更高,放行验证请求
    location ^~ /.well-known/acme-challenge/ {
        default_type "text/plain";
        root /your/webroot;
    }
    # 原有的隐藏文件拦截规则
    location ~ /\. { deny all; }