安装与快速上手
环境口径:Ubuntu 22.04 + Nginx 1.24(走 nginx 官方 apt 源),示例配置可以直接抄。这篇从装上 Nginx 开始,一路做到「静态站点能访问、反向代理能打通」,中间把目录约定、nginx -t、reload 和 restart 的区别这些天天要用的事交代清楚。
三种安装方式怎么选
| 方式 | 适合场景 | 优点 | 缺点 |
|---|---|---|---|
| apt 官方源 | 生产主力,Ubuntu/Debian | 升级走 apt,systemd 单元和日志轮转都配好了 | 版本跟随发行版,想用新版要自己加 nginx 官方源 |
| 源码编译 | 需要额外模块(如第三方模块、stub_status 之外的扩展) | 模块按需裁剪,编译参数自己定 | 升级要重新编译,得自己写 systemd 单元 |
| Docker | 快速验证、K8s 环境、配置完全自管的场景 | 一条命令起,版本固定,环境干净 | 日志默认在容器内,配置和证书要挂载 |
日常结论:裸机生产用 apt(或 nginx 官方源),需要自定义模块才编译,容器/K8s 里用镜像。下面三种都过一遍。
apt 安装
Ubuntu 22.04 自带源里的 Nginx 是 1.18,稳但版本偏老;想用 1.24+ 就加 nginx 官方源,本系列后面都按 1.24 写。两条路选一条走。
路线一:nginx 官方源(推荐,版本新)
sudo apt install -y curl gnupg2 ca-certificates lsb-release ubuntu-keyring
# 导入官方签名 key
curl -fsSL https://nginx.org/keys/nginx_signing.key \
| sudo gpg --dearmor -o /usr/share/keyrings/nginx-archive-keyring.gpg
# 加 stable 源
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
sudo apt update
sudo apt install -y nginx
路线二:发行版自带源(省事,1.18)
sudo apt update
sudo apt install -y nginx
装完确认版本和编译参数(--with-* 那一串就是编进来的模块):
nginx -v
nginx -V
nginx 官方源的包和 Ubuntu 官方源的包包名相同但文件布局不同(比如官方源不带 sites-available)。已经用 Ubuntu 源装了就别直接切源覆盖,先 apt purge nginx nginx-common 再装,否则残留的配置和单元文件会打架。
源码编译
需要额外模块、或者想控制编译参数时用这条路径。核心是 ./configure 那一串参数:
sudo apt install -y build-essential libpcre3-dev zlib1g-dev libssl-dev
# 下载并解压(版本号按需替换)
curl -LO https://nginx.org/download/nginx-1.24.0.tar.gz
tar xf nginx-1.24.0.tar.gz
cd nginx-1.24.0
./configure \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-http_sub_module \
--with-stream
make -j"$(nproc)"
sudo make install
几个参数的意义:
--with-http_ssl_module:HTTPS 必需,不加连证书都加载不了。--with-http_v2_module:HTTP/2 支持,1.24 里还要配listen 443 ssl http2。--with-http_realip_module:从X-Forwarded-For里还原真实客户端 IP,前面有 LB/CDN 时必须加。--with-http_stub_status_module:暴露/nginx_status,监控要用,第 5 篇讲。--with-stream:四层 TCP/UDP 代理,不需要可以不编译。
编译完还没有 systemd 单元,得自己写一个:
[Unit]
Description=nginx - high performance web server
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/nginx.conf
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
存到 /etc/systemd/system/nginx.service,然后 sudo systemctl daemon-reload && sudo systemctl enable --now nginx。
Docker 安装
docker run -d \
--name nginx \
-p 80:80 \
-p 443:443 \
-v /srv/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
-v /srv/nginx/conf.d:/etc/nginx/conf.d:ro \
-v /srv/nginx/certs:/etc/nginx/certs:ro \
-v /srv/nginx/logs:/var/log/nginx \
nginx:1.24
要点:
- 配置、证书、日志全部挂载出来,容器随时可以删了重建。
- 镜像默认把日志写到
/var/log/nginx/下的软链,指向 stdout/stderr。挂载日志目录会覆盖这个软链,日志就落到宿主机文件上了——两种都行,但别指望挂载后还能docker logs看到 access log。 nginx:1.24-alpine体积小,但没有 bash,排障时exec进去只有 sh,按习惯选。
目录约定
apt 方式装完的目录布局:
/etc/nginx/
├── nginx.conf
├── conf.d/ # 自定义站点放这里,*.conf 自动加载
├── sites-available/ # 可用站点定义
├── sites-enabled/ # 软链,只有链接过来的才生效
├── snippets/ # 可复用的配置片段
├── mime.types
└── fastcgi_params
/var/www/html/ # 默认站点根目录
/var/log/nginx/access.log # 访问日志
/var/log/nginx/error.log # 错误日志
nginx.conf 里的 include 是关键:
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 两个都加载:conf.d 下的 .conf + sites-enabled 下的所有文件
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
官方文档和大多数教程用 conf.d/,Debian 系习惯用 sites-available + 软链。一个项目里只用一套,混着写会出「删了 sites-enabled 里的文件,站点还在跑」的灵异问题——因为 conf.d/ 里还有一份同名的。
nginx -t:改配置后的第一件事
# 只检查语法,不改运行状态
sudo nginx -t
# 检查并把完整生效配置打出来(含所有 include),排查「到底加载了哪些」时神器
sudo nginx -T
成功输出:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
失败时会指出文件名和行号,比如 nginx: [emerg] unknown directive "proxy_passs" in /etc/nginx/conf.d/app.conf:12,按行号改就行。
reload 还是 restart
| 动作 | 命令 | 影响 |
|---|---|---|
| 校验 | nginx -t | 无,只读配置文件 |
| 平滑重载 | nginx -s reload 或 systemctl reload nginx | 老 worker 处理完存量请求后退出,新连接走新配置,不断服 |
| 重启 | systemctl restart nginx | 进程全停再起,会断连,几百毫秒的 502 |
| 优雅停机 | nginx -s quit | 处理完当前请求再退出 |
| 快速停机 | nginx -s stop | 立即退出 |
改配置一律 nginx -t && nginx -s reload,restart 只在二进制升级、端口/用户变更或者 reload 出问题时才用。注意 reload 也有坑:新配置里如果 listen 换端口、证书路径写错,reload 会失败但老进程继续用旧配置跑,服务看起来「没坏」,其实新配置根本没生效——所以一定要看 reload 命令的返回和 error.log。
最小静态站点
sudo mkdir -p /var/www/demo
echo '<h1>Hello Nginx</h1>' | sudo tee /var/www/demo/index.html
新建 /etc/nginx/conf.d/demo.conf:
server {
listen 80;
server_name demo.local;
root /var/www/demo;
index index.html;
location / {
try_files $uri $uri/ =404;
}
# 静态资源单独给长缓存,文件名带 hash 时很安全
location ~* \.(css|js|png|jpg|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public";
}
access_log /var/log/nginx/demo.access.log;
error_log /var/log/nginx/demo.error.log warn;
}
生效并验证:
sudo nginx -t && sudo nginx -s reload
# 本机验证,Host 头要带对,否则命中默认 server
curl -H "Host: demo.local" http://127.0.0.1/
没配 DNS 的话,在 /etc/hosts 里加一行 127.0.0.1 demo.local,浏览器就能直接访问了。
第一个反向代理
假设后端应用跑在 127.0.0.1:8080(Spring Boot、Node、Gunicorn 都行):
upstream demo_app {
server 127.0.0.1:8080;
keepalive 32;
}
server {
listen 80;
server_name app.local;
location / {
proxy_pass http://demo_app;
# 最关键的四个头
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;
# 用 1.1 才能和后端保持长连接(配合 upstream 里的 keepalive)
proxy_http_version 1.1;
proxy_set_header Connection "";
# 超时三件套,默认 60s,按业务调
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
四个头各自解决什么问题:
Host $host:把客户端原始域名传给后端。后端靠它做虚拟主机、生成绝对 URL、校验ALLOWED_HOSTS。X-Real-IP $remote_addr:客户端真实 IP,应用记日志、做 IP 白名单时用。X-Forwarded-For $proxy_add_x_forwarded_for:在已有的 XFF 后面追加当前客户端 IP,形成链路。多级代理时用它追溯来源。X-Forwarded-Proto $scheme:告诉后端原始请求是 http 还是 https,否则后端生成的重定向链接会掉回 http。
坑:忘了带 Host,后端返回 404
这是反向代理最经典的翻车现场:proxy_pass 写了,但没写 proxy_set_header Host $host。Nginx 默认把 Host 设成 upstream 里的地址(也就是 127.0.0.1:8080),后端按域名匹配虚拟主机,匹配不上就返回 404 或者默认站点。
排查顺序:
# 1. 直接打后端,确认后端本身没问题
curl -H "Host: app.local" http://127.0.0.1:8080/
# 2. 打 Nginx,对比响应
curl -I -H "Host: app.local" http://127.0.0.1/
# 3. 看 error.log 里有没有 upstream 相关报错
sudo tail -f /var/log/nginx/error.log
# 4. 在配置里临时加一行,把转发的头打进响应,确认到底发了什么
add_header X-Debug-Host $host always;
其他几个新手常见坑,一并列在这:
- 后端的重定向地址是内网 IP:后端生成 302 时用了
127.0.0.1:8080。解法是让后端信任X-Forwarded-*(框架一般有开关),或者在 Nginx 用proxy_redirect改写。 - WebSocket 连不上:需要额外加
proxy_set_header Upgrade $http_upgrade;和proxy_set_header Connection "upgrade";,并且proxy_read_timeout要调大。 - 上传大文件报 413:默认
client_max_body_size 1m,在server或location里调大,第 6 篇细讲。 - reload 后配置没变:先
nginx -T看实际加载的配置里有没有你改的那段,八成是文件放错目录或者被别的server抢了server_name。
小结
- 安装三选一:apt 省心(生产首选)、源码编译为模块、Docker 为隔离;两个 apt 源不要混用。
- 目录约定记三处:
conf.d/*.conf放站点、/var/www放静态文件、/var/log/nginx放日志。 - 改配置的标准动作:
nginx -t校验 →nginx -s reload平滑生效;reload 失败时老配置仍在跑,别被假象骗了。 - 静态站点靠
root + try_files;反向代理靠proxy_pass加四个必备请求头,其中Host忘了带就是后端 404 的头号原因。
下一篇进入配置进阶:root 与 alias 的区别、SPA 回退、超时与缓冲、upstream 负载均衡策略和 HTTPS 全套。