
文章的 TOC 太多了,看起来不方便?PC 端可以看看我的 《两个 TamperMonkey 脚本,解决掘金阅读体验的三点不爽》,可以解决一些掘金阅读体验问题。
我写的「入坑」系列文章:
- 《入坑 Mac,看这一篇就够了》
- 《入坑 iTerm + OMZ,看这一篇就够了》
- 《入坑 Firefox Developer Edition 及 Mobile 版,看这一篇就够了》
- 《入坑 WebStorm,看这一篇就够了》
- 《入坑 VSCode,看这一篇就够了》
- 《入坑 Vim,看这一篇就够了》
- 《入坑 Git,看这一篇就够了》
- 《入坑 Docusaurus,看这一篇就够了》
- 《入坑 Nginx,看这一篇就够了》← 本文
🎙️ 前言
你一定知道 Nginx,那么你「会用」么?说来惭愧,我不太会。这大概是由于之前的公司,此类事情一般都有专门的人负责。
然所谓「技多不压身」,无论你是前端后端,得对 Nginx 有基础的了解,不论是平常开发,还是希望把自己的项目快速落地,都可能用得着。
TL;DR
本文是前端向的 Nginx 入门文章。介绍如何安装、以及如何通过命令行启停、重载、配置检查等。通过不同的实战配置:静态托管、CDN 代理等,帮助开发者快速上手日常开发与部署必备的 Nginx 的基础知识和常用技巧。
主要内容

适合读者
- 有一定的命令行基础
- 想初步了解 Nginx 的同学
- 想知道 Nginx 怎么配置静态托管、CDN 代理等技巧的同学
你将学到
- Nginx 的正确读音
- Nginx 的常用命令(以及如何用
systemctl、brew命令操作 Nginx) - 常规的 Nginx 和使用
brew安装的有什么区别 - Nginx 常用代理模式,静态托管和 CDN 代理
编辑历史
| 日期 | 版本说明 |
|---|---|
| 2023/08/02 | V1 |
😴 认识 Nginx
读音
你或许需要重新认识下 Nginx,先从怎么发音开始吧。估计 10 个里面有 9.5 个人都会把 Nginx 的名字念错。你是不是一直这么读:「恩金克斯」?You say wrong...正确地读法是把「X」念全了:「恩金·埃克斯」。所以,其实应该这么写:NginX。
Nginx 官网 的第一句话就是教你如何正确地发音(然后才是它是什么):
nginx ("engine x") is an HTTP web server, reverse proxy, content cache, load balancer, TCP/UDP proxy server, and mail proxy server.
核心功能
- Web 服务器:托管网站、静态文件
- 反向代理:这是 Nginx 最核心的用途(也是你听到最多的关联词)
- 负载均衡:把流量均匀分给多台后端服务器
- 网关能力:HTTPS 证书、缓存、限流、URL 重写、Gzip 压缩、长连接优化等
核心优势
- 轻量,占用资源(内存 / CPU)极低
- 高并发,单机轻松扛 1 万+ 并发
- 稳定可靠
- 配置简单
- 扩展性强
新手必看
Nginx 的官网...简陋得不像这东西还活着一样,关键是很多对新手有用的链接都藏得很深,放这里以快速参考:
Nginx、Apache、Tomcat、Jetty 这几个兄弟的官网都差不多简陋,难道长得丑,活得久?
📦 安装
一般情况呢,你的 Linux 服务器上一定已经装好了 nginx。线上的机器,不论测试还是生产,都不可亵玩焉,作为练手使用,建议在自己电脑上装一个。
但估计很多人会在这一步就被劝退...官网的 安装文档,不能说并没太多新手友好的东西,只能说跟没写没啥区别。
用 Mac 和 Ubuntu 的同学相对比较简便:
- Mac - 推荐使用
brew安装brew install nginx - Linux - Nginx 对大多数耳熟能详的 Linux 发行版 都有支持
- Windows - 下载 后解压
- Docker - 我简单玩了一下,结论是「用来练手不合适」
Linux 中以对新手友好的 Ubuntu 为例,执行以下命令(不用 sudo 会报错):
shell
sudo apt update # 以避免 `Invalid operation install` 的错误
sudo apt install nginx
若你还是想亲手试试 Docker 的方式:
shell
docker pull nginx:latest
docker pull docker.1ms.run/nginx:latest # 上面这个可能因为神秘力量不能拉取,试试这个
以下,我将记录在 Mac 下通过 brew 安装和使用 Nginx。
Brew 安装
安装一如既往的简单,执行 brew install nginx,等一会儿就好了:

注意最后的警告(Caveats)部分:
- Docroot 是
/opt/homebrew/var/www,而不是默认的/var/www - 端口是 8080,而不是默认的 80
- 子配置目录是
servers,而不是默认的/etc/nginx/conf.d/*.conf
以上 2 和 3 可以在 /opt/homebrew/etc/nginx/nginx.conf 找到:
nginx
http {
...
server {
listen 8080;
...
}
...
include servers/*; # 建议 * 改成 *.conf
}
建议把 include servers/*;改成 include servers/*.conf;,这样,往里面丢 SSL 的 key 或 pem 文件就不会导致出错。
以下是安装后的 /opt/homebrew/etc/nginx 下的文件概览:

启停
你有两种方式启停 Nginx:
| 启动 | 停止 | |
|---|---|---|
| nginx | nginx |
nginx -s stop |
| brew | brew services start nginx |
brew services stop nginx |
无论你用以上哪种方式启动,访问 http://127.0.0.1:8080,你会看到一个很简陋的页面,说明启动成功:

上面这个 HTML 对应的文件在
/opt/homebrew/var/www/index.html。
需要注意的是,如果使用 nginx 启动,不能通过 brew 停止(因为 brew services 感知不到);而使用 brew 启动,还是可以通过 nginx 进行停止。但!建议不要混着来,否则就会想以下这样,碰到「Bootstrap failed: 5」的启动错误(当然,解决的办法也简单,就是执行 brew services reload,但我曾经因此困惑而重装过...)。

因此建议只用 brew 启停,就像在服务器上只用 systemctl 启停一样。
路径差异
brew 安装,跟在服务器上(或者在 Ubuntu 是安装)的 Nginx,除了之前提到的配置目录外,还有一些别的路径区别,一般都是在默认的前面拼上 /op/homebrew,以下是常用的路径:
| 路径 | 常规 | brew |
|---|---|---|
| Bin 文件 | /usr/sbin/nginx |
/opt/homebrew/bin/nginx |
| 安装目录 | /etc/nginx |
/opt/homebrew/Cellar/nginx/{version} |
| 配置文件 | /etc/nginx/nginx.conf |
/opt/homebrew/etc/nginx/nginx.conf |
| 配置目录 | /etc/nginx/conf.d/ |
/opt/homebrew/etc/nginx/servers/ |
| 日志目录 | /var/log/nginx |
/opt/homebrew/var/log/nginx |
| 静态文件 | /var/www |
/opt/homebrew/var/www |
常用命令对比
之前我们已经知道,可以通过 brew services 启停 Nginx,而在服务器上你会被告知最好使用 systemctl 进行操作。下表整理了跟 Nginx 有关的常用命令:
| 操作 | 原生命令(跨系统) | brew | systemctl |
|---|---|---|---|
| 启动 | nginx |
brew services start nginx |
systemctl start nginx |
| 停止 | nginx -s stop / nginx -s quit |
brew services stop nginx |
systemctl stop nginx |
| 重启 | nginx -s stop && nginx |
brew services restart nginx |
systemctl restart nginx |
| 重载配置 | nginx -s reload |
- | systemctl reload nginx |
| 检查配置 | nginx -t / nginx -T |
- | - |
| 查看状态 | 无(需用 `ps aux | grep nginx` 查看进程) | brew services info nginx |
| 一些说明: |
- 停止:
stopVSquit:nginx -s stop是「暴力停止」,立刻终止所有进程,会中断业务;nginx -s quit是「优雅停止」,等所有请求处理完再退出,无业务中断;生产环境请用quit - 重启:使用
brew/systemctl比nginx -s stop && nginx多了服务状态校验 - 检查配置
-tVS-T:-t只讲对错,输出简洁,-T还会显示详细的配置内容;一般情况下用-t即可 - 虽然
brew services文档中并没有reload,但你可以执行brew services reload nginx,其效果其实跟restart是一样的
其他 brew 命令
除了以上常用命令中提到的,使用 brew,跟 Nginx 相关的,还有这些命令:
brew services list查看由brew管理的所有服务,可以看nginx是否在运行brew info nginx查看nginx版本等基础信息brew upgrade nginx升级brew outdated nginx是否需要升级brew uninstall nginx卸载brew reinstall nginx重装
🚀 配置实战
我们跳过枯燥晦涩的配置理论知识,直接进入实战。以下我会以在实际服务器上为前提进行操作,而非本地的 brew nginx。
通常情况下,我们很少会去关注或修改 nginx.conf(最主要的配置文件),只需要往子配置目录 conf.d(或 brew 的 servers) 里丢 域名.conf 就行了。
接下来,假设现在我有个前端项目,它可能是一个 Vite 项目,也可能是一个 SSG 项目。拿之前我讲过的 SSG 框架 Docusaurus 的本地项目 documentation 作例子。
我现在将逐步将其部署到某个域名。
关于域名
首先,我们需要一个域名。域名能否被 Nginx 接住,主要看域名解析 DNS 是否能命中 Nginx 所在的机器。
如果你已经有了一个主域名,最简单的就是给它添加一个二级域名,比如 doc.company.com。在阿里云「云解析 DNS」控制台,就可以快速添加一个子域名(不花钱),只需要让子域名指向 Nginx 服务器所在的 IP 即可:

现在我们只是本地部署看看效果,只需要增加一条 Host 记录,比如:
host
127.0.0.1 doc.test
静态托管
静态托管的方式,需要把项目构建产物上传到服务器对应的目录 /var/www/doc,然后添加 conf.d/doc.<company-domain>.conf。
本地试验,新增 /opt/homebrew/etc/nginx/servers/doc.test.conf:
nginx
server {
listen 80;
server_name doc.test;
root /....../documentation/build; # 这里直接指导项目的构建目录,省去拷贝
# 处理根路径请求
location / {
try_files $uri $uri/ /index.html; # 支持单页面应用路由
}
# 静态资源缓存配置(可选但推荐)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|txt|map|json)$ {
expires 30d;
add_header Cache-Control "public";
access_log off;
}
# 错误页面配置
error_page 404 /404.html;
location = /404.html {
internal;
}
}
如果是以这种方式直接应用于现实中的服务器的话,需要注意 root 并不是相对于 /var/www 的相对路径,需要写绝对路径,Nginx 并没有此类约定。你或许看到 nginx.conf 有 root html 而产生此类想法,但这是 Nginx 内置的约定,html 目录固定在配置前缀下(如 /usr/share/nginx/html),属于标准化的相对路径用法。
另外就是,这种部署方式会很麻烦------你得会往服务器上传目录的资格和技巧。幸运的是,你可以利用类似 Transmit 的 SFTP 工具进行操作,这里推荐你使用 rsync,一个原生的 Linux 命令,Mac 下也支持。
shell
rsync -rvz --delete 本地目录/ 用户@服务器:/var/www/doc/
-r保留目录结构-v详细输出-z传输时压缩,加快上传速度--deletersync默认增量更新,虽然不成问题,但会生产垃圾文件,此参数可以先删除再上传本地目录/末尾带/表示同步目录内的所有内容,而非目录本身/var/www/doc/末尾带/确保内容同步到目标目录下,不存在时自动创建
CDN 代理
以上说到直接去服务器上部署文件,虽然简单粗暴,但却相当麻烦,还将占用服务器的存储资源。
假设现在,我已经把前端资源(包括一个 index.html)发布到了 CDN https://somecdn.com/documentation/1.0.0/,可以把 Nginx 的配置也改成 CDN 代理的方式:
nginx
server {
listen 80;
server_name doc.test;
location / {
proxy_ssl_server_name on;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_pass https://somecdn.com/documentation/1.0.0/; # 注意必须以 / 结尾,否则 404
proxy_set_header Host somecdn.com;
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_set_header Accept-Encoding "";
proxy_redirect off;
proxy_intercept_errors on;
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
gzip off; # gzip,避免重复压缩
error_page 404 = /index.html;
# if ($request_uri = /) {
# rewrite ^ /index.html;
# }
}
# 静态资源缓存配置(可选)
# location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|txt|map|json)$ {
# expires 30d;
# add_header Cache-Control "public";
# access_log off;
# }
}
这样,每次发布 CDN,只要版本号不变(比如测试环境)就没有必要做部署;而如果进行了生产发布,则只需要更新生产机器上的版本号即可。
通过查看 index.html 请求的响应头,可以看到两个方案的差别:

另外,静态托管和 CDN 代理还有一个显著的区别,静态托管的核心配置是 root,有没有 / 结尾都可以,但 CDN 代理的核心 proxy_pass 却必须以 / 结束,否则将 404。
HTTPS
接下来,经验丰富的你会想到 HTTPS 的问题。
有些时候,本地开发、联调、小程序 / 本地H5调试时,可能会被强制要求 HTTPS,如小程序请求、Safari 跨域、PWA、安全接口校验等。本地 Nginx 默认仅支持 HTTP,需要手动生成本地自签名 SSL 证书并配置 Nginx,实现本地可信 HTTPS 访问。
本地 SSL 证书
以下,我们将在 brew Nginx 配置目录下新建一个 ssl 目录专门存放 SSL 文件:
bash
# 进入 nginx 目录(根据自己环境修改)
cd /opt/homebrew/etc/nginx
mkdir ssl && cd ssl
# 生成 RSA 私钥 + 自签名证书(有效期10年)
openssl req -x509 -newkey rsa:4096 -nodes -keyout local.key -out local.crt -days 3650
它会问几个问题,随便回答就行:

这样就在 ssl 目录下生出了两个文件,公钥 local.crt 和私钥 local.key。
配置 SSL
然后,我们基于之前的配置,添加 SSL 的相关配置,同时让 80 端口 301(永久重定向)到 HTTPS:
nginx
server {
listen 80;
server_name doc.test;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name doc.test;
# 核心 ssl 证书配置
ssl_certificate /opt/homebrew/etc/nginx/ssl/local.crt;
ssl_certificate_key /opt/homebrew/etc/nginx/ssl/local.key;
# 基础 ssl 优化配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
proxy_ssl_server_name on;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_pass https://somecdn.com/documentation/1.0.0/;
proxy_set_header Host somecdn.com;
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_set_header Accept-Encoding "";
proxy_redirect off;
proxy_intercept_errors on;
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
gzip off; # gzip,避免重复压缩
error_page 404 = /index.html;
# if ($request_uri = /) {
# rewrite ^ /index.html;
# }
}
# 静态资源缓存配置(可选)
# location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|txt|map|json)$ {
# expires 30d;
# add_header Cache-Control "public";
# access_log off;
# }
}
绕过不安全提示
重启 Nginx 后,你会发现,浏览器并不认可此证书:

这是正常的,毕竟这个证书血统不正宗,但同时也恭喜你,配置 SSL 成功了,可以通过人工干预的方式绕过:
- Firefox / Chrome:高级 → 继续访问(不安全)
- Safari:信任证书
- 小程序开发工具:勾选 不校验合法域名、HTTPS、TLS
以下是配置了 SSL 后的访问效果:

关于生产环境
本地 SSL 签名只能我们开发者自己玩玩,生产环境下就必须使用 CA 机构签发的正式可信 SSL 证书。
而且,证书是有时效的,阿里云 / 腾讯云默认免费证书一般都是时效 90 天的单域名证书(一个证书只能用于特定域名);更方便的长时效的范域名证书(推荐一个证书用于多个子域名),就得舍得花点银子了。
日志
查看日志是必备的技能,记住 tail 这一个命令即可:
| 默认 | brew 安装 | |
|---|---|---|
| 访问日志 | tail -100f /var/log/nginx/access.log |
tail -100f /opt/homebrew/var/log/nginx/access.log |
| 错误日志 | tail -100f /var/log/nginx/error.log |
tail -100f /opt/homebrew/var/log/nginx/access.log |
你已经知道 Nginx 可以带多个 Server,会思考的你也一定会想到上面的 access.log 和 error.log 如果混杂了所有 Server 的日志将会带来不必要的麻烦。你可以在对应的服务下配置日志路径到专属的文件,比如你有个 verynb.com 的 Server:
nginx
server {
listen 80;
server_name verynb.com;
access_log /var/log/nginx/verynb.com.access.log;
error_log /var/log/nginx/verynb.com.error.log;
...
}
📕 配置理论知识
变量
Nginx 内置了很多 变量,也就是你在配置文件中看到的 $xx。以下是一些核心的变量(AI 整理):
| 变量名 | 含义 | 典型使用场景 |
|---|---|---|
$remote_addr |
客户端的 IP 地址(公网 / 内网) | 日志记录、限流、防盗链 |
$remote_port |
客户端与 Nginx 建立连接的端口号 | 日志排查、连接数统计 |
$remote_user |
客户端通过 HTTP 认证的用户名 | 权限控制、日志审计 |
$http_user_agent |
客户端的 User-Agent(浏览器 / 设备信息) | 适配不同设备、反爬虫 |
$http_referer |
客户端请求的来源页面(Referer 头) | 防盗链、统计来源 |
$http_cookie |
客户端携带的 Cookie 信息 | 鉴权、个性化配置 |
$request_method |
HTTP 请求方法 | 限制请求方法、日志 |
$request_uri |
客户端请求的完整 URI(含参数) | 日志记录、重定向 |
$request_filename |
对应请求的本地文件路径(静态资源场景) | 静态资源缓存、文件访问控制 |
$uri |
请求的 URI(不含参数,已解码) | 反向代理、路径匹配 |
$args |
请求的 URL 参数 | 参数透传、日志 |
$host |
请求的主机名(优先 Host 头,无则用服务器域名) | 多域名配置、反向代理 |
$server_name |
Nginx 配置中当前 server 的 server_name(固定值) |
多域名区分、日志 |
$server_port |
Nginx 接收请求的端口号 | 端口适配、日志 |
$scheme |
请求的协议 http/https |
强制 HTTPS 跳转、日志 |
$status |
Nginx 返回给客户端的 HTTP 状态码 | 日志统计、错误监控 |
$bytes_sent |
Nginx 发送给客户端的字节数 | 流量统计、限流 |
$request_time |
请求的总处理时间(秒,含小数) | 性能监控、慢请求排查 |
$proxy_add_x_forwarded_for |
拼接客户端真实 IP(X-Forwarded-For 头),传递给后端 | 后端获取客户端真实 IP |
$upstream_addr |
反向代理转发的后端服务器地址(IP: 端口) | 后端集群排查、日志 |
$upstream_status |
后端服务器返回的 HTTP 状态码 | 后端错误监控 |
$upstream_response_time |
后端服务器的响应时间(反向代理场景) | 后端性能监控 |
Nginx 核心配置项
Nginx 的默认核心配置文件 nginx.conf,可分成 6 个模块:
- 全局段:全局配置,对全局生效
- events:配置影响 Nginx 服务器与用户的网络连接
- http:最常用的配置,配置代理、缓存、日志等功能和第三方模块的配置
- server:配置虚拟主机的相关参数,一个 http 块中可以有多个 server 块
- location:用于配置匹配的 URI
- upstream:配置后端服务器具体地址,负载均衡配置
🙋 FAQ
❓ 如何查看 Nginx 正在代理的所有域名?
这是一个算常见的需求,我就想看看这台机器承接了哪些业务。但可恨的是,Nginx 并不提供类似的便利。幸运的是,有曲线救国的办法:
shell
nginx -T 2>/dev/null | grep -E "\sserver_name\s+" | grep -v "#" | awk '{print $2}' | tr ';' ' ' | tr ',' ' '
以上命令,有几点需要澄清:
- 不要问我诸如 如
2>/dev/null的细节 - 其实它代表不了「正在」二字,因为它只是从
nginx -T的输出中利用正则提取所有server_name后面的字段,可能包括注释掉的部分,也可能 Nginx 甚至都没有起来
❓ 访问到了真实的 CDN 文件怎么回事?
理论上,使用 CDN 代理,是不会看到 CDN 的地址的,尤其不可能在浏览器地址栏看到。但偏偏碰到了,是怎么回事?
原因其实也很简单,就是你很可能把 CDN 的 https 误写成了 http,导致触发了 CDN 的 302 跳转,导致页面地址被强行 Forward 到了 https。