Skip to content

HTTPS 免费证书申请与自动续期指南 ​

定位:本文档旨在提供一套在 Linux 服务器上通过 acme.sh 申请 Let's Encrypt 免费 SSL 证书,并与 Nginx (特别是 Docker 容器化 Nginx) 完美结合的标准操作流程。

核心优势:无需手动干预,证书到期前自动续期并热重载 Nginx。


1. 安装与初始化 acme.sh ​

acme.sh 是一个极为轻量且强大的 ACME 协议客户端。

1.1 一键安装 ​

bash
# 下载并安装 acme.sh
curl https://get.acme.sh | sh

# 使环境变量立即生效
source ~/.bashrc

1.2 注册邮箱并切换默认 CA ​

目前 acme.sh 默认使用 ZeroSSL,建议切换回兼容性更好的 Let's Encrypt。

bash
# 注册邮箱(请替换为你的真实邮箱,用于接收证书过期提醒)
acme.sh --register-account -m your_email@example.com --server letsencrypt

# 将 Let's Encrypt 设置为默认 CA
acme.sh --set-default-ca --server letsencrypt

2. 申请证书 (Webroot 验证模式) ​

前提条件:

  1. 你的域名(如 app.example.com)已正确解析到当前服务器的公网 IP。
  2. 服务器已开放 80 端口,且 Nginx 正在运行。

2.1 Nginx 验证配置 ​

为了让 Let's Encrypt 能够通过 80 端口验证你对域名的所有权,我们需要在 Nginx 配置中指定一个静态资源目录(Webroot)。

在你的 Nginx 配置 (conf.d/app.conf) 中添加:

nginx
server {
    listen 80;
    server_name app.example.com;

    # 专门用于 ACME 验证的路径
    location ^~ /.well-known/acme-challenge/ {
        # 这里的路径必须映射到宿主机上的某个目录,例如 ./static
        alias /usr/share/nginx/html/static/.well-known/acme-challenge/;
        try_files $uri =404;
    }

    # 其他业务逻辑或跳转...
}

2.2 签发证书 ​

假设你在宿主机的 Nginx 网关目录下,静态目录位于 ./static/,执行以下命令签发证书:

bash
acme.sh --issue -d app.example.com --webroot ./static/ --server letsencrypt

如果一切正常,终端会显示绿色字体的 Cert success。


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

⚠️ 警告:千万不要直接使用 ~/.acme.sh/ 目录下的证书文件,这些是内部文件,路径结构可能会变。必须使用 --install-cert 命令将证书“导出”到你的业务目录。

3.1 安装到 Nginx 挂载目录 ​

假设你的 Nginx SSL 证书挂载目录为宿主机的 ./ssl/:

bash
# 确保目录存在
mkdir -p ./ssl/

# 如果你是普通用户,可能需要修复权限
# sudo chown -R $(whoami):$(whoami) ./ssl/

# 安装证书,并配置自动续期时的重载命令
acme.sh --install-cert -d app.example.com \
  --key-file       ./ssl/app.example.com.key \
  --fullchain-file ./ssl/app.example.com.crt \
  --reloadcmd      "docker exec hub-nginx nginx -s reload"

参数说明:

  • --reloadcmd:当 acme.sh 自动续期成功后,会自动执行这条命令让 Nginx 重新加载证书,无需重启容器。

4. Nginx 业务配置 ​

证书就绪后,我们需要配置 Nginx 处理 HTTPS 流量。这里提供两种常见的业务模式。

模式 A:强制 HTTPS 跳转 (推荐) ​

最安全的标准模式,所有 HTTP 流量都会被 301 重定向到 HTTPS。

nginx
# 80 端口配置
server {
    listen 80;
    server_name app.example.com;

    # 保留 ACME 验证路径
    location ^~ /.well-known/acme-challenge/ {
        alias /usr/share/nginx/html/static/.well-known/acme-challenge/;
        try_files $uri =404;
    }

    # 其他所有请求强制跳转 HTTPS
    location / {
        return 301 https://$host$request_uri;
    }
}

# 443 端口配置
server {
    listen 443 ssl;
    server_name app.example.com;

    # 证书路径 (容器内的路径)
    ssl_certificate /etc/nginx/ssl/app.example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/app.example.com.key;

    location / {
        proxy_pass http://backend-app:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
    }
}

模式 B:HTTP 与 HTTPS 并存 (兼容模式) ​

如果不希望破坏现有的纯 HTTP 链路(例如某些老旧客户端不支持 HTTPS,或为了最小化对已有架构的改动),可以配置两者并存,不进行跳转。

nginx
# 80 端口正常提供服务
server {
    listen 80;
    server_name app.example.com;

    location ^~ /.well-known/acme-challenge/ {
        alias /usr/share/nginx/html/static/.well-known/acme-challenge/;
        try_files $uri =404;
    }

    location / {
        proxy_pass http://backend-app:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # 传递 scheme 为 http
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

# 443 端口同样提供服务
server {
    listen 443 ssl;
    server_name app.example.com;

    ssl_certificate /etc/nginx/ssl/app.example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/app.example.com.key;

    location / {
        proxy_pass http://backend-app:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # 传递 scheme 为 https
        proxy_set_header X-Forwarded-Proto https;
    }
}

5. 重载与验证 ​

配置完成后,测试 Nginx 配置并使其生效:

bash
docker exec hub-nginx nginx -t
docker exec hub-nginx nginx -s reload

验证方法:

  • 打开浏览器访问 https://app.example.com,点击地址栏的锁图标,确认证书颁发机构为 Let's Encrypt,且无安全警告。

6. 日常运维说明 ​

  1. 自动续期:acme.sh 安装时已自动在系统中添加了 Cron 定时任务,每天会检查证书是否即将过期(通常是到期前 30 天内),如果满足条件将自动续期并执行配置的 reloadcmd。你无需任何手动干预。

  2. 强制续期:如果需要手动强制续期,可执行:

    bash
    acme.sh --renew -d app.example.com --force

方舟 · Ark