跳到主要内容

安装与快速上手

环境口径: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 全套。