在香港节点部署Next.js SSR站点:PM2、Nginx反向代理与HTTPS验证流程
面向初学者介绍Next.js SSR站点在Linux香港节点上的生产部署流程,涵盖Node.js与PM2进程守护、Nginx反向代理、HTTPS证书申请续期及上线验证排查。

先把目标和前置条件对齐
一次常见的部署故障是:Next.js 项目在本地 npm run dev 正常,上传到香港节点后却出现 502、页面刷新报错、HTTPS 证书申请失败,或者重启服务器后站点无法自动恢复。问题通常不在 Next.js 本身,而在生产运行方式、Nginx 反向代理和证书验证链路没有串起来。
本文以一台 Linux 香港节点为例,完成一个 Next.js SSR 站点的生产部署:Node.js 负责运行应用,PM2 负责进程守护和开机自启,Nginx 负责监听 80/443 并反向代理到本机端口,HTTPS 使用 Let’s Encrypt 证书完成验证。示例默认系统为 Ubuntu 22.04/24.04,域名已准备好并可添加 DNS 记录;如果你的系统是 Debian、CentOS 或其他发行版,包管理命令需要对应调整。
开始前请确认:
- 你拥有一台可公网访问的香港节点,并具备 SSH 登录权限;
- 域名已解析到该节点公网 IPv4 地址,至少准备
example.com,如需同时访问www.example.com也要添加对应解析; - 安全组或防火墙已放行 22、80、443 端口;
- Next.js 项目可以在本地完成
npm run build; - 项目是 SSR 或混合渲染站点,不是纯静态导出的
next export部署方式。
下文中的 example.com、/var/www/nextapp、3000 请替换为你的真实域名、项目路径和应用端口。
最小可用部署:让 Next.js 在服务器上稳定运行
安装 Node.js、Nginx 与 PM2
不要在生产环境使用 next dev。next dev 是开发服务器,包含热更新和调试能力,不适合作为线上入口。生产部署应先执行 next build,再使用 next start 或 standalone 方式运行。
在 Ubuntu 22.04/24.04 上,可以安装 Node.js LTS 版本。以下以 Node.js 20 为例;如果你对安装源有审计要求,应先查看 NodeSource 官方文档后再执行安装脚本。
sudo apt update
sudo apt install -y curl ca-certificates gnupg nginx
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node -v
npm -v
安装 PM2:
sudo npm install -g pm2
pm2 -v
建议不要直接用 root 长期运行应用。可以创建一个普通部署用户,或者使用已有的非 root 用户。下面示例假设使用当前登录用户部署。
创建项目目录:
sudo mkdir -p /var/www/nextapp
sudo chown -R $USER:$USER /var/www/nextapp
将代码上传到 /var/www/nextapp。可以使用 Git 拉取,也可以通过 SFTP、rsync 或 CI/CD 上传。进入项目目录后安装依赖并构建:
cd /var/www/nextapp
npm ci
npm run build
如果构建失败,先不要继续配置 Nginx。Next.js SSR 站点必须先在本机端口跑通,再交给 Nginx 代理,否则后面排查会混在一起。
用 PM2 启动 Next.js 生产进程
在项目根目录创建 ecosystem.config.cjs:
module.exports = {
apps: [
{
name: "next-ssr",
cwd: "/var/www/nextapp",
script: "node_modules/next/dist/bin/next",
args: "start -H 127.0.0.1 -p 3000",
env: {
NODE_ENV: "production"
}
}
]
};
这里有两个关键点:
-H 127.0.0.1表示 Next.js 只监听本机地址,不直接暴露到公网;-p 3000是内部应用端口,后续由 Nginx 将 80/443 请求代理到该端口。
启动应用:
cd /var/www/nextapp
pm2 start ecosystem.config.cjs
pm2 status
查看应用日志:
pm2 logs next-ssr --lines 50
本机验证:
curl -I http://127.0.0.1:3000
如果返回 HTTP/1.1 200 OK、307、308 或其他符合你应用路由逻辑的状态,说明 Node.js 与 PM2 运行链路基本正常。若这里已经连接失败,优先检查 pm2 logs,不要先改 Nginx。
设置 PM2 开机自启:
pm2 save
pm2 startup systemd -u $USER --hp $HOME
执行后,PM2 会输出一条需要 sudo 执行的命令。复制并执行该命令,再确认服务状态:
systemctl status pm2-$USER
后续发布新版本时,常见流程是:
cd /var/www/nextapp
git pull
npm ci
npm run build
pm2 reload next-ssr --update-env
如果你不是通过 Git 部署,替换为你的代码上传方式即可。上线前建议保留上一版本代码或构建包,避免发布失败时无法回滚。
Nginx反向代理:让域名访问到本机应用端口
PM2 只负责让 Next.js 进程稳定运行,真正接收公网 HTTP/HTTPS 请求的是 Nginx。Nginx反向代理的作用是:外部用户访问 http://example.com 或 https://example.com,Nginx 接收请求后转发到 127.0.0.1:3000,再把 Next.js 返回的页面响应给用户。
创建站点配置:
sudo nano /etc/nginx/sites-available/nextapp
写入以下配置,将域名替换为你的真实域名:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
access_log /var/log/nginx/nextapp.access.log;
error_log /var/log/nginx/nextapp.error.log;
client_max_body_size 20m;
location /_next/static/ {
proxy_pass http://127.0.0.1:3000/_next/static/;
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;
expires 30d;
add_header Cache-Control "public, immutable";
}
location / {
proxy_pass http://127.0.0.1:3000;
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_read_timeout 60s;
proxy_send_timeout 60s;
proxy_cache_bypass $http_upgrade;
}
}
启用站点并检查配置:
sudo ln -s /etc/nginx/sites-available/nextapp /etc/nginx/sites-enabled/nextapp
sudo nginx -t
sudo systemctl reload nginx
如果提示符号链接已存在,不要重复创建,直接检查配置即可。若默认站点占用了相同域名或返回 Nginx 欢迎页,可以查看:
ls -l /etc/nginx/sites-enabled/
确认没有其他配置抢占同一个 server_name。不要随意删除配置文件,建议先备份或只取消不需要站点的软链接。
本机带 Host 头验证 Nginx 转发:
curl -I -H "Host: example.com" http://127.0.0.1
公网验证:
curl -I http://example.com
此时即使还没有 HTTPS,只要 DNS 已生效、80 端口放行、PM2 应用正常,HTTP 访问应该能进入 Next.js 站点。
优化项:减少重启风险和代理误判
明确 SSR 应用与静态站点的差异
Next.js SSR 站点需要 Node.js 进程持续运行,因为请求到达时可能要执行服务端渲染、读取环境变量、访问接口或生成响应。它不同于纯静态 HTML 文件,不能只把 .next 目录丢给 Nginx 当静态目录使用。
判断方式很简单:
- 使用
getServerSideProps、动态服务端组件、API Routes、Route Handlers,通常需要 Node.js 运行时; - 使用
next start运行,说明需要 Next.js 服务进程; - 如果项目配置了
output: "export"并生成纯静态文件,部署方式会不同,不应套用本文的 SSR 流程。
注意环境变量加载位置
Next.js 生产运行时常用 .env.production 或系统环境变量。修改环境变量后,只 reload Nginx 不会让应用读取新变量,需要重新构建或重启 PM2,具体取决于变量是在构建期还是运行期读取。
常用处理方式:
cd /var/www/nextapp
npm run build
pm2 reload next-ssr --update-env
环境变量文件建议限制权限:
chmod 600 /var/www/nextapp/.env.production
不要把数据库密码、API Token 写入 Nginx 配置,也不要提交到公开代码仓库。
控制应用监听范围
很多 502 或安全扫描告警来自一个细节:Next.js 同时监听了公网地址和 Nginx 代理入口。生产环境中,推荐 Next.js 只监听 127.0.0.1,对外只开放 Nginx 的 80/443。
检查端口监听:
ss -lntp | grep 3000
期望看到类似:
LISTEN 0 511 127.0.0.1:3000 0.0.0.0:*
如果看到 0.0.0.0:3000,说明应用对所有网卡开放。虽然安全组可能未放行 3000,但更稳妥的做法仍是从启动参数上限制为 127.0.0.1。
HTTPS证书:申请、重定向与续期验证
HTTPS 证书申请前必须满足两个条件:域名解析到当前香港节点公网 IP,且 80 端口可以从公网访问。Let’s Encrypt 的 HTTP-01 验证会通过 80 端口访问你的域名,如果 DNS 未生效、Nginx 配置错误或安全组拦截,证书申请会失败。
安装 Certbot:
sudo apt install -y certbot python3-certbot-nginx
申请证书并让 Certbot 自动修改 Nginx 配置:
sudo certbot --nginx -d example.com -d www.example.com
执行过程中通常会询问邮箱、服务条款以及是否将 HTTP 重定向到 HTTPS。生产站点一般选择重定向到 HTTPS。完成后,Certbot 会在 Nginx 配置中加入 listen 443 ssl、证书路径和重定向规则。
检查 Nginx 配置并重载:
sudo nginx -t
sudo systemctl reload nginx
验证 HTTPS:
curl -I https://example.com
查看证书信息:
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -subject -issuer -dates
确认自动续期任务:
systemctl list-timers | grep certbot
sudo certbot renew --dry-run
如果 renew --dry-run 成功,说明证书续期链路基本可用。证书通常不需要手动下载、上传或定期替换;只要续期任务正常,Certbot 会在到期前自动续期。
HTTPS 稳定后,可以考虑增加安全响应头。示例:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
HSTS 要谨慎启用,尤其是包含子域名的配置。一旦浏览器记住强制 HTTPS,而你的某些子域名没有证书或不支持 HTTPS,访问会直接失败。建议确认所有相关域名都已完成 HTTPS 后再添加。
上线验证:按链路逐层确认
部署完成后,不要只用浏览器打开首页判断成功。Next.js SSR 站点上线至少要验证进程、端口、代理、证书和业务页面五个层面。
| 验证项 | 命令或方式 | 期望结果 |
|---|---|---|
| PM2 进程 | pm2 status |
next-ssr 状态为 online |
| 应用端口 | curl -I http://127.0.0.1:3000 |
返回符合应用逻辑的 HTTP 状态 |
| Nginx 配置 | sudo nginx -t |
显示 syntax is ok 和 test is successful |
| HTTP 访问 | curl -I http://example.com |
可访问,或 301/308 跳转到 HTTPS |
| HTTPS 访问 | curl -I https://example.com |
返回 200、301、308 等有效状态 |
| 证书链 | openssl s_client |
域名、签发者、有效期正常 |
| 日志观察 | pm2 logs、Nginx access/error log |
无持续 502、499、500 错误 |
Nginx 日志位置可按前面的配置查看:
sudo tail -f /var/log/nginx/nextapp.access.log
sudo tail -f /var/log/nginx/nextapp.error.log
PM2 日志查看:
pm2 logs next-ssr --lines 100
如果页面首页正常,但刷新动态路由出现 404,需要确认请求是否进入 Next.js,而不是被 Nginx 当作静态文件处理。本文的 location / 会统一代理给 Next.js,通常不会出现 SPA 静态站点那种 try_files 路由问题。
常见错误与排查顺序
访问域名返回 502 Bad Gateway
502 表示 Nginx 收到了请求,但无法从上游 Next.js 应用拿到有效响应。按顺序检查:
- PM2 进程是否在线:
pm2 status
- 本机端口是否可访问:
curl -I http://127.0.0.1:3000
- Nginx 代理地址是否和 PM2 启动端口一致:
grep -R "proxy_pass" /etc/nginx/sites-enabled/
- 查看应用日志:
pm2 logs next-ssr --lines 100
如果 127.0.0.1:3000 不通,是 Node.js/PM2 问题;如果本机端口通但域名 502,多数是 Nginx 配置或代理地址问题。
证书申请失败
常见原因包括:
- DNS 还没解析到当前香港节点;
- 80 端口未放行;
- Nginx 配置测试失败;
- 同一个域名被其他 server block 抢占;
- CDN 或代理层拦截了 Let’s Encrypt 验证请求。
先在服务器外部执行:
curl -I http://example.com
如果 HTTP 都无法访问,先不要重复申请证书。重复失败过多可能触发证书机构的频率限制。
重启服务器后站点不可用
这种情况通常是 PM2 自启没有配置成功,或应用运行在某个临时 shell 会话中。检查:
pm2 status
systemctl status pm2-$USER
如 PM2 列表为空,进入项目目录重新启动并保存:
cd /var/www/nextapp
pm2 start ecosystem.config.cjs
pm2 save
再执行 pm2 startup 并按提示完成 systemd 自启配置。
HTTPS 正常但页面资源加载异常
如果 HTML 能打开,但 JS、CSS 或图片加载失败,重点查看浏览器控制台和 Nginx access log。常见原因有:
- 应用中写死了旧域名或 HTTP 地址;
NEXT_PUBLIC_环境变量未重新构建;- 反向代理没有正确传递
Host和X-Forwarded-Proto; - 图片域名未在
next.config.js的images.domains或remotePatterns中允许。
修改 next.config.js 或构建期环境变量后,需要重新构建:
npm run build
pm2 reload next-ssr --update-env
上线前检查清单
- 域名 A 记录已指向当前香港节点公网 IP,
www与裸域名按需配置; - 安全组或系统防火墙已放行 80、443,只在需要时开放 SSH;
- Next.js 使用
npm run build后由 PM2 运行,不使用next dev; - 应用监听
127.0.0.1:3000,公网入口交给 Nginx; - Nginx反向代理中的
server_name、proxy_pass、日志路径配置正确; sudo nginx -t通过,reload 后 HTTP 可访问;- HTTPS 证书申请成功,
certbot renew --dry-run通过; pm2 save和pm2 startup已完成,服务器重启后应用能自动恢复;.env.production权限已收紧,敏感信息未写入公开仓库;- 使用
curl、浏览器、PM2 日志和 Nginx 日志完成最终验证,再切换正式流量。