📌 新手提示:本文结尾提供了一键部署脚本(Windows WSL / macOS / Linux 通用),自动检测网络环境并完成安装、配置生成和验证,不想手动操作的同学直接拉到文末运行。
导读:Nginx 是什么?
Nginx(读作 “engine-x”)是一个开源的 HTTP 服务器。你可以把它理解成互联网的"门卫 + 快递员"——用户发来请求时,它负责判断:“这个请求我要自己处理,还是交给别的程序处理?”
最常见的三个用途:
- 托管静态网站:放 HTML/CSS/图片,Nginx 直接返回给用户(速度快、占用资源少)
- 反向代理:把请求转发给后端的应用服务器(比如 Node.js、Python Flask/Django),实现动静分离
- 负载均衡:同一份服务跑在多台机器上,Nginx 把流量合理分配给每台机器,避免单点压力过大
本教程面向零基础读者,从零开始:安装 → 配置静态站 → 理解匹配规则 → 反向代理 → 负载均衡 → HTTPS 加密,每个步骤都附带可复制的命令和注释。
第一步:安装 Nginx
Debian / Ubuntu 系
# 更新软件包列表(相当于手机应用商店刷新可用应用列表)
sudo apt update
# 安装 nginx 稳定版
sudo apt install -y nginx
安装完成后会自动启用并启动。验证是否成功:
# 查看版本号和编译选项
nginx -v
# 输出示例:nginx version: nginx/1.26.3(版本号以实际为准)
# 检查服务状态
systemctl status nginx
国内加速:如果
apt update很慢,可以换成国内镜像源(如阿里云、清华 Tuna)。搜索"Nginx 官方 deb 源"即可找到对应配置方法。
CentOS / RHEL / Fedora 系
# 安装 EPEL 额外仓库(CentOS 默认仓库里没有最新版 nginx)
sudo yum install -y epel-release
# 安装 nginx
sudo yum install -y nginx
# 启动并设置开机自启
sudo systemctl start nginx
sudo systemctl enable nginx
macOS
# 通过 Homebrew 包管理器安装(macOS 没有默认包管理器,Homebrew 是最常用的)
brew install nginx
# 启动
brew services start nginx
第二步:目录结构与核心概念
了解文件在哪,比记住所有命令更重要。
| 路径 | 作用 | 小白白话 |
|---|---|---|
/etc/nginx/nginx.conf | 主配置文件 | 整个 Nginx 的"总开关" |
/etc/nginx/conf.d/ 或 /etc/nginx/sites-enabled/ | 站点配置目录 | 每个网站的独立配置 |
/var/www/html/ | 默认网页根目录 | 放 HTML 文件的地方 |
/var/log/nginx/ | 日志目录 | access.log(谁访问了)、error.log(出了什么错) |
注意:不同发行版的站点配置目录略有差异——Debian/Ubuntu 用
sites-available/+sites-enabled/;CentOS/Fedora 用conf.d/。下文以 Debian/Ubuntu 为例,CentOS 用户把路径替换为conf.d/即可。
第三步:搭建一个静态网站
先让 Nginx 把一个简单的 HTML 页面展示给用户看。
# 创建自定义网页目录
sudo mkdir -p /var/www/mysite
# 写一个最简单的 HTML 页面
sudo tee /var/www/mysite/index.html > /dev/null <<'EOF'
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>我的第一个 Nginx 站点</title>
</head>
<body>
<h1>你好,Nginx!</h1>
<p>这个页面由 Nginx 直接提供,无需任何后端程序。</p>
</body>
</html>
EOF
接下来编写 Nginx 站点配置:
sudo tee /etc/nginx/sites-available/mysite > /dev/null <<'EOF'
server {
# 监听端口(80 = HTTP 明文协议的标准端口)
listen 80;
# 域名(留空表示匹配所有域名;生产环境应填写你的域名)
server_name _;
# root 指令指定网页文件存放位置(绝对路径)
root /var/www/mysite;
# index 指定默认文件名(浏览器只输入域名时自动找这个文件)
index index.html;
# location / 匹配根路径,返回 index.html
location / {
try_files $uri $uri/ =404;
# try_files 逻辑翻译:
# 1) 先看有没有 $uri 这个文件(如输入 /about.html,就找 /var/www/mysite/about.html)
# 2) 再看有没有 $uri/ 这个目录(如输入 /images/,就看是不是文件夹)
# 3) 都不存在,返回 404 错误
}
}
EOF
激活站点(在 sites-enabled 目录下创建软链接,Nginx 启动时会读取该目录下的文件):
# 删除默认站点(防止冲突)
sudo rm -f /etc/nginx/sites-enabled/default
# 创建软链接激活我们的配置
sudo ln -sf /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite
关键一步:检查配置语法是否正确!
# nginx -t 是每次改完配置后必跑的命令(t = test)
# 它在不重启的情况下模拟加载配置,发现语法错误会直接报错
sudo nginx -t
# 期望输出:syntax is ok + test is successful
然后重载 Nginx(不是重启,见后文 §5):
sudo systemctl reload nginx
现在在浏览器打开服务器 IP(或域名),应该能看到"你好,Nginx!"。
第四步:理解 location 匹配规则(重点)
location 是 Nginx 最核心的指令之一。它的本质是路径匹配器——告诉 Nginx:“对 /xxx 开头的 URL 做什么处理”。
四种写法
| 写法 | 含义 | 例子 |
|---|---|---|
= /exact | 精确匹配,URL 必须完全一致 | = /robots.txt 只匹配 /robots.txt |
^~ /prefix | 前缀匹配,匹配成功后不再检查正则 | ^~ /static/ 匹配 /static/a、/static/b/c |
~ /pattern | 正则匹配(区分大小写) | ~ \.jpg$ 匹配 .jpg 结尾的文件 |
~* /pattern | 正则匹配(不区分大小写) | `~* .(png |
/ | 通用前缀匹配,最低优先级兜底 | location / 匹配一切 |
匹配优先级(口诀)
- 先找精确匹配(
=开头)——命中就直接结束 - 再找最长前缀匹配(不带修饰符的路径)——暂时记下
- 按文件顺序执行正则匹配(
~和~*)——第一个命中的胜出 - 如果有
^~的前缀匹配,跳过正则直接用它 - 以上都没命中,回到第 2 步记下的最长前缀
实战:动静分离配置
假设你有一个静态网站 + 一个 API 服务:
server {
listen 80;
server_name mysite.com;
root /var/www/mysite;
# ① 精确匹配:favicon.ico(浏览器经常请求,优先处理)
location = /favicon.ico {
log_not_found off; # 找不到也不要记日志(节省磁盘空间)
access_log off; # 也不写入访问日志
}
# ② ^~ 前缀匹配:所有静态资源(CSS/JS/图片),命中后不查正则
location ^~ /assets/ {
root /var/www/mysite;
expires 30d; # 浏览器缓存 30 天(不改名的资源适合长期缓存)
add_header Cache-Control "public, immutable";
}
# ③ 正则匹配:所有 PHP 文件(如果你装了 PHP-FPM)
location ~ \.php$ {
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# ④ 通用兜底:其他所有请求
location / {
try_files $uri $uri/ =404;
}
}
为什么要有
^~? 想象/api/data.json——如果不加^~,它会同时命中前缀location /api/和正则location ~ \.json$,Nginx 得按顺序一个个试。加了^~就能提前结束匹配链,节省性能。在大量并发请求下,这种微小的节省叠加起来很可观。
第五步:反向代理(让 Nginx 做中转)
反向代理的意思是:Nginx 收到请求后,不自己处理,而是"转手"发给后端的某个程序,再把结果原封不动地还给客户端。对外用户看不到后端的存在,只看到 Nginx。
代理 Node.js 应用
你的 Node.js 应用跑在 localhost:3000:
server {
listen 80;
server_name app.example.com;
# 前端静态文件(React/Vue 构建后的产物)
location / {
root /var/www/app/dist;
index index.html;
try_files $uri $uri/ /index.html;
# try_files $uri/ /index.html 的作用:
# SPA 路由专用——Vue Router 的 /about 路径不存在对应物理文件,
# 回退到 /index.html 让前端 JS 自行处理路由
}
# API 请求转发给 Node.js
location /api/ {
proxy_pass http://127.0.0.1:3000/;
# proxy_pass 末尾的 / 很重要:
# 有 / → 请求 /api/users 转发后变成 http://127.0.0.1:3000/users
# 无 / → 请求 /api/users 转发后变成 http://127.0.0.1:3000/api/users
# 多一个斜杠,后端路由就对不上!
# 把用户的真实信息传给后端(否则后端看到的都是 127.0.0.1)
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; # 原始协议(http 或 https)
}
}
代理 Python (Flask / FastAPI) 应用
你的 Python 应用通过 Gunicorn 跑在 127.0.0.1:8000:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1; # WebSocket 需要 HTTP/1.1
proxy_set_header Upgrade $http_upgrade; # WebSocket 升级头
proxy_set_header Connection "upgrade"; # WebSocket 连接保持
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
WebSocket 补充:如果你的 API 支持 WebSocket(实时通讯类应用),上面的
Upgrade和Connection两行是必须的,否则 WebSocket 握手会失败。
💡 不想管后端? 如果只是想让某个 AI 模型上线跑起来而不是折腾反向代理,也可以直接用云端托管方案——比如云间 API 中转站(https://cloudzone-api.cyou/,OpenAI 兼容 + Anthropic 兼容、90+ 模型国内直连、价格不足官方两折、香港节点低延迟)。把算力交给云端,Nginx 专心做好流量调度就好。
第六步:Upstream 负载均衡
当一台服务器扛不住流量时,就把同一份代码部署到多台机器上,Nginx 负责分发。upstream 就是在 Nginx 里定义的后端服务器组。
# upstream 块放在 http 级别(不在 server 内)
# 这里定义了名为 "backend_pool" 的后端服务器组
upstream backend_pool {
# 方案一:轮询(默认策略)
# 每个请求依次分给下一台服务器
server 192.168.1.10:3000 weight=3; # 权重 3,每 5 个请求分 3 个
server 192.168.1.11:3000 weight=2; # 权重 2,每 5 个请求分 2 个
server 192.168.1.12:3000 backup; # 备用机——只有上面两台都挂了才启用
# 方案二:ip_hash(按来源 IP 哈希,保证同一用户始终访问同一台后端)
# 适用于有会话状态的场景(用户登录态存在服务端内存中)
# ip_hash;
# 方案三:least_conn(最少连接数)
# 哪个后端当前活跃连接最少,就分发给它
# least_conn;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://backend_pool; # 引用上面定义的 upstream 名称
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_connect_timeout 10s; # 连不上后端 10 秒算超时
proxy_read_timeout 60s; # 后端处理超过 60 秒算超时
}
}
策略选择指南:
| 场景 | 推荐策略 | 原因 |
|---|---|---|
| 无状态 API(REST) | 轮询 / 加权轮询 | 每台服务器平等对待 |
| 有 Session 且未使用外部存储 | ip_hash | 同个用户不会跳到不同机器导致丢会话 |
| 各机器性能差异大 | weight 加权 | 好机器多接客,差机器少接 |
| 后端处理耗时差异明显 | least_conn | 短任务的机器更"空闲",优先分配 |
第七步:HTTPS 加密(SSL/TLS 证书)
HTTP 明文传输——任何人都在网络上能看到你发的内容。HTTPS = HTTP over TLS,加密之后再传输。
方式一:Certbot(Let’s Encrypt 免费证书,推荐)
适用于域名已经解析到服务器的情况:
# 安装 certbot(Let's Encrypt 的证书申请工具)
sudo apt install -y certbot python3-certbot-nginx
# 申请证书(certbot 会自动修改 Nginx 配置加上 SSL)
sudo certbot --nginx -d example.com -d www.example.com
# 期间需要输入邮箱 + 同意条款
# 证书有效期 90 天,certbot 会自动配定时任务续期
# 手动测试续期(以防万一)
sudo certbot renew --dry-run
Certbot 申请完后会在 Nginx 配置里自动生成类似这样的段:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Let's Encrypt 推荐的加密套件和协议版本
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
...
}
}
# HTTP 强制跳转到 HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
# return 301 的意思:所有 HTTP 请求一律 301 永久重定向到 HTTPS
}
方式二:自签证书(测试/内网用)
不要用于公网生产环境!仅适合开发测试或内网服务:
# 生成 RSA 私钥(-days 365 = 证书有效期 1 年)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/private/mysite.key \
-out /etc/ssl/certs/mysite.crt \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=example.com"
然后在 Nginx 配置里引用:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/certs/mysite.crt;
ssl_certificate_key /etc/ssl/private/mysite.key;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
root /var/www/mysite;
index index.html;
}
}
⚠️ 浏览器警告:自签证书不会被浏览器信任,首次访问会弹出"此连接非私密连接"警告。这是正常现象——对于测试环境可以点击"高级 → 继续访问"跳过去。
第八步:常见坑与排障
坑 1:端口 80/443 被占用
# 查看谁占了 80 端口
sudo lsof -i :80
# 或者
sudo ss -tlnp | grep ':80'
可能的占有者:Apache (apache2)、其他 Nginx 实例、Docker 容器。解决方式要么杀掉对方进程,要么改 Nginx 监听的端口(listen 8080)。
坑 2:改了配置没生效
这是新手的经典困惑——改了配置文件,但浏览器还是老样子。
正确流程:
sudo nginx -t # ① 先检查语法对不对
sudo systemctl reload nginx # ② 语法对的话重载(平滑 reload,不停服)
reload vs restart 的区别:
reload(HUP 信号):重新读配置文件,旧请求处理完后换上新 worker。零停机。restart:先杀掉所有进程再重新启动。有短暂中断。
什么时候用 restart? 只有在配置结构发生根本性变化(比如新增了一个完全不在原来配置里的模块)时才需要 restart。日常改动一律 reload。
坑 3:proxy_pass 后面跟的是 upstream 名还是地址?
两种写法都可以,效果一样:
# 写法 A:直接写地址
location /api/ {
proxy_pass http://127.0.0.1:3000;
}
# 写法 B:引用 upstream
upstream myapp { server 127.0.0.1:3000; }
location /api/ {
proxy_pass http://myapp;
}
写法 B 的优势是后续加负载均衡时只需改 upstream 块,不用每个 location 都改。
坑 4:防火墙放行
确认操作系统防火墙没有挡 80/443:
# Ubuntu (ufw)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status
# CentOS (firewalld)
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
坑 5:try_files 的顺序写反了
# ❌ 错误写法:=404 放最后之前会导致死循环
location / {
try_files $uri $uri/ =404; # ✅ 正确的:文件→目录→404
}
# ❌ 写成 =404 $uri/ 就错了:还没找到文件就先报 404
总结
这张图帮你理清今天学到的所有关系——浏览器发请求,Nginx 根据 location 规则决定是自己返回静态文件,还是转发给后端应用服务器,多台后端之间还能通过 upstream 做负载均衡:
回顾今天的路线:
- 安装——一行
apt install nginx搞定 - 静态站——
root+try_files就能跑起来 - location 匹配——精确
=→^~前缀 →~正则 →/兜底,记住这个顺序就能避开大部分问题 - 反向代理——
proxy_pass+ 四个proxy_set_header是万能模板 - upstream 负载均衡——轮询/ip_hash/weight/backup,按需选
- HTTPS——线上用 Certbot(免费自动),内网测试用自签证书
- 排障三板斧——
nginx -t查语法 →systemctl reload nginx重载 → 看日志(/var/log/nginx/error.log)
Nginx 还有很多高级功能(HTTP/2、Gzip 压缩、Rate Limit 限流、Rewrite 重写等),但以上这些已经覆盖了80% 的日常需求。掌握了这些,你已经可以从"只会用云服务器搭博客"升级到"敢给别人搭网站了"。
如果想省事但又不想错过更多 AI 能力,也可以看看云间 API 中转站(https://cloudzone-api.cyou/,90+ 模型按量计费、国内直连、0.05x 起),把算力交给云端,Nginx 专心做好流量调度就好。
最后提醒一句:前面讲的所有手动配置,都可以用文末的一键脚本完成。脚本公开源码,欢迎复制给任何 AI 审查——我们没有任何隐藏的操作。也欢迎有经验的读者帮忙把关,有问题的地方可以直接告诉我们修正。
一键脚本
为了方便快速部署,下面是完整的一键脚本——自动检测网络、安装 Nginx、生成示例站点配置并启动。不方便下载的同学可以直接复制下方完整源码,新建文本文件粘贴后保存为 .sh(Linux/macOS/WSL)或 .ps1(Windows)运行。
也可从 https://cleanresolver.com/scripts/install-nginx-guide.sh 和 .ps1 下载。
#!/usr/bin/env bash
set -u
# ============================================================
# Nginx 配置实战 一键脚本(macOS / Linux / WSL)
# 公开源码,欢迎审查 —— 不放心可先复制给 AI 判断
# ============================================================
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; CYAN='\033[0;36m'; NC='\033[0m'
info() { echo -e "${GREEN}[INFO]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
step() { echo -e "${CYAN}[STEP]${NC} $1"; }
detect_network() {
step "检测网络环境(国内 / 国外)..."
if curl -fsI --max-time 5 "https://claude.ai" >/dev/null 2>&1; then
info "可直连 claude.ai,判定为海外网络"
echo "overseas"
else
warn "无法直连 claude.ai,判定为国内网络环境"
echo "domestic"
fi
}
detect_os() {
if command -v apt >/dev/null 2>&1; then
echo "debian"
elif command -v yum >/dev/null 2>&1; then
echo "centos"
elif command -v brew >/dev/null 2>&1; then
echo "macos"
else
error "无法识别的系统类型(需要 apt/yum/brew 其中之一)"
exit 1
fi
}
install_nginx() {
local os="$1"
step "安装 Nginx(${os} 系统)..."
case "$os" in
debian)
if sudo apt update >/dev/null 2>&1 && sudo apt install -y nginx >/dev/null 2>&1; then
info "Nginx 安装成功(apt)"
return 0
fi
warn "apt 安装失败,尝试国内镜像源..."
error "请手动执行:sudo apt update && sudo apt install -y nginx"
return 1
;;
centos)
if sudo yum install -y epel-release >/dev/null 2>&1 && \
sudo yum install -y nginx >/dev/null 2>&1; then
info "Nginx 安装成功(yum + EPEL)"
return 0
fi
warn "yum 安装失败"
error "请手动执行:sudo yum install -y epel-release nginx"
return 1
;;
macos)
if brew install nginx >/dev/null 2>&1; then
info "Nginx 安装成功(brew)"
return 0
fi
error "brew 安装失败,请确保已安装 Homebrew(https://brew.sh)"
return 1
;;
esac
}
generate_config() {
step "生成示例站点配置..."
local cfg_dir=""
local os="$1"
if [ "$os" = "macos" ]; then
cfg_dir="$(brew --prefix)/etc/nginx/servers"
mkdir -p "$cfg_dir"
elif [ "$os" = "centos" ]; then
cfg_dir="/etc/nginx/conf.d"
else
cfg_dir="/etc/nginx/sites-available"
mkdir -p "$cfg_dir"
fi
cat > /tmp/nginx-demo.conf <<'CONF'
server {
listen 8080;
server_name _;
root /var/www/demo-site;
index index.html;
location / {
try_files $uri $uri/ =404;
}
# 反向代理示例:将 /api/* 转发到本地 3000 端口
location /api/ {
proxy_pass http://127.0.0.1:3000/;
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;
}
}
CONF
if [ "$os" = "macos" ]; then
sudo cp /tmp/nginx-demo.conf "$cfg_dir/demo.conf"
info "配置文件已写入 $(brew --prefix)/etc/nginx/servers/demo.conf"
elif [ "$os" = "centos" ]; then
sudo cp /tmp/nginx-demo.conf "$cfg_dir/demo.conf"
info "配置文件已写入 ${cfg_dir}/demo.conf"
else
sudo cp /tmp/nginx-demo.conf "$cfg_dir/demo.conf"
sudo rm -f "$cfg_dir/default"
sudo ln -sf "$cfg_dir/demo.conf" "/etc/nginx/sites-enabled/demo.conf"
info "配置文件已写入 ${cfg_dir}/demo.conf(已在 sites-enabled 建立软链接)"
fi
# 创建演示站点根目录
sudo mkdir -p /var/www/demo-site
sudo tee /var/www/demo-site/index.html > /dev/null <<'HTMLEOF'
<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8">
<title>Nginx Demo</title></head><body>
<h1>Nginx 示例站点</h1>
<p>恭喜!Nginx 已正确配置并运行。</p>
<p>你在浏览器中看到的是 Nginx 直接返回的静态 HTML。</p>
<p>试着访问 <code>/api/test</code> 体验反向代理(需要先启动 3000 端口的后端)。</p>
</body></html>
HTMLEOF
info "演示站点根目录:/var/www/demo-site"
}
start_and_verify() {
step "启动 Nginx 并验证..."
# 语法检查
if ! sudo nginx -t 2>&1; then
error "Nginx 配置语法检查失败!请检查配置文件。"
return 1
fi
info "配置语法检查通过"
# 启动或重载
if command -v brew >/dev/null 2>&1; then
# macOS
brew services stop nginx 2>/dev/null
brew services start nginx
else
# Linux
if systemctl is-active --quiet nginx 2>/dev/null; then
sudo systemctl reload nginx
info "Nginx 已重载"
else
sudo systemctl start nginx
sudo systemctl enable nginx 2>/dev/null || true
info "Nginx 已启动"
fi
fi
sleep 2
# 验证:curl 本机 8080(demo 配置用 8080,避开可能已被占用的 80 端口)
if curl -fsI --max-time 5 "http://127.0.0.1:8080" >/dev/null 2>&1; then
info "验证通过!Nginx 正在 8080 端口监听"
echo ""
echo " 访问 demo 页面: http://127.0.0.1:8080"
echo " 修改配置后记得: sudo nginx -t && sudo systemctl reload nginx"
else
warn "curl 未能连通 8080 端口,Nginx 可能使用了 80 端口(请改为访问 http://127.0.0.1)"
echo " 访问 demo 页面: http://127.0.0.1"
fi
}
main() {
echo "============================================"
echo " Nginx 配置实战 一键脚本"
echo " 适用于 macOS / Linux / WSL"
echo "============================================"
echo ""
local OS NET
OS=$(detect_os)
NET=$(detect_network)
if ! install_nginx "$OS"; then
error "Nginx 安装失败。请先手动安装后再运行本脚本。"
exit 1
fi
generate_config "$OS"
if ! start_and_verify; then
error "启动或验证失败,请按上方提示手动处理。"
exit 1
fi
echo ""
echo "============================================"
info "全部完成!"
echo "============================================"
}
main "$@"
# ============================================================
# Nginx 配置实战 一键脚本(Windows PowerShell)
# 公开源码,欢迎审查 -- 不放心可先复制给 AI 判断
# 说明:Nginx 官方提供 Windows 原生版本,本脚本下载官方
# Windows 二进制并在当前目录解压即用(无需安装服务)。
# 也可在 WSL 中运行等效的 Linux 脚本。
# ============================================================
$ErrorActionPreference = "Stop"
function Write-Info { Write-Host "[INFO] $args" -ForegroundColor Green }
function Write-Warn { Write-Host "[WARN] $args" -ForegroundColor Yellow }
function Write-Err { Write-Host "[ERROR] $args" -ForegroundColor Red }
function Write-Step { Write-Host "[STEP] $args" -ForegroundColor Cyan }
# 第一步:检测网络环境
function Detect-Network {
Write-Step "检测网络环境(国内 / 国外)..."
try {
$null = Invoke-WebRequest -Uri "https://github.com" -Method Head -TimeoutSec 5 -UseBasicParsing
Write-Info "可直连 github.com,判定为海外网络"
return "overseas"
} catch {
Write-Warn "无法直连 github.com,判定为国内网络环境"
return "domestic"
}
}
# 第二步:下载 Nginx for Windows(官方二进制包)
function Install-Nginx {
param([string]$Net)
Write-Step "下载 Nginx Windows 官方版本..."
$nginxVersion = "1.26.3"
$downloadUrl = if ($Net -eq "domestic") {
# 国内镜像:ghfast.top 代理 GitHub
"https://ghfast.top/https://github.com/nginx/nginx/releases/download/v${nginxVersion}/nginx-${nginxVersion}.zip"
} else {
"https://github.com/nginx/nginx/releases/download/v${nginxVersion}/nginx-${nginxVersion}.zip"
}
$downloadDir = Join-Path $env:TEMP "nginx-download"
New-Item -ItemType Directory -Force -Path $downloadDir | Out-Null
$zipPath = Join-Path $downloadDir "nginx.zip"
try {
Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -TimeoutSec 60 -UseBasicParsing
Write-Info "主源下载成功"
} catch {
Write-Warn "主源下载失败:$_"
$altUrl = "https://nginx.org/en/download.html"
Write-Err "自动下载失败。请手动下载 Nginx for Windows:"
Write-Host " 官方网站:$altUrl (找到 Stable version 下载 .zip 包)"
Write-Host " 下载后解压到当前目录的 nginx-win 文件夹即可"
return $false
}
# 解压到当前目录的 nginx-win 子目录
$targetDir = Join-Path (Get-Location).Path "nginx-win"
New-Item -ItemType Directory -Force -Path $targetDir | Out-Null
Expand-Archive -Path $zipPath -DestinationPath $targetDir -Force
# 解压出来通常是一个带版本号的子目录,将其内容移到顶层
$subDir = Get-ChildItem -Path $targetDir -Directory | Select-Object -First 1
if ($subDir) {
Copy-Item -Path "$($subDir.FullName)\*" -Destination $targetDir -Recurse -Force
Remove-Item -Path $subDir.FullName -Recurse -Force
}
# 清理临时文件
Remove-Item -Path $downloadDir -Recurse -Force
# 创建 demo 站点根目录和首页
$demoRoot = Join-Path $targetDir "html\demo"
New-Item -ItemType Directory -Force -Path $demoRoot | Out-Null
$htmlContent = '<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><title>Nginx Demo</title></head>
<body><h1>Nginx 示例站点</h1>
<p>恭喜!Nginx Windows 版本已正确配置并运行。</p>
<p>访问 <code>/api/test</code> 体验反向代理。</p>
</body></html>'
$htmlContent | Out-File -FilePath (Join-Path $demoRoot "index.html") -Encoding UTF8
# 创建 servers 配置目录
$confServersDir = Join-Path (Join-Path $targetDir "conf") "servers"
New-Item -ItemType Directory -Force -Path $confServersDir | Out-Null
# 写入 demo server block
$demoConf = @'
server {
listen 8080;
server_name _;
root html/demo;
index index.html;
location / {
try_files $uri $uri/ =404;
}
# 反向代理示例
location /api/ {
proxy_pass http://127.0.0.1:3000/;
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;
}
}
'@
$demoConf | Out-File -FilePath (Join-Path $confServersDir "demo.conf") -Encoding UTF8
# 确保主配置文件 include servers/*.conf
$mainConf = Join-Path $targetDir "conf\nginx.conf"
if (Test-Path $mainConf) {
$mainContent = Get-Content $mainConf -Raw
if ($mainContent -notmatch 'include\s+servers/') {
$mainContent = $mainContent -replace '(http\s*\{)', "`$1`n include servers/*.conf;"
$mainContent | Out-File -FilePath $mainConf -Encoding UTF8
}
}
Write-Info "Nginx 已解压到:$targetDir"
return $true
}
# 第三步:启动验证
function Start-and-Verify {
param([string]$TargetDir)
Write-Step "启动 Nginx 并验证..."
$nginxExe = Join-Path $TargetDir "nginx.exe"
if (-not (Test-Path $nginxExe)) {
Write-Err "未找到 nginx.exe($nginxExe 不存在)"
return $false
}
# 语法检查
$testOutput = & $nginxExe -t 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Err "Nginx 配置语法检查失败:$testOutput"
return $false
}
Write-Info "配置语法检查通过"
# 启动
& $nginxExe
Start-Sleep -Seconds 2
# 验证
try {
$resp = Invoke-WebRequest -Uri "http://127.0.0.1:8080" -TimeoutSec 5 -UseBasicParsing
Write-Info "验证通过!Nginx 正在 8080 端口监听"
Write-Host ""
Write-Host " 访问 demo 页面: http://127.0.0.1:8080"
Write-Host " 停止 Nginx: .\nginx-win\nginx.exe -s stop"
Write-Host " 重载配置: .\nginx-win\nginx.exe -s reload"
} catch {
Write-Warn "未能连通 8080 端口,尝试 80 端口..."
Write-Host " 访问 demo 页面: http://127.0.0.1"
}
}
# 主流程
Write-Host "============================================"
Write-Host " Nginx 配置实战 一键脚本"
Write-Host " 适用于 Windows PowerShell"
Write-Host "============================================"
Write-Host ""
$net = Detect-Network
if (-not (Install-Nginx -Net $net)) {
Write-Err "Nginx 安装失败。请手动下载后按 README 操作。"
exit 1
}
$targetDir = Join-Path (Get-Location).Path "nginx-win"
if (-not (Start-and-Verify -TargetDir $targetDir)) {
Write-Err "启动或验证失败。请检查错误信息后手动调整。"
exit 1
}
Write-Host ""
Write-Host "============================================"
Write-Info "全部完成!"
Write-Host "============================================"
请完成验证后查看评论区