文档样式:

证书制作

基于 Let's Encrypt ACME v2 协议,免费申请和管理 SSL/TLS 证书。

这里有什么

快速开始

  1. 在申请表输入域名(一行一个)和联系邮箱
  2. 先用 Staging 测试环境验证流程(不限频率)
  3. 通过后切换为 Production 生产环境,正式申请证书
  4. 将生成的 fullchain.pem 和 private.pem 配置到 Web 服务器

申请步骤详解

1. 域名准备

  • 域名须已解析到本服务器 IP,否则 HTTP-01 验证会失败
  • 支持 SAN 多域名证书:第一个域名为主域名,其余为附加域名
  • 支持裸域名和 www 子域名同时申请

2. 邮箱

用于 Let's Encrypt 账户注册,接收证书到期提醒邮件。

3. 环境选择

环境 说明
Staging(测试) 无速率限制,颁发不受信任的测试证书(浏览器会警告)
Production(生产) 颁发受浏览器信任的正式证书,每域名每周限 5 次

4. HTTP-01 验证

Let's Encrypt 会访问 http://<你的域名>/.well-known/acme-challenge/<token> 验证域名控制权。请确保:

  • .well-known/acme-challenge/ 目录存在且 PHP 进程用户可写
  • Nginx 未拦截 /.well-known/ 路径
  • 80 端口可从公网访问

证书部署

Nginx 配置示例

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    ssl_certificate     /usr/share/nginx/html/ssl_certs/your-domain.com/fullchain.pem;
    ssl_certificate_key /usr/share/nginx/html/ssl_certs/your-domain.com/private.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers on;

    add_header Strict-Transport-Security "max-age=63072000" always;

    location / {
        root /usr/share/nginx/html;
        index index.php index.html;
    }
}

配置后执行 nginx -t && nginx -s reload 生效。


自动续期

证书有效期 90 天。本页面提供四种语言的续期脚本,无需 PHP 环境即可独立运行:

语言 脚本文件 依赖
PHP cert_renew.php PHP + openssl + curl
Python cert_renew.py Python 3.6+ stdlib + openssl + curl
Perl cert_renew.pl Perl 5 核心模块 + openssl + curl
Bash cert_renew.sh bash + openssl + curl + python3

Crontab 配置

# 每天早上 3 点检查并续期(PHP 版)
0 3 * * * php /usr/share/nginx/html/api/cert_renew.php --email=admin@example.com >> /var/log/cert-renew.log 2>&1

续期脚本会扫描 ssl_certs/ 目录下所有证书,到期前 30 天自动申请新证书。

续期参数

参数 说明
--email=xxx Let's Encrypt 账户邮箱
--force 强制续期,忽略 30 天窗口
--dry-run 试运行,仅检查不申请
--staging 使用测试环境

文件目录结构

ssl_certs/
├── account/
│   ├── private.pem        # ACME 账户私钥
│   ├── account_url.txt    # 账户 URL
│   └── email.txt          # 账户邮箱
├── your-domain.com/
│   ├── fullchain.pem      # 完整证书链
│   ├── private.pem        # 证书私钥
│   └── cert.pem           # 单独证书(不含链)
└── ...

.well-known/acme-challenge/   # HTTP-01 验证临时文件(自动清理)

常见问题

Q: 测试环境证书浏览器不信任?
A: 正常现象。Staging 证书由 (STAGING) Let's Encrypt 颁发,仅供测试。切换到 Production 即可获得受信任的证书。

Q: 验证失败?
A: 检查:1) 域名 DNS 是否解析到本服务器 2) .well-known/acme-challenge/ 目录权限 3) 80 端口是否开放。

Q: 如何续期?
A: 下载续期脚本,配置 crontab 即可自动续期。也可在页面重新申请(会覆盖旧证书)。

Q: 证书存储在哪里?
A: ssl_certs/<域名>/ 目录下。该目录已加入 .gitignore,私钥不会入库。

Q: 测试和生产有什么区别?

⏺ Let's Encrypt 的测试环境 (Staging) 和生产环境 (Production) 核心区别:

Staging(测试) Production(生产)
证书信任 浏览器不信任,访问会报警告 浏览器受信任,正常 HTTPS
速率限制 无限制,随便申请 每域名每周 5 次,超限锁定 7 天
颁发机构 (STAGING) Let's Encrypt Let's Encrypt / R3
用途 测试流程是否跑通 正式上线使用
API 地址 acme-staging-v02.api.letsencrypt.org acme-v02.api.letsencrypt.org

建议流程: 先在 Staging 测试整个申请链路(域名验证、challenge、CSR、下载),确认无报错后再切 Production 申请正式证书。避免因流程错误浪费生产环境的额度。

环境预检

cURL:正常
OpenSSL:正常
Challenge 目录:就绪
证书存储目录:就绪

SSL 证书检查器

支持输入域名或 URL,例如 example.com 或 https://example.com

申请 SSL 证书

支持多个域名(SAN 证书),用换行、逗号和空格分隔
Let's Encrypt 账号关联邮箱,用于证书到期提醒
验证方式: DNS API:自动通过服务商 API 添加/删除 TXT 记录,全自动验证
环境: 测试环境不限频率,生产环境有速率限制(每域名/周5次)

已申请证书

域名颁发者到期时间剩余天数操作
www.heyanper.top Let's Encrypt 2026-11-05 23:24:42 0 天 证书 私钥 删除
www.media9988.cn Let's Encrypt 2026-11-06 10:27:19 0 天 证书 私钥 删除

自动续期(Crontab)

Let's Encrypt 证书有效期为 90 天,建议设置定时任务自动续期。续期脚本会扫描已有证书,到期前 30 天内自动申请新证书。提供四种独立实现,均无需 PHP。
续期脚本 (cert_renew.php)
php /usr/share/nginx/html/api/cert_renew.php --email=admin@example.com
Crontab 配置(每天凌晨 3 点检查)
0 3 * * * php /usr/share/nginx/html/api/cert_renew.php --email=admin@example.com >> /var/log/cert-renew.log 2>&1
直接调用 AcmeClient 类,仅需 PHP + openssl + curl 扩展。
可用参数
--email=xxxLet's Encrypt 账户邮箱(必填,也可存于 ssl_certs/account/email.txt)
--force强制续期(忽略 30 天窗口限制)
--dry-run试运行(仅检查,不实际申请)
--staging使用 Let's Encrypt 测试环境
设置 crontab:crontab -e,粘贴上方配置即可。日志写入 /var/log/cert-renew.log。

Nginx HTTPS 配置参考

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    ssl_certificate     /usr/share/nginx/html/ssl_certs/your-domain.com/fullchain.pem;
    ssl_certificate_key /usr/share/nginx/html/ssl_certs/your-domain.com/private.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers on;

    # HSTS (可选)
    add_header Strict-Transport-Security "max-age=63072000" always;

    location / {
        root /usr/share/nginx/html;
        index index.php index.html;
    }
}