目标:为新域名
xxx.yyy.com部署 HTTPS,反向代理到localhost:PORT,支持自动续期,零干扰已有服务。
✅ 前提条件
-
服务器:Ubuntu 20.04/22.04(或其他主流 Linux)
-
域名:
xxx.yyy.com已解析到服务器公网 IP(可通过dig +short xxx.yyy.com验证) -
应用:可运行在
localhost:PORT(如9000),监听0.0.0.0 -
已安装:
sudo apt update sudo apt install -y nginx certbot npm install -g pm2 # 或已全局安装
🔒 本流程绝不修改已有 Nginx 配置或证书,仅操作新域名。
🔧 第一阶段:启动应用(PM2 托管)
# 进入项目目录
cd /path/to/your/app
# 示例 1:启动静态站点(如 Storybook)
pm2 start npx --name "xxx-yyy-app" -- serve -s dist -l 9000
# 示例 2:启动 Node.js 服务
pm2 start app.js --name "xxx-yyy-app" -- --port 9000
# 验证本地访问
curl -s http://localhost:9000 | head -n 1
# ✅ 应返回有效内容(非空)
🔐 第二阶段:申请 Let’s Encrypt 证书(安全模式)
步骤 1:创建临时 HTTP 验证站点
DOMAIN="xxx.yyy.com" # ← 替换为你的域名
sudo tee /etc/nginx/sites-available/$DOMAIN <<EOF
server {
listen 80;
server_name $DOMAIN;
location /.well-known/acme-challenge/ {
root /var/www/html;
try_files \$uri =404;
}
location / {
return 403;
}
}
EOF
步骤 2:启用站点并重载 Nginx
sudo ln -sf /etc/nginx/sites-available/$DOMAIN /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
步骤 3:申请证书(不修改 Nginx)
sudo certbot certonly --webroot -w /var/www/html -d $DOMAIN
✅ 成功后证书路径:
/etc/letsencrypt/live/xxx.yyy.com/{fullchain.pem, privkey.pem}
🌐 第三阶段:配置正式 HTTPS(含自动续期 + 无依赖错误)
⚠️ 关键修复:避免
options-ssl-nginx.conf不存在导致 Nginx 启动失败!
步骤 4:创建正式 Nginx 配置(安全、无外部依赖)
DOMAIN="xxx.yyy.com"
PORT="9000" # ← 替换为你的应用端口
sudo tee /etc/nginx/sites-available/${DOMAIN}-https <<EOF
# HTTP → HTTPS 重定向
server {
listen 80;
server_name $DOMAIN;
return 301 https://\$host\$request_uri;
}
# HTTPS 主服务
server {
listen 443 ssl http2;
server_name $DOMAIN;
# 🔑 Let's Encrypt 验证路径(用于 certbot renew)
location ^~ /.well-known/acme-challenge/ {
root /var/www/html;
default_type "text/plain";
try_files \$uri =404;
}
# SSL 证书
ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;
# ✅ 安全 SSL 配置(内联,无需 options-ssl-nginx.conf)
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_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=63072000" always;
# 反向代理到本地应用
location / {
proxy_pass http://localhost:$PORT;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
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;
proxy_cache_bypass \$http_upgrade;
}
}
EOF
✅ 此配置:
- 不依赖
/etc/letsencrypt/options-ssl-nginx.conf- 支持自动续期(通过 HTTPS 路径验证)
- 安全、现代、兼容性好
步骤 5:切换到正式配置
# 禁用临时站点
sudo rm -f /etc/nginx/sites-enabled/$DOMAIN
# 启用正式 HTTPS 配置
sudo ln -sf /etc/nginx/sites-available/${DOMAIN}-https /etc/nginx/sites-enabled/$DOMAIN
# 测试并重载
sudo nginx -t && sudo systemctl reload nginx
✅ 如果看到
nginx: [emerg] open() "/etc/letsencrypt/options-ssl-nginx.conf" failed,说明你漏掉了内联 SSL 配置 —— 请检查上一步。
✅ 第四阶段:验证服务与自动续期
验证 1:网站可访问
curl -I https://xxx.yyy.com
# 应返回 HTTP/2 200
验证 2:ACME 验证路径可访问(关键!)
echo "ok" | sudo tee /var/www/html/.well-known/acme-challenge/test-ok
curl http://xxx.yyy.com/.well-known/acme-challenge/test-ok # 应返回 ok
curl https://xxx.yyy.com/.well-known/acme-challenge/test-ok # 应返回 ok
sudo rm /var/www/html/.well-known/acme-challenge/test-ok
验证 3:模拟证书续期
sudo certbot renew --dry-run
# ✅ 必须成功!
🔄 第五阶段:设置开机自启(可选)
pm2 startup
# 按提示执行输出的命令(通常是 sudo env PATH=... pm2 startup ...)
pm2 save # 保存当前 PM2 进程
📁 最终结构(干净隔离)
/etc/nginx/sites-available/
├── xxx.yyy.com ← 临时(已删除)
└── xxx.yyy.com-https ← 正式配置(独立、安全)
/etc/nginx/sites-enabled/
└── xxx.yyy.com → 指向 -https 文件
/var/www/html/.well-known/acme-challenge/ ← 所有域名共用,Certbot 自动写入
✅ 完全不影响其他域名(如
staging-ui-components.walks.org等)
🛡️ 常见问题与解决方案
| 问题 | 原因 | 解决 |
|---|---|---|
options-ssl-nginx.conf not found | 使用了include但文件不存在 | 改用内联 SSL 配置(本手册已包含) |
404on/.well-known/acme-challenge/ | HTTPS server 未暴露该路径 | 必须在 HTTPS server 中添加**location ^~ /.well-known/...** |
502 Bad Gateway | 应用未运行或端口错误 | 检查curl http://localhost:PORT |
certbot renew失败 | 验证路径不可公网访问 | 确保 80/443 端口开放,Nginx 配置正确 |
📝 使用模板(部署新项目时复制)
只需替换三处:
DOMAIN="your-domain.com" # ← 替换
PORT="9000" # ← 替换
# 启动命令根据项目调整
pm2 start npx --name "your-app" -- serve -s dist -l $PORT
然后按手册执行即可。
✅ 总结
本手册确保:
- ✅ 零干扰已有服务
- ✅ HTTPS 正常访问
- ✅ 证书自动续期成功
- ✅ 避免
options-ssl-nginx.conf不存在的坑 - ✅ 配置清晰、可审计、可版本控制
📌 建议:将本手册保存为
docs/deploy-https-domain.md,作为团队标准流程。
如需我为你生成 具体域名 + 端口的一键部署脚本,请提供:
- 域名(如
api.fulingweilai.com) - 端口(如
3000) - 启动命令(如
npm start)
祝你部署顺利,系统稳定运行!🚀
1740

被折叠的 条评论
为什么被折叠?



