Nginx 命令备忘
- 安装:
brew install nginx
- 启动:
nginx
(默认端口 8080,启动后查看 http://localhost:8080/)(所以启动了 nginx,8080 的端口就被占用了) - 停止:
nginx -s stop
- 重启:
nginx -s reload
- 查看配置是否正确:
nginx -t
如果用 brew 下载的 nginx,brew 也提供了一套类似的命令,比如 brew services restart nginx
配置文件:
- 配置入口文件:
/usr/local/etc/nginx/nginx.conf
- 我的配置:
/usr/local/etc/nginx/servers/
(会在入口文件中引入)
2023-09-28 brew 安装的 nginx 的配置文件并不一定在这个位置,参考 这里,比如目前我新安装完后在 /opt/homebrew/etc/nginx
下
入门配置
首先在 /Users/fish/demo/test/
目录下新建个 index.html 文件,我们希望用 http://www.test.com 来打开它
第一步,我们在 hosts 中新增规则,将 www.test.com 指向 127.0.0.1:
127.0.0.1 www.test.com
第二步,我们在 /usr/local/etc/nginx/servers/
下新增 test.conf
文件(名字可以随意,修改下 listen 的端口为 80):
server {
listen 80;
server_name www.test.com;
location / {
root /Users/fish/demo/test/;
index index.html index.htm;
}
}
try_files 配置
一般用于 SPA 的打包结果起的 server,需要配置 try_files。更多可参考 Vue 项目部署
server {
listen 30000;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /Users/bytedance/fish/dustbin/a6af6beb-6821-495c-8e0a-8b933da60e73/mf-simple-demo-with-arco/host/build;
try_files $uri $uri/ /index.html;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
Nginx 反向代理
简单配置
假设有一个 http://localhost:8000 的服务,我们想用 www.test.com 访问它
第一步,我们在 hosts 中新增规则,将 www.test.com 指向 127.0.0.1:
127.0.0.1 www.test.com
第二步,我们在 /usr/local/etc/nginx/servers/
下新增 test.conf
文件(文件名可以随意,proxy_pass 也可以设置为线上的地址,表示一个实际可访问的地址):
server {
listen 80;
server_name www.test.com;
location /{
proxy_pass http://127.0.0.1:8000; # 核心配置
proxy_set_header Host $host;
}
}
第三步,重启 nginx:
sudo nginx -s reload
一个域名配置多个反向代理
以我的项目 anymusic/api 为例,用 npm run prd
启动项目,比如这时候可以访问如下链接:
http://localhost:3333/api/qq/search?keywords=林俊杰&page=1
我们先做个简单的反代:
server {
listen 80;
server_name api.anymusic.com;
location /{
proxy_pass http://127.0.0.1:3333;
proxy_set_header Host $host;
}
}
我们假设这个项目都是以 /api
的路径开头,而另一个项目假设都是以 /test
开头,我们便可以在一个域名下配置两个项目:
server {
listen 80;
server_name api.anymusic.com;
location /api { # 核心配置
proxy_pass http://127.0.0.1:3333;
proxy_set_header Host $host;
}
location /test { # 核心配置
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
}
}
这样以 /api
开头的请求会被代理到 127.0.0.1:3333,而以 /test
开头的请求会被代理到 127.0.0.1:8000
还有另一种方法,我们还可以统一在请求地址上加前缀,用这个前缀来做区分(注意修改了两处):
server {
listen 80;
server_name api.anymusic.com;
location /path/ {
proxy_pass http://127.0.0.1:3333/; # 注意最后的 /
proxy_set_header Host $host;
}
location /test/ {
proxy_pass http://127.0.0.1:8000/;
proxy_set_header Host $host;
}
}
这个时候我们要访问之前的链接,需要这样:
http://api.anymusic.com/path/api/qq/search?keywords=林俊杰&page=1
这是两种方案,第一种方案可以理解成我们针对目前的请求地址已经可以做区分了;第二种是我们针对目前的请求地址不能做区分,或者为了更明显的区分,给不同的服务再加个统一前缀,是会改变请求地址的(相当于添加的 path 其实是不在项目路由中的,是 Nginx 帮忙加上去的)
理解反向代理
以下内容摘自 https://segmentfault.com/q/1010000003491873/a-1020000003492000
A 找 B 直接沟通,这就等于没有什么代理;
然而中间夹一个传话的 C,C 就是代理了,A 通过 C 把信息传递给 B,然后 C 再把 B 的反馈转达给 A。
在这个过程中,A 知道沟通的直接目标是 B,只不过由于各种原因无法直接和 B 面对面,需要中间人 C,这就是所谓“正向代理”,其实我们很少说正向代理,在英文原文里,这个叫 Forward Proxy,一般就直接叫代理,你翻译成“转交代理”或“传达代理”都比“正向代理”强,然而没必要,因为代理这个词的本意就是如此。
另外一种情况则是:A 并不知道 B 的存在,它只知道找 C 就可以得到想要的回复,对于 A 来说有没有 B 或者有多少个 B、D、E、F……都不重要,只要有 C 就够了。而 C 则根据情况去获取反馈然后响应给 A。
这种就叫反向代理了。理解其中的区别不要从“正反”两个反义方向词上做文章,英文里的 Forward 和 Reverse 并不是一对反义词,Forward 和 Backward 才是,然而 Reverse 和 Backward 并不是一个意思……所以说技术书籍资料还就是得看原文的啊。
Forward Proxy(代理)
我想访问 www.google.com,然而大家都知道它被墙了,我没法直接访问它。于是我连接了一个 VPN 服务并设定其为本地 HTTP 访问的代理(比如说在 Mac 下勾选 通过 VPN 连接发送所有流量),然后我再访问 www.google.com,此时我的请求被该 VPN 服务代理了,它帮我访问了 www.google.com 然后把结果返回给我。
- 这个例子是代理的一种应用场景,但并非代表代理只能用于这个
- 最重要的特征是我知道 www.google.com 的存在,而且我访问的网址也的确是 www.google.com,只不过我的访问请求经由了 VPN 代理来转交,同样响应也是如此
- 在本例中,代理是透明的,用户有可能不知道它的存在(通常是知道的,只不过代理的设置可能不是他自己来做)
Reverse Proxy(反向代理)
我有一个 Nginx 服务部署在 www.mysite.com 的 80 端口,用户访问它就可以看见我做的网站;在我的网站中有一些 Ajax 请求去获取 JSON 数据,然而提供这些数据的 API Service 部署在服务器上的 8000 端口,该端口由于防火墙的阻挠使得用户无法直接访问到。
于是我重新配置了 Nginx,让它把所有经由 :80/api/ 的访问请求都代理给 localhost:8000,然后把响应返回给原始的请求方(即:Origin Host),这就是反向代理。现在我的用户可以正常访问 www.mysite.com 啦。
- 同上,这是反向代理的一种应用场景,但并非代表它只能这样用
- 最重要的特征是我的用户压根不知道 localhost:8000 这个服务的存在,并且即使知道也访问不到——开 VPN 也访问不到,这是俩码事!
- 对于用户来讲,唯一的“对话”方只有 www.mysite.com(80 端口),他们不知道也不必知道后面发生了什么
Nginx 做负载均衡
举个简单例子:(假设 http://www.test.com 后有多个可访问服务,本质是随机进行 proxy_pass)
负载均衡配置
# 两个简单的静态服务配置,进行模拟
server {
listen 8080;
server_name localhost;
location / {
root /Users/fish/demo/test/server0;
index index.html index.htm;
}
}
server {
listen 8081;
server_name localhost;
location / {
root /Users/fish/demo/test/server1;
index index.html index.htm;
}
}
upstream myServer {
server localhost:8080;
server localhost:8081;
}
server {
listen 80;
server_name www.test.com;
location / {
proxy_pass http://myServer;
proxy_set_header Host $host;
}
}
本地起了两个静态服务,我们可以分别用 http://localhost:8080 以及 http://localhost:8081 访问。如上配置后,访问 http://www.test.com,我们可以依次返回两个页面中的一个(默认是轮循)
我们可以修改权重,权重越大,被访问到的机会越大:
upstream myServer {
server localhost:8080 weight=1;
server localhost:8081 weight=2;
}
我们也可以用 nginx 内置的 ip_hash 选项:
upstream myServer {
server localhost:8080;
server localhost:8081;
ip_hash;
}
每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个服务器,可以解决 session 的问题,但有时会导致分配不均即无法保证负载均衡
Nginx 访问权限设置
我们可以在 location
中进行访问权限的配置:
server {
listen 80;
server_name www.docs.com;
location / {
root /Users/fish/docs/;
index index.html index.htm;
allow 45.76.202.231;
deny all;
}
}
上面的配置表示只允许 ip 45.76.202.231
进行访问,其他的 ip 是禁止访问的
如果我们把 deny all
指令移到 allow 45.76.202.231
之前,会发现所有的 ip 都不允许访问了。这说明配置是按顺序执行的
这里需要特别注意,如果你想在本地测试,记住本地的 ip 是
127.0.0.1
,而不是外网的 ip
在工作中,访问权限的控制需求更加复杂,例如,对于网站下的 img(图片目录)是允许所有用户访问,但对于网站下的 admin 目录则只允许公司内部固定 IP 访问,我们可以用 location 块来完成相关的需求匹配
location =/img { # `=` 表示精确匹配
allow all;
}
location =/admin {
deny all;
}
禁止访问所有 php 文件:
location ~\.php$ {
deny all;
}
Nginx 错误处理
假设原始配置如下:
server {
listen 80;
server_name www.docs.com;
location / {
root /Users/fish/docs/;
index index.html index.htm;
}
}
404
server {
listen 80;
server_name www.docs.com;
location / {
root /Users/fish/docs/;
index index.html index.htm;
}
error_page 404 /404.html;
}
需要在根目录(即 /Users/fish/docs/
)新增 404.html 文件
也可以将 404 指向外部资源地址:
server {
listen 80;
server_name www.docs.com;
location / {
root /Users/fish/docs/;
index index.html index.htm;
}
error_page 404 https://segmentfault.com/;
}
这样我们访问 http://www.docs.com/xxx 就会跳转到 https://segmentfault.com/
50x
50x 错误设置也是类似:
error_page 500 502 503 504 /50x.html;
Nginx 适配 pc 和移动设备
server {
listen 80;
server_name www.test.com;
location / {
root /Users/fish/demo/test/pc;
if ($http_user_agent ~* '(Android|webOS|iPhone|iPod|BlackBerry)') {
root /Users/fish/demo/test/mobile;
}
index index.html index.htm;
}
}
当移动端打开时(根据 UA 判断),使用的是 /Users/fish/demo/test/mobile
目录
直接在服务端进行重定向处理,这样的好处是移动端和 pc 端打开后是同一个链接
Nginx 开启 gzip
Gzip 是网页的一种网页压缩技术,经过 gzip 压缩后,页面大小可以变为原来的 30% 甚至更小。更小的网页会让用户浏览的体验更好,速度更快。gzip 网页压缩的实现需要浏览器和服务器的支持
Nginx 提供了专门的 gzip 模块,并且模块中的指令非常丰富。
- gzip : 该指令用于开启或 关闭 gzip 模块。
- gzip_buffers : 设置系统获取几个单位的缓存用于存储 gzip 的压缩结果数据流。
- gzip_comp_level : gzip 压缩比,压缩级别是 1-9,1 的压缩级别最低,9 的压缩级别最高。压缩级别越高压缩率越大,压缩时间越长。
- gzip_disable : 可以通过该指令对一些特定的 User-Agent 不使用压缩功能。
- gzip_min_length: 设置允许压缩的页面最小字节数,页面字节数从相应消息头的 Content-length 中进行获取。
- gzip_http_version:识别 HTTP 协议版本,其值可以是 1.1. 或 1.0.
- gzip_proxied : 用于设置启用或禁用从代理服务器上收到相应内容 gzip 压缩。
- gzip_vary : 用于在响应消息头中添加 Vary:Accept-Encoding, 使代理服务器根据请求头中的 Accept-Encoding 识别是否启用 gzip 压缩。
gzip 最简单的配置:
server {
listen 80;
server_name www.docs.com;
location / {
root /Users/fish/docs/;
index index.html index.htm;
}
gzip on;
gzip_types text/plain application/javascript text/css;
}
gzip on
是启用 gizp 模块,下面的一行是用于在客户端访问网页时,对文本、JavaScript 和 CSS 文件进行压缩输出。
开启后重启 nginx 服务,可以看到 http 返回头中有个 Content-Encoding: gzip
Nginx 常用配置项
location
# 匹配所有根目录
location /
# 字符串匹配, 表示匹配所有“/static”开头的目录
location /static
# ~ 匹配符合表达式目录比如代理目录中存在“static/(js|images)”的目录
location ~ /static/(js|images)/
# ~* 加上 * 表示不区分大小写
location ~* /static/(js|images)/
# = 表示精确匹配, 只有"/index"路径才会被代理,"/index/test"将不会被代理
location = /index
# 依次按照顺序检查文件是否存在(spa 项目的配置)
location / {
try_files $uri $uri/ /index.html;
}
- 第一种是
=
类型,表示精确匹配,优先级最高,一旦匹配到忽略之后的正则匹配 ^~
类型,表示前缀匹配,是字符串开头匹配而非正则匹配,当匹配到该规则时,停止往下面的搜索,所以如果存在两个^~
匹配的时候要注意有顺序之分。优先级比正则高。~
和~*
, 正则匹配,两者区别是后者不区分大小写。有顺序之分,匹配到第一个正则停止搜索。(一说如果有多个location的正则能匹配,选择最长的那个)/uri
, 普通字符串匹配,无顺序之分,会选择匹配长度最长的配置。/
通用匹配,匹配所有请求
location 正则匹配符号:
~ 和 ~* :(区分大小写匹配)和(不区分大小写匹配)
!~ 和 !~* :(区分大小写不匹配)和(不区分大小写不匹配)
^~ :如果该选项匹配,则只匹配该选项,不匹配其他选项
= :精确字符匹配
@ :使用内部定向,如: error_page,try_files
-f 和 !-f :用来判断是否存在文件
-d 和 !-d :用来判断是否存在目录
-e 和 !-e :用来判断是否存在文件或目录
-x 和 !-x :用来判断文件是否可执行
. : 匹配除换行符以外的任意字符
? : 重复0次或1次
+ : 重复1次或更多次
* : 重复0次或更多次
\d : 匹配数字
^ : 匹配字符串的开始
$ : 匹配字符串的介绍
{n} : 重复n次
{n,} : 重复n次或更多次
[c] : 匹配单个字符c
[a-z] : 匹配a-z小写字母的任意一个
proxy_set_header
# 设置代理请求服务器请求头host
proxy_set_header Host $host;
# 设置代理请求的ip地址
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 设置代理请求自定义数据
proxy_set_header header value;
rewrite
rewrite regex replacement [flag];
rewrite 的原始地址应该是 host 后的地址,以 /
开头,比如 http://www.test.com/demo0/about 为 /demo0/about
flag参数说明:
- last 本条规则匹配完成后,继续向下匹配新的规则
- break 本条规则匹配完即终止
- redirect 返回 302 临时重定向
- permanent 返回 301 永久重定向
last 和 break 用来实现 URL 重写,浏览器地址栏不变。redirect 和 permanet 用来实现 URL 跳转
常用全局变量
$args : #这个变量等于请求行中的参数,同$query_string
$content_length : 请求头中的Content-length字段
$content_type : 请求头中的Content-Type字段
$document_root : 当前请求在root指令中指定的值
$document_uri : 与$uri相同
$host : 请求主机头字段,否则为服务器名称
$http_user_agent : 客户端agent信息
$http_cookie : 客户端cookie信息
$limit_rate : 这个变量可以限制连接速率
$request_method : 客户端请求的动作,通常为GET或POST
$request_uri : 包含请求参数的原始URI,不包含域名
$remote_addr : 客户端的IP地址
$remote_port : 客户端的端口
$remote_user : 已经经过Auth Basic Module验证的用户名
$request_filename : 当前请求的文件路径,由root或alias指令与URI请求生成
$scheme : HTTP方法(如http,https)
$server_protocol : 请求使用的协议,通常是HTTP/1.0或HTTP/1.1
$server_addr : 服务器地址,在完成一次系统调用后可以确定这个值
$server_name : 服务器名称
$server_port : 请求到达服务器的端口号
$uri : 不带请求参数的当前URI,$uri不包含域名