nginx 常用配置详解(下)

简介: nginx 常用配置详解(下)

给图片等静态资源加强缓存

location ~* \.(?:css(\.map)?|js(\.map)?|gif|svg|jfif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {
    # 静态资源设置一年强缓存 
    expires 365d; 
    access_log off; 
}
复制代码

一般会给静态资源设置一个较长的缓存。如果资源发生变化,会修改文件名。一般用 md5 值做为文件名。

root 与 alias

root 与 alias 决定到哪里在去访问实际的文件,区别在于 root 会拼接匹配到路径,alias 会替换匹配到的路径。我们看个实际的例子。

location / {
 root /home/user/web; 
}
请求 /a.jpg,实际请求的是 /home/user/web/a.jpg,找到图片
把 root 换成 alias
location / {
 alias /home/user/web; 
}
请求 /a.jpg,实际请求的是 /home/user/weba.jpg,找到不图片了。因为 / 被替换掉了。解决的方法是把 / 补上
location / {
 alias /home/user/web/; 
}
也不是说 alias 必须以 / 结尾,如果location中 不是以 /结尾,alias 后面就不加 /
在下面的例子中 ,请求 /a/b.jpg ,实际查找 /home/user/web/b.jpg。 只是把 /a 去掉而已。
location /a {
 alias /home/user/web; 
}
复制代码

相比于 alias,建议优先用 root。root 可以用在 http, server, location, if in location,alias 只能用在 location。root 对末尾的斜杠并不在意,兼容性更好。 alias 可以作为 root 的补充来使用。

url 美化

我们在开发的时候用的 url 是这样的

/users?id=1
复制代码

让别人访问的时候可能是这样的

/users/1
复制代码

所以我们需要把 /users/1 转为 /users?id=1 ,这时就需要 rewrite 出场了。

location /users/{
    rewrite ^/users/(.*)$ /nodejs/id=$1? last;
}
location /nodejs/{
   proxy_pass http://127.0.0.1:3000;
}
复制代码

当请求/users/1 的时候,命中 location /users/ ,执行rewrite 指令, last flag 指示停止后面的rewrite 指令并做内部跳转,匹配到 location /nodejs/ ,经过 proxy_pass 指令,转到 /nodejs/ 。

你可能对 last 表示疑惑,都已经是最后了,怎么又跳到 /nodejs/ 了呢? 接下来,我详细讲解一下 nginx 的 rewrite 模块。为什么说一个模块呢?因为与 rewrite 相关的是一组指令。

nginx http rewrite module 详解

简单来说, ngx_http_rewrite_module module 用正则匹配请求,改写请求,然后做跳转。可以是内部跳转,也可以是外部跳转。

学习这个模块的时候,把 rewrite_log 打开,可以在 error log 里查看跳转信息

rewrite_log on; 
 error_log /home/log/test-error.log notice;
复制代码

注意 notice 是必须的

顺序执行和循环跳转

  1. 直接写在 server level 的 指令,顺序执行。
  2. 写在 location 中的指定顺序执行。可以跳到其它 location ,最多不超过 10次。
server{
    rewrite ^/users/(.*)$ /show?user=$1 ;
    rewrite ^/teachers/(.*)$ /show?teacher=$1 ;
}
复制代码

请求 /users/1 ,先执行第一条 rewrite ^/users/(.*)$ /show?user=$1 再执行第二条 rewrite ^/teachers/(.*)$ /show?teacher=$1 ; 虽然第一条匹配到了,还是会执行第二条。这就是顺序执行的意思。

location /{
    rewrite ^/teachers/(.*)$ /show/$1;
    rewrite ^/users/(.*) /show/$1;
}
location /show/{
    rewrite ^/show/(.*)$ /users/$1 ;
}
复制代码

请求 /users/1,命中第一个location 。顺序执行第一个 rewrite,没命中,即使命中也会继续执行第二 rewrite ,命中。执行 rewirte 指令跳转到第二个location /show/,执行 rewirte 又回跳回 / ,这样循环10次,报 500 错误,查看 error 日志可以看到说明。

rewrite or internal redirection cycle while processing "/show/1"
复制代码

这个过程演示了 location 中 rewrite 的执行逻辑。顺序执行,循环跳转。

rewrite module 中还有 5 个指令 break, if, return, rewrite, and set

return

return 可以直接返回,打断后面的 rewrite module 指令的执行。

location / {
 return 409;
 rewrite ^/teachers/(.*)$ /show/$1;
}
复制代码

执行 return 后,后面的指令就没有机会执行了。

return code [text];
return code URL;
return URL;
复制代码
location /admin/{
    return 403 '没有访问权限';
}
location / {
    return 302 $scheme://www.baidu.com$request_uri;
}
location /abc/{
    return 404;
}
复制代码

set,break 比较简单,和其它语言差不多。下面着重讲下 rewirte 指令的 flag。

rewrite 指令的 flag

rewriteregexreplacement [flag]

flag有四种

  • last 停止执行后面的 ngx_http_rewrite_module 指令,并发起新的 location 匹配。
  • break 停止执行后面的 ngx_http_rewrite_module 指令,然后没有后续了,不再发起 location 匹配。
  • redirect 执行 302跳转,后面的指令不再执行。
  • permanent 执行 301跳转,后面的指令不再执行。

last、break 停止执行的是 ngx_http_rewrite_module 指令,其它指令不受影响,还是会执行的。

regex 匹配的是路径部分

location / {
   rewrite ^/teacher/(.*)$ /show1/$1 last;
   rewrite ^/teacher/(.*) /show2/$1;
}
location /show1{
 return 900;
}
location /show2{
 return 901;
}
curl http://localhost:3000/teacher/1
HTTP/1.1 900
因为 last 会终止后面的  ngx_http_rewrite_module 指令,所以 第二句 rewrite ^/teacher/(.*) /show2/$1 不会执行。第一句执行完后,跳到 /show1,返回 900
如果把 last 换成  break
HTTP/1.1 404
因为 break 不再执行跳转,直接查找 show1/1 找不到,报 404.
把 last 换成 redirect.
HTTP/1.1 302 
浏览器会请求两次。
把 last 换成 permanent.
HTTP/1.1 301 
浏览器会请求两次。
复制代码

如果 replacement 是 http开头,是可以直接跳转的

location / {
  rewrite ^/teacher/ http://juejin.cn
}
curl http://localhost:3000/teacher/1
HTTP/1.1 302 
Location: http://juejin.cn
相当于 redirect 指令的效果。
复制代码

if 语句

if 语句不复杂,但是非常有用,可以这样说,用 if 可以实现很多指令,但是用内置指令更简洁,还是要优先用指令。

if ($param) 如果 $param 为空字符串或 0 为假,其它情况为真。

注意 if 后面必须要有空格,否则报错。

set $param '0';
set $param 0;
set $param '';
这三种写法 $param 都为假,其它情况都为真
复制代码

用 = ,!=判断相等。

if ($request_method = POST){
      return 403;
 }
复制代码

注意 是一个 =  不是两个=, 等号左右必须要有空格,否则报错

用正则表达式判断

~ 区分大小写
if ($http_user_agent ~ mobile)
~* 不区分大小写
if ($http_referer ~* juejin\.cn)
!~ 和 !~* 是对应的两个否定写法,不再举例了。
复制代码

用 flag

-f !-f  文件是否存在
if (-f $request_filename)
if (!-f $request_filename)
-d !-d 目录是否存在
-e !-e 文件或目录是否存在
-x !-e 是否可执行
复制代码

移动 pc 适配

我们希望在访问 一个网址的时候,如果是在 pc端打开的时候,显示pc的页面,如果是在移动端打开的,显示移动端的页面。网址只有一个。

server{
 {  
    set $isMobile true;
    if ($http_user_agent ~* '(Android|webOS|IEMobile|iPhone|iPod|BlackBerry)') { 
        set $isMobile true;
    }
    set $root  /home/duhongwei/web/pc;
    if ($isMobile = true) {
      set $root  /home/duhongwei/web/h5;
    }
    root $root;
}
复制代码

这个设置需要放在 server 下面,对于所有 location 有效。在 location 里如果有需要还可以修改 root,所以 set  一个 $isMobile 的变量,方便后面使用。

ipad 虽然是移动设置,但从尺寸上来看更接近 pc,所以页面在 ipad 打开,一般会显示 pc 的页面

配置https服务

虽然在开发的时候用 http 就行,但有的时候,必须要 https 才行。所以配置开发环境可能也得配置 https 服务。我们的目的是为了让服务跑起来,还是很简单的。

  1. 申请证书
  2. 证书包含一个crt文件一个key文件,crt为证书,key为密钥
  3. 配置nginx

如果你正在做一个项目,这个项目的域名证书应该是提前就申请好的。用这个证书就行。本地配 host ,配项目的线上域名,就可以测试了。

server {
    # 1 
    listen       443 ssl;    
    server_name  www.xxx.com; 
    # 2
    ssl_certificate      证书的绝对路径     
    # 3
    ssl_certificate_key  密钥的绝对路径
}   
复制代码

配置 https服务,只需要三步

  1. 监听 443 端口
  2. 设置证书的绝对路径
  3. 设置密钥的绝对路径

只这三步就完成,很简单吧。

要启用 http2 也很简单, 只需要在 listen 后面加 http2 即可。

listen 443 ssl http2;
复制代码

请求地址末尾加斜杠与不加斜杠

请求 juejin.cn/b 与 juejin.cn/b/ 有什么区别吗? nginx 解析起来区别就大了。

不加斜杠,如果存在 b 文件,返回文件 b 如果不存在文件b,但有文件夹 b, 301 到 b/,在浏览器中看到的现象是发了两个请求.如果 b下面没有index.html,返回  403,如果有,返回内容。

image.png


如果没有 index.html,为什么是返回 403而不是 404呢?

这是因为 nginx 如果找不到 index.html,会尝试浏览目录,默认是不允许的。

autoindex off;
复制代码

如果既没有文件夹 b 也没有文件 b 返回 404。

为什么访问 b/ 会去查找 b/index.html?这是因为 index 指令默认是这样的

index index.html
复制代码

nginx 默默做了这么多,就是为了让我们用起来方便。如果从性能方面来考虑,写完整地址最好,别让nginx去猜了。用户怎么输入网址我们管不了,但是我们在写跳转地址的时候,最好是写完整地址。

请求头信息对应的 nginx 变量

nginx 中的有些变量有是规律的,按规律可以方便记忆。

image.png


对每一个请求标头,都对应一个变量

大小写不敏感,以 http_ 开头, -  改为 _ 。

  • $http_accept
  • $http_cache_control

cookie 的中的变量

比如 cookie 中 包含 name=jack,用 $cookie_name 可以拿到 jack 这个值。

  • cookie_name

arg 中的变量

比如有这样的 get 请求 index.html?name=jack ,用 $arg_name 可以拿到 jack 这个值。

  • arg_name

nginx 接收客户端提交

当我们提交一个表单的时候,会生成一个请求的 header,body。在header中 Content-Length:123 标明 content 字节大小。nginx 接收到 header,先检查 header 大小,如果 header 大小超过 client_header_buffer_size 的默认值 1K,并超过 large_client_header_buffers 的默认值 8K nginx 会报错。

检查 Content-Length ,如果超过 client_body_buffer_size 默认值  8K(除了 x86 的 64位系统是 16K),内存缓冲区无法接收,会存到 client_body_temp_path 指定的目录。但是接收的body总大小不是无限的,不能超过 client_max_body_size 的默认值  1M。

对于大多数请求来讲,都是 get 请求,我们可以直接躺平,默认值即可。 get 请求没有 body,超限的情况可能是 cookie 过多,url 过长。一般来说,超出的可能性不大。

对于 post 请求,当上传文件的时候,可能会超限。nginx 默认只能接收 1M 的内容,可以增加这个默认值。

locatoin /upfile/ {
    client_max_body_size:200M;
}
复制代码

如果网络状态不好,可能刚发一个字节,就断了,如果在默认 60 秒内没有再次发送,nginx 会中断链接。这样做是为了节省服务器资源。可以通过 修改 client_body_timeoutclient_header_timeout 改变默认值。

server{
  client_body_timeout 10s;
  client_header_timeout 10s;
}
复制代码

60秒有点太保守了,可以减小这个值。

nginx gzip 压缩

gzip压缩的知识还是非常多的,不过只是启用gzip ,打开功能,还是很简单的。

server{
gzip on;
gzip_min_length 0;
}
复制代码

gzip 指令默认是 off,设置为 on 打开 gzip。如果只设置 gzip on gzip 可有不会生效,gzip 默认只对大于20字节的内容做处理。我们在测试的时候页面内容都很少,很容易少于 20 字节 gzip_min_length,设为 0 代表所有大小都压缩。

启用压缩后,在请求 /index.html  响应 200 的时候,查看 header,发现有两个增加,并且 Content-Length 不见了。


image.png


Content-Encoding:gzip 内容的格式为 gzip,告诉浏览器,需要 gzip 解压再展示。

Transfer-Encoding:chunked 数据是通过一系列块来传输的,省略  Content-Length ,为了得到内容大小,需要把每个 chunk 的大小加起来。

为什么打开 gzip 后 content-length 信息没有了呢? 这是因为 nginx 的压缩是异步的,发送头的时候,nginx 可能正在压缩,不知道压缩完成的文件大小。

指定需要 gzip 的文件

我们访问 /index.css ,发现并没有压缩,这是因为 gzip ,默认只压缩 text/html 类型的文件。

增加 text/css 类型后,css 文件 也可以压缩了。

gzip_types text/html,text/css;
复制代码

压缩级别。

gzip 有9个压缩级别,越高,压缩效果越好,但是对 cpu 的消耗越多。默认压缩级别为 1 。我们可以设置一个合适的级别,比如 2;

gzip_comp_level 2;
复制代码

gzip_static

前面讲的nginx 处理 gzip 的方式是服务器负责压缩,这样会消耗掉很多 cpu 资源。我们可以先把文件压缩成 gzip,nginx 直接拿 gzip 过的文件就行了。预处理的好处不光是节省了 cpu 压缩时间,还可以 让 nginx 可以使用 sendfile 系统调用来传输文件,性能得到提高。

为了能直接拿 gzip 过的文件,需要 gzip_static 模块。 新版本的 nginx 已经默认安装了这个模块,如果是老版的 nginx 这个模块需要安装一下。

gzip_static always; 
复制代码

如果加上这句,nginx 不报错,说明 gzip_static 模块已经安装了。

gzip_static 可以有三个值。

  • off 默认值。 不启用 gzip_static。gzip功能还是可以用的。
  • on 启用。 当客户端支持 gzip的时候,发送压缩文件,不支持的时候发送原文件。
  • always 总是。 不管客户端是否支持,都优先发送压缩文件。如果没有压缩文件,再发送原文件。

实操的时候,用 always 比较好。现在不支持 gzip 的浏览器太少了,这样可以免掉 nginx 判断的步骤,对性能有所提高。为了方便 nginx 查找(文件越多,查找越慢),只保留 gzip 文件,原文件全部删除。

负载均衡

upstream servers {
    server 192.168.1.1;
    server 192.168.1.2;
}
server {
    listen       80;
    server_name  _;
    location / {
        proxy_pass   http://servers;
        proxy_set_header        Host    $host;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
复制代码

nginx 常用的负载均衡 是 upstream  来完成的。默认采用轮询的方式依次访问各个服务器。可以给每个服务器加权重,调整访问的频率,权重越高,被访问的越频繁。

upstream servers {
    server 192.168.1.1 weight=1;
    server 192.168.1.2 weight=2;
}
复制代码

根据 ip 也可以分配请求。这样能保证同一个用户可以由同一个服务器来服务,可以解决登录 session 的问题。但是根据 ip 分配可能导致某些服务器请求过多,又不能再做调整,所以解决登录 session 的问题,可以用统一的 redis 服务来解决。

upstream servers {
    ip_hash;
    server 192.168.1.1 ;
    server 192.168.1.2 ;
}
复制代码

根据 url 分配请求。比如这样的场景, 资源(图片等静态文件)服务器从源服务器拉取资源后,下次请求会再次落到这个资源服务器,就可以直接返回结果 ,不用再从源服务器请求资源了。

upstream servers {
    hash $request_uri;
    server 192.168.1.1 ;
    server 192.168.1.2 ;
}
复制代码

最后再介绍一下最小连接数方案。在这种场景下,least_conn 算法很简单,首先遍历后端集群,比较每个后端的conns/weight(连接数除权重),选取该值最小的后端。 如果有多个后端的 conns/weight 值同为最小,那么对它们采用加权轮询算法。

upstream servers {
    least_conn;
    server 192.168.1.1 weight=2;
    server 192.168.1.2 weight=1 ;
}
复制代码

参考

目录
相关文章
|
1月前
|
缓存 应用服务中间件 网络安全
Nginx中配置HTTP2协议的方法
Nginx中配置HTTP2协议的方法
79 7
|
2月前
|
应用服务中间件 BI nginx
Nginx的location配置详解
【10月更文挑战第16天】Nginx的location配置详解
|
2月前
|
缓存 负载均衡 安全
Nginx常用基本配置总结:从入门到实战的全方位指南
Nginx常用基本配置总结:从入门到实战的全方位指南
325 0
|
2月前
|
应用服务中间件 Linux nginx
Jetson 环境安装(四):jetson nano配置ffmpeg和nginx(亲测)之编译错误汇总
这篇文章是关于在Jetson Nano上配置FFmpeg和Nginx时遇到的编译错误及其解决方案的汇总。
103 4
|
3天前
|
存储 应用服务中间件 nginx
nginx反向代理bucket目录配置
该配置实现通过Nginx代理访问阿里云OSS存储桶中的图片资源。当用户访问代理域名下的图片URL(如 `http://代理域名/123.png`)时,Nginx会将请求转发到指定的OSS存储桶地址,并重写路径为 `/prod/files/2024/12/12/123.png`。
31 5
|
27天前
|
缓存 负载均衡 算法
如何配置Nginx反向代理以实现负载均衡?
如何配置Nginx反向代理以实现负载均衡?
|
1月前
|
存储 负载均衡 中间件
Nginx反向代理配置详解,图文全面总结,建议收藏
Nginx 是大型架构必备中间件,也是大厂喜欢考察的内容,必知必会。本篇全面详解 Nginx 反向代理及配置,建议收藏。
Nginx反向代理配置详解,图文全面总结,建议收藏
|
19天前
|
负载均衡 前端开发 应用服务中间件
负载均衡指南:Nginx与HAProxy的配置与优化
负载均衡指南:Nginx与HAProxy的配置与优化
39 3
|
1月前
|
应用服务中间件 API nginx
nginx配置反向代理404问题
【10月更文挑战第18天】本文介绍了使用Nginx进行反向代理的配置方法,解决了404错误、跨域问题和302重定向问题。关键配置包括代理路径、请求头设置、跨域头添加以及端口转发设置。通过调整`proxy_set_header`和添加必要的HTTP头,实现了稳定的服务代理和跨域访问。
242 1
nginx配置反向代理404问题
|
27天前
|
负载均衡 监控 应用服务中间件
配置Nginx反向代理时如何指定后端服务器的权重?
配置Nginx反向代理时如何指定后端服务器的权重?
49 4