Docker 部署 HTTPS 最常见的坑:证书路径写错、配置不生效、续签失败。这篇教程覆盖两种场景——开发环境用自签名证书快速验证,生产环境用 Let’s Encrypt 免费证书 + 自动续签,让你少走弯路。
准备工作
确保服务器已安装:
- Docker(推荐 20.10 及以上版本)
- Docker Compose(推荐 V2)
- 如使用 Let’s Encrypt,需要一个已解析到服务器 IP 的域名,且 80 端口可被公网访问
方案一:自签名证书(开发 / 内网环境)
自签名证书不受浏览器信任,适合开发调试或内网服务,3 分钟搞定。
第一步:生成自签名证书
mkdir -p /opt/nginx/ssl
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /opt/nginx/ssl/nginx.key \
-out /opt/nginx/ssl/nginx.crt \
-subj "/C=CN/ST=Beijing/L=Beijing/O=Dev/CN=localhost"
生成两个文件:nginx.key(私钥)和 nginx.crt(证书),有效期 365 天。
第二步:写 Nginx 配置
创建 /opt/nginx/conf.d/ssl.conf:
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
注意:
ssl_certificate填的是容器内路径,不是宿主机路径!这是最常见的配置错误。
第三步:启动容器
docker run -d --name my-nginx \
-p 80:80 -p 443:443 \
-v /opt/nginx/conf.d:/etc/nginx/conf.d \
-v /opt/nginx/ssl:/etc/nginx/ssl \
nginx
访问 https://localhost 验证,浏览器会提示”不安全”(自签名正常现象),点击继续即可。
热重载配置(无需重启容器)
docker exec my-nginx nginx -s reload
方案二:Let’s Encrypt 免费证书 + 自动续签(生产环境)

Let’s Encrypt 证书当前有效期 90 天(2027 年起将逐步缩短至 45 天),Certbot 按约 2/3 有效期自动续签。整套方案用两个容器搞定:Nginx 负责反向代理,Certbot 负责证书申请和续签。
目录结构
/opt/myapp/
├── docker-compose.yml
├── nginx/
│ ├── conf.d/
│ │ └── app.conf
│ └── html/
├── certbot/
│ ├── conf/ # 证书存储目录
│ └── www/ # HTTP 验证临时目录
第一步:创建 docker-compose.yml
version: '3'
services:
nginx:
image: nginx:latest
container_name: my-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/html:/usr/share/nginx/html
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
networks:
- web
certbot:
image: certbot/certbot
container_name: certbot
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
networks:
- web
networks:
web:
driver: bridge
两个关键设计:
- Nginx 每 6 小时自动 reload,确保续签后的新证书立即生效
- Certbot 每 12 小时检查一次,按证书剩余有效期的约 1/3 时间点触发续签
2026 年重要变更:Let’s Encrypt 正在推进证书有效期从 90 天缩短至 45 天——2026 年 5 月 13 日起可选加入,2027 年 2 月 10 日起全面切换为 64 天过渡期,2028 年 2 月最终到 45 天。使用本文 docker-compose 方案(Certbot 自动循环检查)无需修改,但建议确保 Certbot 镜像保持最新版本以获得 ARI 支持。
第二步:首次申请证书前,先配置 HTTP 验证
创建 nginx/conf.d/app.conf(暂时只配 HTTP,不配 HTTPS):
server {
listen 80;
server_name your-domain.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
把 your-domain.com 替换成你的域名。
第三步:启动服务
cd /opt/myapp
docker-compose up -d
第四步:申请证书
docker-compose run --rm certbot certonly --webroot \
-w /var/www/certbot \
-d your-domain.com \
--email your@email.com \
--agree-tos \
--no-eff-email
看到 “Congratulations!” 提示表示申请成功,证书存放在 ./certbot/conf/live/your-domain.com/。
第五步:更新 Nginx 配置,启用 HTTPS
修改 nginx/conf.d/app.conf:
server {
listen 80;
server_name your-domain.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
# SSL 性能优化
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers on;
# 强制 HTTPS(HSTS)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
第六步:重载 Nginx
docker-compose exec nginx nginx -s reload
访问 https://your-domain.com 验证,地址栏出现绿锁即配置成功。
验证自动续签是否正常
# 模拟续签(不实际执行,只验证流程)
docker-compose run --rm certbot renew --dry-run
# 查看续签日志
docker-compose logs certbot
SSL 安全加固(可选)

配置 OCSP Stapling 和安全响应头,可以把 SSL Labs 评分提到 A+:
# 在 443 server 块内追加以下内容
# OCSP Stapling(加快证书验证速度)
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/your-domain.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# 安全响应头
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
配置完成后,访问 SSL Labs 测试工具 测一下分数。
反向代理配置(附加场景)
如果 Nginx 需要把流量转发给其他 Docker 容器(比如 Node.js、Go 应用),在 server 块内把 location / 替换成:
upstream backend {
server backend-api:3000; # backend-api 是目标容器名
keepalive 32;
}
location / {
proxy_pass http://backend;
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 $scheme;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
容器间通信用容器名(如
backend-api:3000),不是localhost,这是 Docker 网络的基本规则。
常见问题排查
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 证书路径 404 | 写的是宿主机路径而非容器内路径 | 配置文件里用容器内路径(/etc/letsencrypt/...) |
| 申请证书失败 | 域名未解析到当前服务器,或 80 端口被占用 | 检查 DNS 解析 + docker ps 确认 80 端口 |
| 续签后证书未生效 | Nginx 未重载 | Nginx 容器命令已内置每 6 小时 reload,或手动执行 nginx -s reload |
| 502 Bad Gateway | 后端容器未启动,或未加入同一网络 | docker ps 检查状态,确认两个容器在同一 network |
| 浏览器仍显示不安全 | 使用了自签名证书,或证书域名与访问域名不匹配 | 生产环境使用 Let’s Encrypt,或检查 server_name |
常用命令速查
# 检查 Nginx 配置语法
docker exec my-nginx nginx -t
# 优雅重载(不中断现有连接)
docker exec my-nginx nginx -s reload
# 查看 Certbot 日志
docker-compose logs certbot
# 测试证书续签流程
docker-compose run --rm certbot renew --dry-run
参考资料
- Docker 部署 Nginx HTTPS 完整指南 · EastonDev —— EastonDev,2025-12-18
- Docker+Certbot+Nginx 实现自动续期 HTTPS 证书 · 掘金 —— 掘金,2025-04-01
- Docker Nginx HTTPS 实战:Let’s Encrypt 证书生成与自动续签 · 博客园 —— 博客园,2026-02-15




