simplify the life

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;
}
  1. 第一种是 = 类型,表示精确匹配,优先级最高,一旦匹配到忽略之后的正则匹配
  2. ^~ 类型,表示前缀匹配,是字符串开头匹配而非正则匹配,当匹配到该规则时,停止往下面的搜索,所以如果存在两个 ^~ 匹配的时候要注意有顺序之分。优先级比正则高。
  3. ~~*, 正则匹配,两者区别是后者不区分大小写。有顺序之分,匹配到第一个正则停止搜索。(一说如果有多个location的正则能匹配,选择最长的那个)
  4. /uri, 普通字符串匹配,无顺序之分,会选择匹配长度最长的配置。
  5. / 通用匹配,匹配所有请求

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不包含域名

posted on 2022-11-09 09:26  lessfish  阅读(66)  评论(0编辑  收藏  举报

导航