Next.js 博客全流程部署:Ubuntu + Nginx + SSL + PM2 实战记录
从零开始将 Next.js 博客部署到 Ubuntu 22.04 服务器的完整流程,包括 Node.js 环境配置、Nginx 反向代理、Let's Encrypt SSL 证书申请、PM2 进程保活,以及 CentOS 7.6 迁移到 Ubuntu 过程中遇到的坑和解决方案。
Next.js 博客全流程部署:Ubuntu + Nginx + SSL + PM2 实战记录
前言
之前博客部署在三丰云的 Ubuntu 22.04 上,重装系统后换了 CentOS 7.6,结果发现 CentOS 7.6 的 yum 源已经全部失效(所有镜像站返回 404),certbot 的 Python 依赖链也断光了。最终决定换回 Ubuntu 22.04。本文记录从零开始的完整部署流程,以及过程中踩过的所有坑。
一、环境准备
安装 Node.js
Ubuntu 22.04 自带 Node.js 版本较老,建议用 NodeSource 安装最新的 LTS 版本:
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs
node -v # 确认版本 ≥ 20
npm -v
克隆项目
git clone git@github.com:joeplover/JoeBlog.git /opt/JoeBlog
cd /opt/JoeBlog
如果是第一次部署,注意配置 Git SSH key:
ssh-keygen -t ed25519 -C "你的邮箱"
cat ~/.ssh/id_ed25519.pub # 添加到 GitHub
ssh -T git@github.com # 验证
配置环境变量
echo 'DATABASE_URL="file:./prisma/dev.db"' > /opt/JoeBlog/.env.production
注意:DATABASE_URL 是 Prisma 连接 SQLite 必需的。如果数据库文件不在 prisma/dev.db,需要从本地 scp 上传。
cd /opt/JoeBlog
npm install
npm run build
生产启动
npm start
这时博客在 http://服务器IP:3000 运行。但直接暴露 3000 端口不安全,需要 Nginx 反代。
二、Nginx 反向代理
安装 Nginx
apt install -y nginx
systemctl start nginx
systemctl enable nginx
Ubuntu 源装的 Nginx 版本是 1.18.0,虽然不算最新但稳定够用。
配置反向代理
创建站点配置 /etc/nginx/sites-available/joeblog:
server {
listen 80;
server_name www.ploverjoe.xyz;
location / {
proxy_pass http://localhost: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_cache_bypass $http_upgrade;
}
}
启用配置:
rm -f /etc/nginx/sites-enabled/default
ln -s /etc/nginx/sites-available/joeblog /etc/nginx/sites-enabled/
nginx -t && systemctl restart nginx
坑:配置文件被误删
部署过程中发现配置文件 /etc/nginx/sites-available/joeblog 意外丢失了,但软链接 /etc/nginx/sites-enabled/joeblog 仍然指向它。结果 Nginx 实际用的是默认配置,导致外部请求被错误处理。
排查方法:检查 ls -la /etc/nginx/sites-enabled/ 确认链接状态,再用 cat /etc/nginx/sites-available/joeblog 确认目标文件存在。
三、SSL 证书——certbot 失败与 acme.sh 替代方案
第一步:尝试 certbot(失败)
apt install -y certbot python3-certbot-nginx
先停 nginx 用 standalone 模式:
systemctl stop nginx
certbot certonly --standalone -d www.ploverjoe.xyz
失败原因: 三丰云(国内机房)的 80/443 端口被上游拦截,即使服务器内 Nginx 在监听,外部请求也无法到达。certbot standalone 启动的临时 web 服务器同样无法被 Let's Encrypt 验证服务器访问到。
错误信息一:Timeout during connect (likely firewall problem) —— 端口上游拦截
错误信息二:Invalid response from ... 404 —— 即使端口通了,请求被三丰云代理层拦截
第二步:UFW 防火墙排查
尝试用 UFW 开放端口:
apt install -y ufw
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
但发现 UFW 在此场景下并没有帮助——问题不出在服务器防火墙,而是云服务商网络层。
第三步:改用 acme.sh DNS 模式(成功)
certbot 走不通时,换用 acme.sh 的 DNS 手动验证模式,完全不需要 80/443 端口:
# 安装 acme.sh
curl https://get.acme.sh | sh
source ~/.bashrc
# 注册邮箱(acme.sh 默认使用 ZeroSSL CA)
~/.acme.sh/acme.sh --register-account -m 1665637752@qq.com
# DNS 手动模式申请
~/.acme.sh/acme.sh --issue -d www.ploverjoe.xyz --dns \\
--yes-I-know-dns-manual-mode-enough-go-ahead-please
执行后会提示添加一条 TXT 记录:
Domain: '_acme-challenge.www.ploverjoe.xyz'
TXT value: 'DdNs_2Xwirt19tH3Bfey4nCPXrSt9Tgh2KDYYcsYYrg'
去域名管理后台(阿里云)添加:
| 记录类型 | 主机记录 | 记录值 |
|---|---|---|
| TXT | _acme-challenge.www | 上面生成的字符串 |
注意:阿里云的主机记录只填前缀,不拼主域名。要得到 _acme-challenge.www.ploverjoe.xyz,主机记录应填 _acme-challenge.www(不是 _acme-challenge)。
添加后等待 2-3 分钟生效,然后 renew 获取证书:
~/.acme.sh/acme.sh --renew -d www.ploverjoe.xyz \\
--yes-I-know-dns-manual-mode-enough-go-ahead-please
安装证书到 Nginx:
mkdir -p /etc/nginx/ssl
~/.acme.sh/acme.sh --install-cert -d www.ploverjoe.xyz \\
--key-file /etc/nginx/ssl/www.ploverjoe.xyz.key \\
--fullchain-file /etc/nginx/ssl/www.ploverjoe.xyz.pem
四、三丰云免备案 CDN
为什么需要免备案
国内云服务器要求 ICP 备案才能使用 80/443 端口。三丰云提供免备案服务,本质是通过他们的 CDN 中转:用户访问域名 → 三丰云 CDN 节点 → 转发到你的服务器。
配置步骤
- 在三丰云后台提交域名白名单:
www.ploverjoe.xyz - 系统分配别名地址:
cloud40099.sfycname.cn - 去阿里云 DNS 把 A 记录改为 CNAME:
| 记录类型 | 主机记录 | 记录值 |
|---|---|---|
| CNAME | www | cloud40099.sfycname.cn |
坑:CNAME 生效但 CDN 节点未同步
CNAME 全球生效后(通过 nslookup www.ploverjoe.xyz 8.8.8.8 确认),访问可能遇到三丰云 CDN 的海外错误页面:
当前地址(45.58.185.251)为海外节点!
这说明 CDN 节点还没完全同步你的域名配置。需要等一段时间(半小时到几小时)或联系三丰云客服。
DNS 缓存问题
Windows 本地 DNS 缓存(路由器 192.168.0.1)可能还有旧 A 记录的缓存,清除方法:
ipconfig /flushdns
或用公共 DNS 验证解析:
nslookup www.ploverjoe.xyz 8.8.8.8
nslookup www.ploverjoe.xyz 223.5.5.5
五、IP 变更的问题
重装系统后服务器 IP 可能改变。我这次从旧 IP 171.80.9.218 变成了新 IP 171.80.9.183。排查方法:
# 查看当前服务器 IP
ip addr show | grep inet
# 查看对外的公网 IP
curl -s ifconfig.me
注意:有些云服务器是 NAT 模式,ip addr 看到的是内网 IP(如 171.80.9.183/21),而 ifconfig.me 返回的是公网出口 IP(如 1.27.141.120)。DNS 记录应使用三丰云提供的 IP(通常是内网 IP)。
六、验证与测试
从服务器本地测试:
curl -I http://127.0.0.1:3000 # 确认 Next.js 运行
curl -I http://127.0.0.1 # 确认 Nginx 反代
curl -I https://127.0.0.1 -k # 确认 HTTPS(跳过证书验证)
从外部测试:
curl -I http://www.ploverjoe.xyz
curl -I https://www.ploverjoe.xyz
正常响应应该是:HTTP → 301 跳转,HTTPS → 200 返回博客 HTML。
七、PM2 进程保活
npm install -g pm2
cd /opt/JoeBlog
pm2 start npm --name "joeblog" -- start
pm2 save
pm2 startup
pm2 startup 会提示执行一行命令(类似 env PATH=... pm2 startup systemd -u root),复制执行即可。
常用命令:
pm2 status # 查看状态
pm2 logs joeblog # 查看日志
pm2 restart joeblog # 重启
pm2 stop joeblog # 停止
八、坑点总结
| 问题 | 原因 | 解决 |
|---|---|---|
| certbot 验证失败 | 三丰云拦截 80/443 | 改用 acme.sh DNS 模式 |
| Nginx 反代不生效 | 配置文件被误删,链接悬空 | ls -la 检查配置文件和软链接 |
| 域名无法访问 | 重装系统后 IP 变更 | ip addr 确认当前 IP,更新 DNS |
| CNAME 生效后仍报错 | CDN 节点未同步 | 等待或联系客服 |
| 路由器 DNS 缓存 | DNS 指向旧 IP | ipconfig /flushdns 或临时改公共 DNS |
| CentOS 7 yum 源失效 | 系统太旧镜像已下线 | 改用 Ubuntu 22.04 |
九、最终架构
用户浏览器 → https://www.ploverjoe.xyz
→ 三丰云 CDN(cloud40099.sfycname.cn)
→ Nginx(:443 SSL → :3000 反代)
→ Next.js(:3000)
→ SQLite(prisma/dev.db)
整个部署流程的核心验证点:npm start 确认 3000 可访问、nginx -t 确认配置正确、浏览器打开 https://域名 确认 HTTPS 生效。