Nginx SSL 證書配置與自動續期工具
適配 Let's Encrypt 免費通配符證書、付費 OV 通配 SSL,自動生成 Nginx 全站 HTTPS 配置與 acme.sh/certbot DNS 驗證續期腳本。
觸發條件
滿足以下任一場景時啟用此 skill:
- 用戶提到 SSL、證書、HTTPS、Let's Encrypt、acme.sh、certbot、通配符證書
- 用戶要求配置域名 HTTPS、強制跳轉 443、證書續期
- 用戶使用以下關鍵詞:SSL 證書、Nginx 配置、證書過期、Wildcard SSL、DNS 驗證
第一部分:Nginx HTTPS 配置生成
步驟 1:收集用戶環境資訊
必填:
- 主域名(例如:example.com)
- 是否為通配符證書(*.example.com)
- 服務器系統(Ubuntu / Debian / CentOS / Rocky)
- 現有 Nginx 配置路徑(通常為 /etc/nginx/)
- 證書類型:Let's Encrypt 免費 / 付費 OV / 自簽名
- 網站根目錄路徑(例如:/var/www/html)
選填:
- 是否需要 HTTP 強制跳轉 HTTPS
- 是否有多個子域名需要配置
- 是否需要 OCSP Stapling
- 是否需要 HSTS 頭
步驟 2:生成標準 HTTPS 配置
單域名 HTTPS 配置:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# 強制跳轉 HTTPS(生產環境推薦開啟)
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# 證書路徑
ssl_certificate /etc/nginx/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/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 1d;
ssl_session_tickets off;
# HSTS(開啟後無法回退 HTTP,謹慎使用)
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# OCSP Stapling(提升 HTTPS 驗證速度)
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# 網站根目錄
root /var/www/html;
index index.html index.php;
# 日誌
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
# 安全頭
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
location / {
try_files $uri $uri/ =404;
}
# PHP 配置(如需要)
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
}
# 禁止訪問隱藏文件
location ~ /\. {
deny all;
}
}
通配符 HTTPS 配置(多子域名共用):
# 主域名 HTTP -> HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com api.example.com;
# Let's Encrypt DNS 驗證路徑(申請時需要,之後可刪除或保留)
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /var/www/letsencrypt;
}
location / {
return 301 https://$host$request_uri;
}
}
# API 子域名
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/example.com/privkey.pem;
# 安全配置(同上)
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 1d;
root /var/www/api;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
}
}
步驟 3:配置生效與驗證
# 1. 測試配置語法
nginx -t
# 2. 重新加載配置
systemctl reload nginx
# 3. 驗證 HTTPS 生效
curl -I https://example.com
curl -I https://api.example.com
# 4. SSL 實驗室評級測試(在線工具)
# https://www.ssllabs.com/ssltest/analyze.html?d=example.com
第二部分:Let's Encrypt 通配符證書申請
步驟 1:安裝 acme.sh
# Ubuntu / Debian
apt update && apt install -y socat
curl https://get.acme.sh | sh -s email=admin@example.com
# CentOS / Rocky
yum install -y socat
curl https://get.acme.sh | sh -s email=admin@example.com
# 安裝後重啟 shell 或使用絕對路徑
source ~/.bashrc
# 常用命令路徑
alias acme.sh=~/.acme.sh/acme.sh
步驟 2:DNS API 自動驗證(推薦方式)
通配符證書必須使用 DNS 驗證,以下是常見 DNS 提供商配置:
Cloudflare(推薦,最簡單):
# 設置 API Token(Cloudflare 面板生成)
export CF_Token="your_cf_api_token"
# 申請通配符證書
~/.acme.sh/acme.sh --issue --dns dns_cf \
-d example.com \
-d '*.example.com' \
--keylength ec-256
阿里雲 DNS:
export Ali_Key="your_access_key_id"
export Ali_Secret="your_access_key_secret"
~/.acme.sh/acme.sh --issue --dns dns_ali \
-d example.com \
-d '*.example.com'
騰訊雲 DNSPod:
export DP_Id="your_dp_id"
export DP_Key="your_dp_key"
~/.acme.sh/acme.sh --issue --dns dns_dp \
-d example.com \
-d '*.example.com'
步驟 3:安裝證書到 Nginx 目錄
# 創建證書目錄
mkdir -p /etc/nginx/ssl/example.com
# 安裝證書(自動配置更新)
~/.acme.sh/acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/example.com/privkey.pem \
--fullchain-file /etc/nginx/ssl/example.com/fullchain.pem \
--reloadcmd "systemctl reload nginx"
步驟 4:HTTP 文件驗證(備選,單域名)
# 如果 DNS 驗證不可用,可用 HTTP 驗證
# 先創建驗證目錄
mkdir -p /var/www/letsencrypt
# 申請證書
~/.acme.sh/acme.sh --issue -d example.com -d www.example.com \
--webroot /var/www/letsencrypt
第三部分:自動續期腳本
步驟 1:acme.sh 自動續期(默認已開啟)
# acme.sh 默認每天自動檢查並續期
# 查看當前任務
crontab -l | grep acme
# 輸出類似:
# 0 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
步驟 2:自定義 Nginx + acme.sh 完整腳本
#!/bin/bash
# /opt/scripts/ssl-renew.sh
# 用途:手動觸發證書更新並驗證 Nginx 配置
CF_Token="your_cf_api_token"
export CF_Token
DOMAINS=("example.com" "another.com")
for domain in "${DOMAINS[@]}"; do
echo "[$(date)] Renewing certificate for: $domain"
# 更新證書
~/.acme.sh/acme.sh --renew -d "$domain" \
--dns dns_cf \
--keylength ec-256
# 重載 Nginx
systemctl reload nginx
# 記錄日誌
echo "[$(date)] Certificate renewed for $domain" >> /var/log/ssl-renew.log
done
步驟 3:過期告警腳本
#!/bin/bash
# /opt/scripts/ssl-expiry-alert.sh
# 用途:證書到期前 7 天發送告警
CERT_DIR="/etc/nginx/ssl/example.com"
DAYS_WARN=7
ADMIN_EMAIL="admin@example.com"
# 檢查 cert 文件到期時間
CERT_FILE="$CERT_DIR/fullchain.pem"
EXPIRY_DATE=$(openssl x509 -in "$CERT_FILE" -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
if [ $DAYS_LEFT -le $DAYS_WARN ]; then
echo "SSL certificate for example.com expires in $DAYS_LEFT days!" | \
mail -s "[ALERT] SSL Certificate Expiring Soon" "$ADMIN_EMAIL"
echo "[$(date)] Alert sent: $DAYS_LEFT days remaining" >> /var/log/ssl-alert.log
else
echo "[$(date)] Certificate OK: $DAYS_LEFT days remaining" >> /var/log/ssl-alert.log
fi
Crontab 部署:
# 每天早上 9 點檢查證書過期
echo "0 9 * * * root /opt/scripts/ssl-expiry-alert.sh" >> /etc/crontab
# 手動測試
/opt/scripts/ssl-expiry-alert.sh
cat /var/log/ssl-alert.log
第四部分:付費 OV SSL 證書配置
步驟 1:付費證書申請後的 Nginx 配置
付費 OV 證書與 Let's Encrypt 配置完全相同,區別在於路徑:
server {
listen 443 ssl http2;
server_name example.com;
# 付費證書路徑(由 CA 提供)
ssl_certificate /etc/nginx/ssl/example.com/certificate.crt;
ssl_certificate_key /etc/nginx/ssl/example.com/private.key;
# 如果 CA 提供中間證書,合併到 fullchain
# cat certificate.crt intermediate.crt > fullchain.crt
# 其餘配置與 Let's Encrypt 相同
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers '...';
root /var/www/html;
location / {
try_files $uri $uri/ =404;
}
}
步驟 2:證書格式轉換
# PEM 轉 PKCS12(Java/Tomcat 可能需要)
openssl pkcs12 -export \
-in fullchain.pem \
-inkey privkey.pem \
-out keystore.p12 \
-name "tomcat" \
-password pass:your_password
# PEM 轉 DER(部分設備需要)
openssl x509 -outform der -in fullchain.pem -out certificate.der
第五部分:故障排查
故障一:證書申請失敗 - DNS 驗證未生效
# 1. 檢查 DNS TXT 記錄是否生效
dig -t txt _acme-challenge.example.com
# 2. 等待 DNS 傳播(通常 5 分鐘 - 24 小時)
# 期間可測試:
~/.acme.sh/acme.sh --issue --dns dns_cf \
-d example.com \
--test
# 3. 確認 Cloudflare API Token 權限
# 需要 Zone.Zone:Read + Zone.DNS:Edit
故障二:HTTPS 混合內容警告
# 1. 檢查頁面源碼中的 http:// 引用
curl -s https://example.com | grep -o 'http://[^"]*'
# 2. 批量替換為 https://
# 在 Nginx 配置中添加:
sub_filter 'http://example.com' 'https://example.com';
sub_filter_types text/html;
# 或使用 PHP/代碼層替換
故障三:證書與域名不匹配
# 檢查證書綁定的域名
openssl x509 -in /etc/nginx/ssl/example.com/fullchain.pem -noout -subject
# 檢查 CN 和 SAN
openssl x509 -in /etc/nginx/ssl/example.com/fullchain.pem -noout -text \
| grep -A1 "Subject Alternative Name"
# 錯誤:證書只有 example.com,但配置了 api.example.com
# 解決:重新申請包含所有子域名的證書
~/.acme.sh/acme.sh --renew -d example.com \
-d '*.example.com' \
--force
故障四:Nginx SSL 配置錯誤
# 1. 測試配置
nginx -t
# 2. 查看詳細錯誤
nginx -t 2>&1 | grep -v "syntax is ok" | grep -v "test is successful"
# 3. 常見錯誤:
# - ssl_certificate_key 權限不足
chmod 600 /etc/nginx/ssl/example.com/privkey.pem
chown root:root /etc/nginx/ssl/example.com/privkey.pem
# - 證書文件格式錯誤(應為 PEM)
file /etc/nginx/ssl/example.com/fullchain.pem
# 應輸出:PEM certificate
故障五:證書過期導致網站不可訪問
# 1. 臨時解決:使用自簽名證書(瀏覽器會顯示不安全,但可訪問後台)
openssl req -x509 -nodes -days 7 -newkey rsa:2048 \
-keyout /tmp/self-signed.key \
-out /tmp/self-signed.crt
# 2. 修改 Nginx 使用臨時證書
sed -i 's|ssl_certificate.*|ssl_certificate /tmp/self-signed.crt;|' /etc/nginx/sites-enabled/default
sed -i 's|ssl_certificate_key.*|ssl_certificate_key /tmp/self-signed.key;|' /etc/nginx/sites-enabled/default
# 3. 緊急重新申請
systemctl reload nginx
~/.acme.sh/acme.sh --renew -d example.com --force
# 4. 恢復正常證書後恢復配置
sed -i 's|ssl_certificate /tmp/self-signed.crt;|ssl_certificate /etc/nginx/ssl/example.com/fullchain.pem;|' /etc/nginx/sites-enabled/default
sed -i 's|ssl_certificate_key /tmp/self-signed.key;|ssl_certificate_key /etc/nginx/ssl/example.com/privkey.pem;|' /etc/nginx/sites-enabled/default
systemctl reload nginx
驗證步驟
完成任意模組操作後,按以下順序驗證:
- Nginx 配置語法:
nginx -t - HTTPS 響應:
curl -I https://example.com - 證書信息:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -subject - SSL 評級: 訪問 https://www.ssllabs.com/ssltest/ 確認 A 以上評級
- 續期測試:
~/.acme.sh/acme.sh --renew -d example.com --force && systemctl reload nginx
注意事項
- 通配符證書只保護一級子域名(*.example.com 包含 api.example.com,但不包含 sub.api.example.com)
- Let's Encrypt 證書有效期 90 天,acme.sh 默認在到期前 30 天自動續期
- Nginx SSL 配置禁用 TLSv1.0 和 TLSv1.1(已廢棄,存在安全風險)
- HSTS 頭開啟後無法回退 HTTP,建議先關閉一段時間觀察再開啟
- Cloudflare 等 CDN 會緩存 SSL,在 CDN 側也需要配置證書
如需完整伺服器部署方案、澳門政企 IT 搭建諮詢,可進一步溝通。
📊 使用統計
本技能會自動記錄使用情況到本地文件 ~/.qclaw/skills/nginx-ssl-cert-toolkit/usage.json。
記錄內容
{
"totalSessions": 0,
"totalCertGen": 0,
"totalRenewals": 0,
"totalTroubleshoot": 0,
"lastUsed": null,
"sessions": []
}
安全聲明
- ✅ 只記記錄維度(證書生成次數、續期次數、故障排查次數等)
- ❌ 不記錄域名、私鑰路徑、API Token 等敏感內容
- ✅ 數據完全本地存儲,不上傳、不外傳
- ✅ 說「清理統計」可一鍵清空記錄
📋 更新日誌
v1.1.0(2026-07-20)
新增功能:
- ✅ 使用統計模塊(自動記錄啟動次數、證書生成次數、續期次數等)
- ✅ 統計數據本地存儲,不上傳,不記錄敏感信息
- ✅ 支持「清理統計」一鍵清空
安全性:
- 只記維度(次數、日期),不記錄證書詳情
- 數據完全本地化,符合隱私保護原則
v1.0.0(初始版本)
- 初始功能發布
- Nginx HTTPS 配置生成
- Let's Encrypt 通配符證書申請
- 自動續期腳本
- 故障排查指南