Ubuntu Nginx站点部署实录:从配置详解到自动化脚本
Ubuntu Nginx站点部署实录:从配置详解到自动化脚本
前言:
最近为了把跑在内网8090端口的博客服务发布到公网,我折腾了一番 Nginx。
目标很简单:通过域名blog.twopair.cn访问,由 HTTP 自动跳转 HTTPS,且以后要能轻松管理多个站点。
这里的记录涵盖了从环境安装、配置文件原理详解,到最终封装自动化脚本的全过程。
一、 起步:环境与基础
在 Ubuntu 20.04/22.04 上,第一步永远是把环境铺好。
# 1. 更新源并安装 Nginx 和解压工具
sudo apt update
sudo apt install nginx unzip -y
# 2. 确认防火墙放行 HTTP/HTTPS,否则外网白搭
sudo ufw allow 'Nginx Full'
二、 核心逻辑:Nginx 的“仓库”与“柜台”
Ubuntu 下 Nginx 的目录结构设计非常巧妙,理解这个逻辑是手动配置的基础:
/etc/nginx/sites-available/(仓库):
存放配置文件的原稿。写在这里的文件 Nginx 是不读的,相当于“草稿箱”。/etc/nginx/sites-enabled/(柜台):
存放指向仓库文件的软链接(快捷方式)。Nginx 启动时只加载这里的内容。
我的管理逻辑:
- 上线:用
ln -s把仓库里的文件链接到柜台。 - 下线:用
rm删掉柜台里的链接(原稿保留,随时可恢复)。
三、 深度解析:Nginx 配置文件写了什么?
在使用脚本“一键生成”之前,必须搞懂配置文件里每一行的含义。以我的 blog.twopair.cn 为例,一个标准的HTTPS + 反向代理配置如下:
1. 证书存放规范
为了不乱,我统一将证书存放在 /etc/nginx/cert/<域名>/ 目录下:
.pem/.crt:公钥/证书链。.key:私钥(注意权限保护)。
2. 配置文件拆解
通常包含两个 server 块。
第一部分:HTTP 强制跳转 HTTPS
拦截所有 80 端口请求,将其扔给 443 端口。
server {
listen 80;
server_name blog.twopair.cn; # 匹配我的域名
# 301 永久重定向到 HTTPS,保留原本的请求路径
return 301 https://$host$request_uri;
}
第二部分:HTTPS 核心配置
server {
# --- 基础监听 ---
listen 443 ssl;
server_name blog.twopair.cn;
# --- SSL 证书配置 ---
# 这里指向我解压后的证书路径
ssl_certificate /etc/nginx/cert/blog.twopair.cn/fullchain.pem;
ssl_certificate_key /etc/nginx/cert/blog.twopair.cn/privkey.key;
# --- 关键:解决上传报错 ---
# 默认 Nginx 限制上传 1MB,博客传大图会报 413 Too Large
# 手动改成了 100M
client_max_body_size 100M;
# --- 反向代理设置 (连接后端) ---
location / {
proxy_pass http://127.0.0.1:8090; # 转发给本地 8090 端口
# --- 请求头传递 (Header) ---
# 如果不加下面这几行,后端服务拿到的 IP 全是 127.0.0.1
# 这会导致日志分析和 IP 限制功能失效
proxy_set_header Host $host; # 传递原始域名
proxy_set_header X-Real-IP $remote_addr; # 传递用户真实IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # 告诉后端原本是 https
}
}
四、 终极方案:自动化管理脚本
为了以后不再手动复制粘贴上述配置,我让AI写了一个脚本 vhost_manager.sh。
它会自动部署站点:解压证书 -> 生成上述配置 -> 建立软链接 -> 重载 Nginx。
1. 脚本代码
保存为 vhost_manager.sh 并赋予执行权限 chmod +x vhost_manager.sh。
#!/bin/bash
# Nginx 站点管理工具
set -euo pipefail
NGINX_AVAILABLE="/etc/nginx/sites-available"
NGINX_ENABLED="/etc/nginx/sites-enabled"
CERT_BASE="/etc/nginx/cert"
error() {
echo "错误:$1" >&2
exit 1
}
checkRoot() {
if [ "$(id -u)" -ne 0 ]; then
error "请使用 root 或 sudo 执行该脚本"
fi
}
reloadNginx() {
nginx -t || error "Nginx 配置校验失败"
systemctl reload nginx
}
case "${1:-}" in
deploy)
checkRoot
DOMAIN="${2:-}"
PORT="${3:-}"
ZIP_PATH="${4:-}"
if [ -z "$DOMAIN" ] || [ -z "$PORT" ] || [ -z "$ZIP_PATH" ]; then
error "用法: $0 deploy <domain> <port> <cert.zip>"
fi
if ! [[ "$PORT" =~ ^[0-9]+$ ]]; then
error "端口号必须是数字"
fi
if [ ! -f "$ZIP_PATH" ]; then
error "证书 ZIP 文件不存在: $ZIP_PATH"
fi
CERT_DIR="$CERT_BASE/$DOMAIN"
CONF_PATH="$NGINX_AVAILABLE/$DOMAIN"
echo ">>> [1/5] 准备证书目录: $CERT_DIR"
mkdir -p "$CERT_DIR"
unzip -j -o "$ZIP_PATH" -d "$CERT_DIR" > /dev/null \
|| error "证书 ZIP 解压失败"
PEM=$(find "$CERT_DIR" \( -name "*.pem" -o -name "*.crt" \) -type f | head -n 1)
KEY=$(find "$CERT_DIR" -name "*.key" -type f | head -n 1)
if [ -z "$PEM" ] || [ -z "$KEY" ]; then
error "未在证书包中找到 .pem/.crt 或 .key 文件"
fi
chmod 600 "$KEY"
chmod 644 "$PEM"
echo ">>> [2/5] 生成 Nginx 配置: $CONF_PATH"
cat > "$CONF_PATH" <<EOF
server {
listen 80;
server_name $DOMAIN;
return 301 https://\$host\$request_uri;
}
server {
listen 443 ssl http2;
server_name $DOMAIN;
ssl_certificate $PEM;
ssl_certificate_key $KEY;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:$PORT;
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_connect_timeout 60s;
proxy_read_timeout 300s;
}
}
EOF
echo ">>> [3/5] 启用站点"
ln -sf "$CONF_PATH" "$NGINX_ENABLED/$DOMAIN"
echo ">>> [4/5] 校验并重载 Nginx"
reloadNginx
echo ">>> [5/5] 完成"
echo ">>> https://$DOMAIN -> 127.0.0.1:$PORT"
;;
enable)
checkRoot
DOMAIN="${2:-}"
[ -z "$DOMAIN" ] && error "用法: $0 enable <domain>"
ln -sf "$NGINX_AVAILABLE/$DOMAIN" "$NGINX_ENABLED/$DOMAIN"
reloadNginx
echo ">>> 站点 $DOMAIN 已上线"
;;
disable)
checkRoot
DOMAIN="${2:-}"
[ -z "$DOMAIN" ] && error "用法: $0 disable <domain>"
rm -f "$NGINX_ENABLED/$DOMAIN"
reloadNginx
echo ">>> 站点 $DOMAIN 已下线(配置保留)"
;;
remove)
checkRoot
DOMAIN="${2:-}"
[ -z "$DOMAIN" ] && error "用法: $0 remove <domain>"
rm -f "$NGINX_ENABLED/$DOMAIN"
rm -f "$NGINX_AVAILABLE/$DOMAIN"
rm -rf "$CERT_BASE/$DOMAIN"
reloadNginx
echo ">>> 站点 $DOMAIN 已彻底移除"
;;
status)
echo "--- 已启用站点 ---"
ls -l "$NGINX_ENABLED" || echo "暂无已启用站点"
;;
*)
echo "用法:"
echo " $0 deploy <domain> <port> <cert.zip>"
echo " $0 enable <domain>"
echo " $0 disable <domain>"
echo " $0 remove <domain>"
echo " $0 status"
exit 1
;;
esac
五、 实战演示:我的博客部署过程
假设我已经把申请好的 SSL 证书压缩包 blog_cert.zip 上传到了服务器,且博客程序已经在 8090 端口运行。
1. 一键部署
运行脚本,填入我的域名和端口:
./vhost_manager.sh deploy blog.twopair.cn 8090 ./blog_cert.zip
执行后,脚本会自动解压证书、写入上面的那套配置、并重载Nginx部署站点。
2. 日常维护
- 临时维护博客:
./vhost_manager.sh disable blog.twopair.cn
(此时用户访问会挂掉,但配置和证书都在) - 维护完毕上线:
./vhost_manager.sh enable blog.twopair.cn - 以后不再用这个站了:
./vhost_manager.sh remove blog.twopair.cn
(彻底删除干净)
六、 避坑指南
在折腾过程中遇到过几个坑,特此记录:
- 413 Request Entity Too Large:
- 现象:博客上传图片稍微大点就报错。
- 原因:Nginx 默认限制 1MB。
- 解决:脚本里我已经加了
client_max_body_size 100M;。
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 Twopair
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果