Nginx 反向代理 + Let's Encrypt SSL:从零搭建安全 Web 服务完整指南
说明: 本文为配置思路与示例整理,不代表作者已在自己的服务器上逐项验证全部命令。执行涉及公网暴露、账户权限、数据删除或服务重启的操作前,请先备份,并结合官方文档与实际环境核验。
任何 Web 应用上线前都绕不开两件事:反向代理和 HTTPS。本文用一台全新的 Ubuntu VPS,手把手带你完成 Nginx 反向代理配置、Let's Encrypt 免费 SSL 证书申请、自动续期,以及常见的坑和排查方法。
为什么需要反向代理?
直接把 Node.js / Python / Go 应用暴露在 80/443 端口上有很多问题:
- 权限问题:非 root 用户无法绑定 1024 以下端口
- 无法同时服务多个站点:一个端口只能绑一个应用
- 没有 HTTPS:现代浏览器对 HTTP 网站会标记"不安全"
- 缺少缓存和压缩:每次请求都打到后端,性能差
Nginx 作为反向代理,站在客户端和后端应用之间,解决以上所有问题。
环境准备
本文基于以下环境(其他 Linux 发行版步骤类似):
# 操作系统
Ubuntu 22.04 LTS
# 需要准备
- 一个已解析到服务器 IP 的域名(本文用 example.com)
- 服务器已开放 80、443 端口
- 一个运行在 localhost:3000 的 Web 应用第一步:安装 Nginx
# 更新包索引
sudo apt update
# 安装 Nginx
sudo apt install -y nginx
# 启动并设置开机自启
sudo systemctl start nginx
sudo systemctl enable nginx
# 验证安装
curl -I http://localhost如果看到 200 OK,说明 Nginx 已经正常运行。
第二步:配置反向代理
创建一个 Nginx 配置文件。建议为每个站点单独一个文件,方便管理:
sudo nano /etc/nginx/sites-available/example.com写入以下内容:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# 日志路径
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# 透传客户端真实 IP
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# 静态资源缓存(如果有)
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2|svg)$ {
proxy_pass http://127.0.0.1:3000;
expires 7d;
add_header Cache-Control "public, immutable";
}
}启用这个配置并重载 Nginx:
# 创建软链接启用站点
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
# 删除默认配置(可选)
sudo rm /etc/nginx/sites-enabled/default
# 测试配置语法
sudo nginx -t
# 重载 Nginx
sudo systemctl reload nginx常见错误:如果
nginx -t报错could not open或already exists,检查/etc/nginx/sites-enabled/下是否有重复的软链接。
第三步:申请 Let's Encrypt SSL 证书
Let's Encrypt 提供免费的 SSL 证书,通过 Certbot 工具自动完成申请和配置。
# 安装 Certbot 和 Nginx 插件
sudo apt install -y certbot python3-certbot-nginx
# 自动申请证书并配置 Nginx
sudo certbot --nginx -d example.com -d www.example.comCertbot 会自动:
- 验证你对域名的控制权(通过 HTTP-01 挑战)
- 下载 SSL 证书
- 修改你的 Nginx 配置,添加 SSL 相关指令
- 设置 HTTP → HTTPS 自动跳转
完成后你的 Nginx 配置会变成类似这样:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# Certbot 自动添加的跳转
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com www.example.com;
# Certbot 自动添加的 SSL 配置
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# 你原来的反向代理配置...
location / {
proxy_pass http://127.0.0.1:3000;
# ...
}
}访问 https://example.com,浏览器应显示连接安全/HTTPS 正常。
第四步:配置证书自动续期
Let's Encrypt 的证书有效期是 90 天,过期后浏览器会报警告。Certbot 安装时会自动创建 systemd timer,但我们需要确认它正常工作:
# 查看自动续期定时器状态
sudo systemctl status certbot.timer
# 手动测试续期是否正常(不会真正续期,只是模拟)
sudo certbot renew --dry-run如果 dry-run 没有报错,说明自动续期已经配置好了。
如果你想更主动地控制,可以加一个 cron 任务作为双保险:
# 编辑 root 用户的 crontab
sudo crontab -e
# 添加以下行(每天凌晨 3 点检查续期)
0 3 * * * certbot renew --quiet --deploy-hook "systemctl reload nginx"--deploy-hook 参数会在证书成功续期后自动重载 Nginx,确保新证书立即生效。
第五步:安全加固(可选但推荐)
基本的反向代理和 SSL 就够用了,但如果你想更安全,可以加上这些配置:
server {
# ... 之前的配置 ...
# 安全响应头
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 隐藏 Nginx 版本号
server_tokens off;
# 限制请求体大小(防止大文件上传攻击)
client_max_body_size 10m;
# 禁止访问隐藏文件(如 .git、.env)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}常用安全头说明
| Header | 作用 |
|---|---|
Strict-Transport-Security |
强制浏览器使用 HTTPS(HSTS) |
X-Content-Type-Options |
防止浏览器 MIME 类型嗅探 |
X-Frame-Options |
防止页面被嵌入 iframe(防点击劫持) |
X-XSS-Protection |
启用浏览器内置 XSS 过滤 |
常见问题排查
问题 1:502 Bad Gateway
原因:Nginx 无法连接到后端服务。
# 检查后端是否在运行
curl http://127.0.0.1:3000
# 检查 Nginx 错误日志
sudo tail -f /var/log/nginx/example.com.error.log
# 检查端口是否被占用
sudo ss -tlnp | grep 3000常见原因:
- 后端应用没有启动
- 后端监听的端口与 Nginx 配置不一致
- 防火墙阻止了本地连接
问题 2:SSL 证书申请失败
# Certbot 报错 "Challenge failed"
# 最常见原因是 80 端口不通
# 检查防火墙
sudo ufw status
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# 检查是否有其他服务占用 80 端口
sudo ss -tlnp | grep :80问题 3:WebSocket 连接断开
如果用了 WebSocket 但连接频繁断开,检查这些配置:
location /ws {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# WebSocket 需要更长的超时时间
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}问题 4:混合内容警告(Mixed Content)
页面加载了 HTTP 资源但页面本身是 HTTPS。检查你的应用配置,确保:
# 在应用代码中确保生成的 URL 使用 HTTPS
# 例如 Node.js + Express
app.set('trust proxy', 1); # 信任第一层代理
app.use((req, res, next) => {
if (req.headers['x-forwarded-proto'] === 'https') {
res.locals.baseUrl = 'https://example.com';
}
next();
});完整的较完整的配置模板
把上面的内容综合起来,一个完整的较完整的 Nginx 配置长这样:
# /etc/nginx/sites-available/example.com
# HTTP → HTTPS 跳转
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
# HTTPS 主配置
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# SSL 证书(Certbot 自动管理)
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# 安全头
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
server_tokens off;
# 日志
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
# 反向代理
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# 禁止访问隐藏文件
location ~ /\. {
deny all;
}
}总结
搭建一个安全的 Web 服务并不复杂,核心就三步:
- Nginx 反向代理:让 Nginx 处理客户端请求,转发到后端应用
- Let's Encrypt SSL:一行命令申请免费证书
- 自动续期:Certbot timer 保证证书不过期
Let's Encrypt 的 DV 证书在传输加密强度上可以满足大多数网站 HTTPS 需求,但它只验证域名控制权,不验证企业主体身份;如果业务需要组织身份背书或合规审计,应评估 OV/EV 或商业 CA。如果遇到问题,先看 Nginx 错误日志,大部分问题都能在那里找到答案。
本文所有配置示例均可直接复制使用,只需将 example.com 替换成你自己的域名。
评论
游客无需注册即可评论。
你提交的昵称、邮箱、网址和评论内容会保存在服务端,用于展示评论身份、接收回复及必要的安全审计。
浏览器会本地保存已填游客信息和评论草稿,方便下次免填。
回复提醒会通过站内消息和邮件通知。