HTTPS 证书免费申请与配置(Certbot + Nginx)全记录

78 阅读7分钟

1. 前言与准备工作

在当今互联网环境中,为网站启用HTTPS加密连接已成为基本安全要求。本文将详细介绍如何使用Let's Encrypt的Certbot工具为Nginx服务器免费申请和配置SSL/TLS证书。

1.1 系统要求

  • Linux服务器(本文以Ubuntu 20.04 LTS为例)
  • 已安装Nginx Web服务器
  • 拥有一个已解析到服务器IP的域名
  • 服务器开放80和443端口

1.2 准备工作检查

首先验证系统环境和必要服务状态:

# 检查系统版本
lsb_release -a

# 检查Nginx状态
sudo systemctl status nginx

# 检查端口监听状态
sudo netstat -tulpn | grep -E ':(80|443)'

# 更新系统包
sudo apt update && sudo apt upgrade -y

2. Certbot安装与配置

2.1 安装Certbot和Nginx插件

# 添加Certbot官方仓库
sudo apt install software-properties-common -y
sudo add-apt-repository ppa:certbot/certbot -y

# 更新包列表并安装Certbot
sudo apt update
sudo apt install certbot python3-certbot-nginx -y

# 验证安装
certbot --version

2.2 配置防火墙规则

确保防火墙允许HTTP和HTTPS流量:

# 如果使用ufw
sudo ufw allow 'Nginx Full'
sudo ufw delete allow 'Nginx HTTP'

# 如果使用iptables
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT

3. Nginx基础配置

3.1 创建Nginx配置文件

创建网站配置文件:/etc/nginx/sites-available/yourdomain.conf

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    
    # 基础安全头
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    
    # 根目录配置
    root /var/www/html;
    index index.html index.htm;
    
    # 日志配置
    access_log /var/log/nginx/yourdomain.access.log;
    error_log /var/log/nginx/yourdomain.error.log;
    
    location / {
        try_files $uri $uri/ =404;
    }
    
    # 静态资源缓存
    location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

启用网站配置:

# 创建符号链接
sudo ln -s /etc/nginx/sites-available/yourdomain.conf /etc/nginx/sites-enabled/

# 测试Nginx配置
sudo nginx -t

# 重新加载Nginx
sudo systemctl reload nginx

4. SSL证书申请流程

以下是证书申请和配置的完整流程图:

graph TD
    A[开始证书申请] --> B[安装Certbot工具]
    B --> C[配置Nginx虚拟主机]
    C --> D[申请SSL证书]
    D --> E{申请是否成功?}
    E -->|是| F[配置SSL到Nginx]
    E -->|否| G[排查错误]
    G --> D
    F --> H[测试HTTPS配置]
    H --> I{测试是否通过?}
    I -->|是| J[设置自动续期]
    I -->|否| K[调试配置]
    K --> H
    J --> L[完成HTTPS部署]
    
    style A fill:#2d5c8a,color:#ffffff
    style B fill:#2d5c8a,color:#ffffff
    style C fill:#2d5c8a,color:#ffffff
    style D fill:#2d5c8a,color:#ffffff
    style F fill:#2d5c8a,color:#ffffff
    style H fill:#2d5c8a,color:#ffffff
    style J fill:#2d5c8a,color:#ffffff
    style L fill:#2d5c8a,color:#ffffff
    style E fill:#b34045,color:#ffffff
    style I fill:#b34045,color:#ffffff
    style G fill:#b34045,color:#ffffff
    style K fill:#b34045,color:#ffffff

4.1 申请SSL证书

使用Certbot申请证书:

# 使用Nginx插件自动配置(推荐新手)
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

# 或者使用独立模式(如果Nginx插件有问题)
sudo certbot certonly --standalone -d yourdomain.com -d www.yourdomain.com

申请过程中会提示:

  1. 输入邮箱地址(用于安全通知和证书恢复)
  2. 同意服务条款
  3. 选择是否接收邮件通知
  4. 选择是否将HTTP流量重定向到HTTPS(推荐选择2)

4.2 手动模式申请证书

如果自动模式遇到问题,可以使用手动模式:

# 手动模式申请
sudo certbot certonly --manual --preferred-challenges dns \
  -d yourdomain.com -d www.yourdomain.com \
  --manual-public-ip-logging-ok

手动模式需要根据提示在DNS中添加TXT记录验证域名所有权。

5. Nginx SSL配置

5.1 完整的SSL配置示例

创建或更新Nginx SSL配置文件:/etc/nginx/sites-available/yourdomain-ssl.conf

# HTTP到HTTPS重定向
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

# HTTPS服务器配置
server {
    listen 443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;
    
    # SSL证书路径
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    
    # SSL安全配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers off;
    
    # SSL会话设置
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;
    
    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 1.1.1.1 valid=300s;
    resolver_timeout 5s;
    
    # 安全头
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
    
    # 根目录配置
    root /var/www/html;
    index index.html index.htm;
    
    # 日志配置
    access_log /var/log/nginx/yourdomain.ssl.access.log;
    error_log /var/log/nginx/yourdomain.ssl.error.log;
    
    location / {
        try_files $uri $uri/ =404;
    }
    
    # 安全配置 - 隐藏敏感文件
    location ~ /\.(?!well-known) {
        deny all;
        access_log off;
        log_not_found off;
    }
    
    location ~ /\.ht {
        deny all;
        access_log off;
        log_not_found off;
    }
    
    # 静态资源缓存
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf|txt)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header Vary "Accept-Encoding";
    }
    
    # 启用Gzip压缩
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        application/atom+xml
        application/geo+json
        application/javascript
        application/x-javascript
        application/json
        application/ld+json
        application/manifest+json
        application/rdf+xml
        application/rss+xml
        application/xhtml+xml
        application/xml
        font/eot
        font/otf
        font/ttf
        image/svg+xml
        text/css
        text/javascript
        text/plain
        text/xml;
}

5.2 应用配置并测试

# 测试Nginx配置
sudo nginx -t

# 重新加载Nginx配置
sudo systemctl reload nginx

# 检查SSL证书状态
sudo certbot certificates

6. SSL配置测试与验证

6.1 在线SSL测试

使用以下命令测试SSL配置:

# 使用openssl测试
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com < /dev/null

# 检查证书链
openssl s_client -connect yourdomain.com:443 -showcerts < /dev/null

# 使用在线工具(需要curl)
curl -I https://yourdomain.com

6.2 创建测试脚本

创建SSL测试脚本:/usr/local/bin/ssl-test.sh

#!/bin/bash

DOMAIN="yourdomain.com"

echo "=== SSL证书测试报告 ==="
echo "测试时间: $(date)"
echo "测试域名: $DOMAIN"
echo "========================="
echo ""

# 检查证书过期时间
echo "1. 证书有效期检查:"
openssl s_client -connect $DOMAIN:443 -servername $DOMAIN 2>/dev/null | openssl x509 -noout -dates

echo ""
echo "2. 证书链完整性检查:"
openssl s_client -connect $DOMAIN:443 -servername $DOMAIN -verify_return_error 2>/dev/null
if [ $? -eq 0 ]; then
    echo "✓ 证书链完整"
else
    echo "✗ 证书链不完整"
fi

echo ""
echo "3. 支持的协议检查:"
for protocol in ssl2 ssl3 tls1 tls1_1 tls1_2 tls1_3; do
    result=$(openssl s_client -connect $DOMAIN:443 -$protocol < /dev/null 2>&1 | grep "CONNECTED")
    if [ -n "$result" ]; then
        echo "✓ 支持 $protocol"
    else
        echo "✗ 不支持 $protocol"
    fi
done

echo ""
echo "4. HTTP到HTTPS重定向检查:"
http_code=$(curl -s -o /dev/null -w "%{http_code}" http://$DOMAIN)
if [ "$http_code" = "301" ] || [ "$http_code" = "302" ]; then
    echo "✓ HTTP重定向正常"
else
    echo "✗ HTTP重定向异常,返回码: $http_code"
fi

echo ""
echo "5. HSTS头检查:"
hsts_header=$(curl -s -I https://$DOMAIN | grep -i "strict-transport-security")
if [ -n "$hsts_header" ]; then
    echo "✓ HSTS头已设置: $hsts_header"
else
    echo "✗ HSTS头未设置"
fi

echo ""
echo "=== 测试完成 ==="

给脚本执行权限:

sudo chmod +x /usr/local/bin/ssl-test.sh
./usr/local/bin/ssl-test.sh

7. 证书自动续期配置

7.1 设置自动续期任务

# 测试证书续期(干跑模式)
sudo certbot renew --dry-run

# 设置自动续期的cron任务
sudo crontab -e

在cron文件中添加以下内容:

# Certbot SSL证书自动续期
0 12 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"

# 每周一检查证书状态并发送通知
0 9 * * 1 /usr/bin/certbot certificates | mail -s "SSL证书状态报告" admin@yourdomain.com

7.2 创建续期监控脚本

创建证书监控脚本:/usr/local/bin/cert-monitor.sh

#!/bin/bash

# 配置参数
DOMAIN="yourdomain.com"
ADMIN_EMAIL="admin@yourdomain.com"
LOG_FILE="/var/log/ssl-renewal.log"
WARNING_DAYS=30

# 获取证书过期时间
expiry_date=$(openssl s_client -connect $DOMAIN:443 -servername $DOMAIN 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
expiry_timestamp=$(date -d "$expiry_date" +%s)
current_timestamp=$(date +%s)
days_until_expiry=$(( (expiry_timestamp - current_timestamp) / 86400 ))

# 日志函数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> $LOG_FILE
}

# 检查证书状态
check_certificate() {
    log "开始检查证书: $DOMAIN"
    log "证书过期时间: $expiry_date"
    log "距离过期还有: $days_until_expiry 天"
    
    if [ $days_until_expiry -le $WARNING_DAYS ]; then
        log "警告: 证书将在 $days_until_expiry 天后过期"
        
        # 发送邮件通知
        mail -s "证书过期警告: $DOMAIN" $ADMIN_EMAIL << EOF
域名: $DOMAIN
过期时间: $expiry_date
剩余天数: $days_until_expiry

请及时处理证书续期。
EOF
        
        # 尝试自动续期
        /usr/bin/certbot renew --force-renewal --quiet --post-hook "systemctl reload nginx"
        if [ $? -eq 0 ]; then
            log "证书续期成功"
            mail -s "证书续期成功: $DOMAIN" $ADMIN_EMAIL << EOF
域名: $DOMAIN
续期时间: $(date)
状态: 成功

证书已自动续期。
EOF
        else
            log "证书续期失败"
            mail -s "证书续期失败: $DOMAIN" $ADMIN_EMAIL << EOF
域名: $DOMAIN
续期时间: $(date)
状态: 失败

请手动检查证书续期。
EOF
        fi
    else
        log "证书状态正常"
    fi
}

# 执行检查
check_certificate

设置脚本权限和执行计划:

sudo chmod +x /usr/local/bin/cert-monitor.sh

# 添加到cron,每天检查一次
echo "0 8 * * * /usr/local/bin/cert-monitor.sh" | sudo crontab -

8. 高级配置与优化

8.1 创建Nginx SSL配置片段

创建可重用的SSL配置片段:/etc/nginx/snippets/ssl-params.conf

# 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:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

# SSL会话设置
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

# DH参数(如果需要生成)
# ssl_dhparam /etc/nginx/dhparam.pem;

# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 1.1.1.1 valid=300s;
resolver_timeout 5s;

# 安全头
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

8.2 生成强DH参数

# 生成DH参数(需要较长时间)
sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048

# 然后在ssl-params.conf中取消注释并指向该文件

8.3 多域名配置

对于多个域名的配置,创建通用模板:/etc/nginx/sites-available/ssl-template.conf

# HTTP重定向模板
server {
    listen 80;
    server_name DOMAIN_PLACEHOLDER;
    return 301 https://$server_name$request_uri;
}

# HTTPS服务器模板
server {
    listen 443 ssl http2;
    server_name DOMAIN_PLACEHOLDER;
    
    # SSL证书
    ssl_certificate /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/privkey.pem;
    
    # 包含SSL参数
    include snippets/ssl-params.conf;
    
    # 网站特定配置
    root /var/www/DOMAIN_PLACEHOLDER/html;
    index index.html index.htm;
    
    # 日志
    access_log /var/log/nginx/DOMAIN_PLACEHOLDER.access.log;
    error_log /var/log/nginx/DOMAIN_PLACEHOLDER.error.log;
    
    location / {
        try_files $uri $uri/ =404;
    }
}

9. 故障排除与常见问题

9.1 常见问题解决

创建故障排除脚本:/usr/local/bin/ssl-troubleshoot.sh

#!/bin/bash

echo "=== SSL故障排除工具 ==="
echo ""

# 检查服务状态
echo "1. 服务状态检查:"
sudo systemctl status nginx --no-pager -l
echo ""

# 检查证书状态
echo "2. 证书状态检查:"
sudo certbot certificates
echo ""

# 检查Nginx配置
echo "3. Nginx配置检查:"
sudo nginx -t
echo ""

# 检查端口监听
echo "4. 端口监听检查:"
sudo netstat -tulpn | grep -E ':(80|443)'
echo ""

# 检查防火墙状态
echo "5. 防火墙状态检查:"
sudo ufw status verbose
echo ""

# 检查DNS解析
echo "6. DNS解析检查:"
nslookup yourdomain.com
echo ""

echo "=== 检查完成 ==="

9.2 证书更新问题

如果证书更新失败,尝试以下命令:

# 强制更新证书
sudo certbot renew --force-renewal

# 调试模式查看详细错误
sudo certbot renew --debug

# 手动更新特定证书
sudo certbot certonly --force-renewal -d yourdomain.com -d www.yourdomain.com

10. 安全加固与最佳实践

10.1 定期安全扫描

# 安装SSL扫描工具
sudo apt install sslscan -y

# 扫描SSL配置
sslscan yourdomain.com

# 使用nmap进行安全扫描
sudo apt install nmap -y
nmap --script ssl-enum-ciphers -p 443 yourdomain.com

10.2 监控和告警

创建监控脚本:/usr/local/bin/ssl-monitor.sh

#!/bin/bash

DOMAINS=("yourdomain.com" "www.yourdomain.com")

for domain in "${DOMAINS[@]}"; do
    expiry_date=$(echo | openssl s_client -servername $domain -connect $domain:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
    expiry_timestamp=$(date -d "$expiry_date" +%s)
    current_timestamp=$(date +%s)
    days_until_expiry=$(( (expiry_timestamp - current_timestamp) / 86400 ))
    
    if [ $days_until_expiry -lt 15 ]; then
        # 发送紧急告警
        echo "紧急: $domain 证书将在 $days_until_expiry 天后过期" | mail -s "SSL证书紧急告警" admin@yourdomain.com
    elif [ $days_until_expiry -lt 30 ]; then
        # 发送警告
        echo "警告: $domain 证书将在 $days_until_expiry 天后过期" | mail -s "SSL证书警告" admin@yourdomain.com
    fi
done

11. 总结

通过本文详细的步骤,您已经成功:

  1. 安装并配置了Certbot工具
  2. 申请了免费的Let's Encrypt SSL证书
  3. 配置Nginx支持HTTPS
  4. 设置了证书自动续期
  5. 实现了SSL安全加固
  6. 建立了监控和告警机制

现在您的网站已经具备了企业级的HTTPS安全保障。记得定期检查证书状态和维护系统更新,确保网站持续安全运行。

11.1 维护建议

  • 每月检查证书状态
  • 定期更新系统和软件包
  • 监控SSL/TLS安全公告
  • 定期审查和更新SSL配置
  • 备份证书和配置文