本文正在参加「金石计划」
这篇文章是记录了我从未接触过Nginx开始,慢慢摸索出来的关于Nginx的相关知识以及实践,相信对那些还未接触过或者刚接触过Nginx的小伙伴们有所帮助!👀。也欢迎大佬们批评指正,感谢大家!😀
Nginx 简介
Nginx 是什么
nginx (engine x) 是一个 HTTP 和反向代理服务器,一个邮件代理服务器,一个通用的 TCP/UDP 代理服务器,最初由 Igor Sysoev 编写。实际在在设计之初Nginx的产品目的就是为了邮件服务而诞生的,特点是占有内存少、体积小、并发能力强、性能高。
nginx 适用场景
Nginx 的三个主要应用场景:
1、静态资源服务
静态资源直接可以由 nginx 提供服务,降低对后台应用访问。
2、反向代理
- 通过反向代理访问后端接口
- 后端应用服务构成集群后,需要动态扩容,有的应用出问题了需要做容灾,那么需要 nginx 负载均衡功能
- 通过缓存功能,加快资源访问。
3、API 服务
数据库服务的性能,远高于后端应用服务,所以可以衍生出直接使用 nginx 访问数据库或者 redis,利用 nginx 的高并发性能,实现如 web 防火墙等复杂的业务功能。如 OpenResty 工具。
nginx 历史背景
C10K
问题是 Nginx 抢占舞台的主要原因。
随着互联网数据的快速增长,对我们的硬件设备和性能提出了很高的要求。根据摩尔定义,硬件性能得到很大提升,但是低效的 Apache 拖累了性能,导致硬件的性能没有得到最大的发挥。因为 Apache 的一个进程在同一时间只能处理一个连接,导致高并发的时候,进程间切换会消耗大量的性能。
1999 年的一个晴朗的早晨,Dan Kegel 发现了所有传统 Web 服务器都无法处理 10K 并发客户端/连接的问题,并将其命名为 C10K 问题。这个缩写中的 C 代表并发连接数,10K 代表数字。一起,它将问题表示为 10K 并发连接问题。
nginx 的优点
- 高并发,高性能
- 可扩展性好:模块化设计,生态圈好,如
Tengine
- 高可靠性:持续运行数年
- 热部署:不停止服务的时候升级 Nginx
- BSD 许可证:开源免费,可以修改源码并商用
nginx 的安装和使用
一般我们都会使用 Linux 下进行部署服务,所以 window 版本暂不做考虑。
安装 Nginx
💡 关于 yum
相信你每天都在使用 `npm`,npm 是一个包管理工具,可在本地环境中轻松操作各种包应用。当然 `CentOS`也有一个相当 npm 那样的包管理工具,可在服务器环境中轻松管理各种 npm 模块。
`yum`是一个在 `Fedora`、`RedHat`和 `CentOS`中的**Shell 软件包管理器**。其基于 `rpm包管理`,可从指定的服务器自动安装 `rpm包`,可自动处理依赖关系并一次性地安装所有依赖的软件包,整个过程与 `npm`有点像,只需掌握以下命令就能操作 `yum`。
注意:在 ubuntu 里面就不是 `yum`了,在 Ubuntu 中,类似于 yum 的包管理器是 `apt`(Advanced Package Tool)。
下面以 CentOS
环境为例:
首先检测 yum 源中有无 nginx
yum list | grep nginx
如果存在,在安装 nginx:
yum install nginx
再执行 nginx -v
,输出版本表示安装成功。
如果不存在,或者不是你需要的版本,需要**自行配置 yum 源**。
启动 nginx
nginx
启动 Nginx
后,在浏览器打开公网 IP,就可以看到 nginx 启动页面。
注意需要在阿里云配置 80 端口安全组。
常用命令
得益其安全稳定的特性,若未遇到特殊情况几乎都不会再重启,只需掌握以下命令就能操作 nginx
:
命令 | 功能 |
---|---|
nginx | 启动进程 |
nginx -t | 验证配置 |
nginx -s reload | 重启进程 |
nginx -s stop | 强制退出 |
nginx -s quit | 安全退出 |
ps -ef | grep nginx | 查看进程 |
Linux 每个应用运行都会产生一个进程,那么我们就可以通过查看 Nginx 进程是否存在来判断它是否启动。 用 ps -ef 列出进程列表,然后通过 grep 过滤。 如: ps -ef | grep nginx 就可以看到 Nginx 进程是否存在了。
nginx 配置
文件结构
... # 全局配置,对全局生效
events # 配置影响 Nginx 服务器或与用户的网络连接
http # 配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置
├── upstream # 配置后端服务器具体地址,负载均衡配置不可或缺的部分
├── server # 配置虚拟主机的相关参数,一个 http 块中可以有多个 server 块
├── server
│ ├── location # server 块可以包含多个 location 块,location 指令用于匹配 uri
│ ├── location
│ └── ...
└── ...
- 1、 全局块 :「影响 nginx 服务器整体配置的指令」。一般有运行 nginx 服务器的用户组,nginx 进程 pid 存放路径,日志存放路径,配置文件引入,允许生成 worker process 数等。
- 2、 events 块 :影响「Nginx 服务器与用户的网络连接」。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。
- 3、 http 块 :代理、缓存、日志等绝大多数功能和第三方模块的配置功能(可以嵌套多个 server)。如文件引入,mime-type 定义,日志自定义,是否使用 sendfile 传输文件,连接超时时间,单连接请求数等。
- 4、 server 块 :主要用于制定虚拟主机域名、IP 和端口号,一个 http 中可以有多个 server。
- 5、 location 块 :配置请求的路由,以及各种页面的处理情况。
他们之间的关系:server 继承 main,location 继承 server;upstream 既不会继承指令也不会被继承。
语法说明
- 配置文件由
指令
与指令块
组成 - 指令以
分号
结尾,指令与参数以空格
分隔 - 指令块以
大括号
将多条指令组织在一起 - 使用
$
表示变量,提高复用性 - 使用
#
加入注释,提高可读性 - 部分指令的参数支持正则表达式
include
语句允许组合多个配置文件以提升配置的可维护性
一个简单的 nginx 配置示例:
user nginx; # 运行用户,默认即是nginx,可以不进行设置
worker_processes 1; # Nginx 进程数,一般设置为和 CPU 核数一样
error_log /var/log/nginx/error.log warn; # Nginx 的错误日志存放目录
pid /var/run/nginx.pid; # Nginx 服务启动时的 pid 存放位置
events {
use epoll; # 使用epoll的I/O模型(如果你不知道Nginx该使用哪种轮询方法,会自动选择一个最适合你操作系统的)
worker_connections 1024; # 每个进程允许最大并发数
}
http { # 配置使用最频繁的部分,代理、缓存、日志定义等绝大多数功能和第三方模块的配置都在这里设置
# 设置日志模式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main; # Nginx访问日志存放位置
sendfile on; # 开启高效传输模式
tcp_nopush on; # 减少网络报文段的数量
tcp_nodelay on;
keepalive_timeout 65; # 保持连接的时间,也叫超时时间,单位秒
types_hash_max_size 2048;
gzip on; #开启gzip压缩
include /etc/nginx/mime.types; # 文件扩展名与类型映射表
default_type application/octet-stream; # 默认文件类型
include /etc/nginx/conf.d/*.conf; # 包含的子配置项位置和文件
server {
listen 80; # 配置监听的端口
server_name localhost; # 配置的域名
location / {
root /usr/share/nginx/html; # 网站根目录
index index.html index.htm; # 默认首页文件
deny 172.168.22.11; # 禁止访问的ip地址,可以为all
allow 172.168.33.44; # 允许访问的ip地址,可以为all
}
error_page 500 502 503 504 /50x.html; # 默认50x对应的访问页面
error_page 400 404 error.html; # 同上
}
}
location 匹配规则
注意:location 匹配的不是 url 路由地址,而是对于服务器中的目录或者文件。
/
:通用匹配,任何内容请求都会匹配到=
:进行普通字符匹配。也就是完全匹配。^~
:前缀匹配。如果匹配成功,则不再匹配其他 location。~
:表示执行一个正则匹配,区分大小写~*
:表示执行一个正则匹配,不区分大小写/xxx/
:常规字符串路径匹配
预定义变量
nginx 预定义变量也叫全局变量。
$arg_PARAMNETER GET请求变量名PARAMEMTER参数的值
$args 这个变量等于Get 请求中的参数
$body_bytes_sent 传送页面的字节数
$content_length 请求头中发热content-lenth字段
$content_type 请求头中的Content-Type字段。
$cookie_COOKIE cookie COOKIE的值。
$document_root 当前请求在root指令中指定的值
$document_uri 与$uri相同
$host 请求中的主机头(Host)字段,如果请求中的主机头不可用或者空,则为处理请求的server名称(处理请求的server的server_name指令的值)。值为小写,不包含端口。
$hostname机器名使用 gethostname系统调用的值
$http_HEADER HTTP请求头中的内容,HEADER为HTTP请求中的内容转为小写,-变为_(破折号变为下划线),例如:$http_user_agent(Uaer-Agent的值);
$sent_http_HEADER HTTP响应头中的内容,HEADER为HTTP响应中的内容转为小写,-变为_(破折号变为下划线),例如: $sent_http_cache_control, $sent_http_content_type…;
$is_args 如果$args设置,值为"?",否则为""
$limit_rate 这个变量可以限制连接速率。
$nginx_version当前运行的nginx版本号
$query_string 与$args相同
$remote_addr客户端的IP地址。
$remote_port 客户端的端口。
$remote_user 已经经过Auth Basic Module验证的用户名
$request_filename 当前连接请求的文件路径,由root或alias指令与URI请求生成
$request_body这个变量(0.7.58+)包含请求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比较有意义
$request_body_file客户端请求主体信息的临时文件名
$request_completion 如果请求成功,设为"OK";如果请求未完成或者不是一系列请求中最后一部分则设为空。
$request_method 这个变量是客户端请求的动作,通常为GET或POST。包括0.8.20及之前的版本中,这个变量总为main request中的动作,如果当前请求是一个子请求,并不使用这个当前请求的动作。
$request_uri 这个变量等于包含一些客户端请求参数的原始URI,它无法修改,请查看$uri更改或重写URI。
$scheme所用的协议,比如http或者是https,比如rewrite ^(.+)$ $scheme://example.com$1 redirect;
$server_addr服务器地址,在完成一次系统调用后可以确定这个值,如果要绕开系统调用,则必须在listen中指定地址并且使用bind参数。
$server_name 服务器名称
$server_port请求到达服务器的端口号
$server_protocol 请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
$uri 请求中的当前URI(不带请求参数,参数位于args,不同于浏览器传递的args),不同于浏览器传递的request_uri的值,它可以通过内部重定向,或者使用index指令进行修改。不包括协议和主机名,例如/foo/bar.html
关于 $http_origin
$http_origin并不是nginx的内置参数,nginx支持取自定义的参数值,$http_XXX这个格式是nginx取请求中header的XXX的值的。
这里取的是origin,而一般跨域请求都会将请求的来源放在origin中(浏览器会往跨域请求的header上面加origin这个header)。
```
request headers:
-------------------------------
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Host: 61.231.19.187:8089
If-Modified-Since: Sun, 16 Aug 2020 08:22:11 GMT
If-None-Match: "5f38ecb3-159"
Origin: http://abc.bdc.cn:8080
Referer: http://abc.bdc.cn:8080/1/tmp/index.html
User-Agent: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36
```
一个示例
使用 nginx 部署静态网页:
server {
listen 80;
server_name 101.200.146.230; # 指定IP或域名
location / {
root /www/client/daotin; # 静态文件存放在/www/client/daotin目录下
index index.html;
}
}
server 虚拟主机配置
基于端口号来配置虚拟主机,算是 Nginx 中最简单的一种方式了。原理就是 Nginx 监听多个端口,根据不同的端口号,来区分不同的网站。
server{
listen 8001;
server_name localhost;
root /usr/share/nginx/html/html8001;
index index.html;
}
然后重启 nginx,我们就可以在浏览器中访问 http://localhost:8001
了。
缓存配置
缓存对于 Web 至关重要,尤其对于大型高负载 Web 站点。Nginx 缓存可作为性能优化的一个重要手段,可以极大减轻后端服务器的负载。通常对于静态资源,即较少经常更新的资源,如图片,css 或 js 等进行缓存,从而在每次刷新浏览器的时候,不用重新请求,而是从缓存里面读取,这样就可以减轻服务器的压力。
Nginx 缓存类型:
Nginx 设置缓存有两种方式:
proxy_cache_path
和proxy_cache
Cache-Control
和Pragma
一般来说,由服务器设置缓存即可,nginx 代理缓存可以不设置。
跨域配置
<script>
let myHeaders = new Headers({
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json",
});
fetch("http://101.200.146.230:4444/test.json", {
method: "GET",
headers: myHeaders,
mode: "cors",
})
.then((response) => {
console.log("response.json==>", response.json());
return response.json();
})
.then((data) => console.log("test.json ==>", data))
.catch((err) => console.log("fetch失败", err));
</script>
preflight request 预检请求
客户端仅发送了一个 OPTIONS 方法的请求,被服务器 403 状态码给拒绝了,查阅了有关 OPTIONS 方法和预检请求的博客和文档,梳理了大概关系。
什么是预检请求?
- HTTP 请求分为简单请求 与 复杂请求,两种请求的区别主要在于简单请求不会触发 CORS 预检请求,而复杂请求会触发 CORS 预检请求
- 满足简单请求的条件(两个条件需要都满足)
- 方法为 GET、HEAD、POST 之一
- 无自定义请求头,且 Content-Type 为 text/plain, mutipart/form-data application/x-www-form-urlencoded 之一
- 不满足简单请求的一切请求都是复杂请求
- 预检请求(一般是浏览器自动发起的 OPTIONS 方法的请求) 中
- Access-Control-Request-Method 字段告诉服务器实际请求会使用的 HTTP 方法;
- Access-Control-Request-Headers 字段告知服务器实际情况所携带的自定义首部字段。服务器基于预检请求获得的信息来判断,是否接受接下来的实际请求。服务器端返回的 Access-Control-Allow-Methods 字段 将服务器允许的请求方法告诉客户端。该首部字段与 Allow 类似,但只能用户设计到 CORS 的场景中。
💡 为什么要发起预检请求 ?
[《关于 preflight request》](https://blog.csdn.net/mym940725/article/details/79506994 "《关于preflight request》") 解释的比较清楚,目前浏览器限制跨域的方式主要有两种
1. 浏览器限制发起跨域请求
2. 跨域请求可以正常发起,但是返回的结果被浏览器拦截
一般浏览器都是采用第二种方式限制跨域请求,也就是说请求已经到达了服务器,如果是复杂请求,对服务器数据库的数据进行了操作,但返回给浏览器的结果却被拦截,被识别为一次失败的请求,这时候可能对数据库里数据已经产生了影响。为了防止这种情况发生,这种可能对服务器数据产生操作的 HTTP 请求,浏览器必须先试用 OPTIONS 方法发起预检请求,从而获知服务器是否允许该跨域请求。
参考资料:
如何配置跨域
注意:如果是 A 访问 B 出现跨域,则需要在 B 上进行跨域设置,而不是在 A 上。
比如 A 的地址是 101.200.146.230:80
,然后访问 B 的地址 101.200.146.230:4444
中的一个 test.json
文件,因为端口不同,所以会报跨域错误。
需要在 B 服务设置跨域:
server {
listen 4444;
server_name 101.200.146.230;
# 参考方案:https://segmentfault.com/q/1010000021055878
add_header "Access-Control-Allow-Origin" $http_origin;
# 带cookie请求需要加上这个字段,并设置为true
add_header Access-Control-Allow-Credentials true;
# $http_origin动态获取请求客户端请求的域 不用*的原因是带cookie的请求不支持*号
# add_header "Access-Control-Allow-Origin" $http_origin; # 当前请求域名,不支持携带Cookie的请求
add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS"; # 允许的请求方式
# 表示请求头的字段 动态获取
add_header Access-Control-Allow-Headers $http_access_control_request_headers;
# 防止报preflight request错误
if ($request_method = "OPTIONS") {
return 200;
}
location / {
root /www/static; #资源存放位置
index index.html;
}
}
除了访问别人服务器上的文件是跨域外,接口访问也是跨域。因为接口也是文件,接口实际上跟服务器上的文件是有一个映射关系的,因此,接口也可以使用 nginx 来进行跨域配置。
反向代理
什么是反向代理?
反向代理(Reverse Proxy)方式是指以代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
正向代理: 一般的访问流程是客户端直接向目标服务器发送请求并获取内容,使用正向代理后,客户端改为向代理服务器发送请求,并指定目标服务器(原始服务器),然后由代理服务器和原始服务器通信,转交请求并获得的内容,再返回给客户端。正向代理隐藏了真实的客户端,为客户端收发请求,使真实客户端对服务器不可见;
举个具体的例子 🌰,你的浏览器无法直接访问谷哥,这时候可以通过一个代理服务器来帮助你访问谷哥,那么这个服务器就叫正向代理。
反向代理: 与一般访问流程相比,使用反向代理后,直接收到请求的服务器是代理服务器,然后将请求转发给内部网络上真正进行处理的服务器,得到的结果返回给客户端。反向代理隐藏了真实的服务器,为服务器收发请求,使真实服务器对客户端不可见。一般在处理跨域请求的时候比较常用。现在基本上所有的大型网站都设置了反向代理。
举个具体的例子 🌰,去饭店吃饭,可以点川菜、粤菜、江浙菜,饭店也分别有三个菜系的厨师 👨🍳,但是你作为顾客不用管哪个厨师给你做的菜,只用点菜即可,小二将你菜单中的菜分配给不同的厨师来具体处理,那么这个小二就是反向代理服务器。
简单的说,一般给客户端做代理的都是正向代理,给服务器做代理的就是反向代理。
总结:
正向代理与反向代理:juejin.cn/post/709532…
正向代理是代理客户端,为客户端收发请求,使真实客户端对服务器不可见。
用途:科学上网。
反向代理是代理服务器,为服务器收发请求,使真实服务器对客户端不可见。
用途:负载均衡,提供安全保障。
nginx 反向代理主要通过 proxy_pass
来配置,将你项目的开发机地址填写到 proxy_pass 后面,正常的格式为 proxy_pass URL 即可。
server {
listen 80;
location / {
proxy_pass http://10.10.10.10:20186;
}
}
反向代理还有些常用的指令,我在这里给大家列出:
proxy_set_header
:在将客户端请求发送给后端服务器之前,更改来自客户端的请求头信息。proxy_connect_timeout
:配置 Nginx 与后端代理服务器尝试建立连接的超时时间。proxy_read_timeout
: 配置 Nginx 向后端服务器组发出 read 请求后,等待相应的超时时间。proxy_send_timeout
:配置 Nginx 向后端服务器组发出 write 请求后,等待相应的超时时间。proxy_redirect
:用于修改后端服务器返回的响应头中的 Location 和 Refresh。
负载均衡
一般情况下,客户端发送多个请求到服务器,服务器处理请求,其中一部分可能要操作一些资源比如数据库、静态资源等,服务器处理完毕后,再将结果返回给客户端。
这种模式对于早期的系统来说,功能要求不复杂,且并发请求相对较少的情况下还能胜任,成本也低。随着信息数量不断增长,访问量和数据量飞速增长,以及系统业务复杂度持续增加,这种做法已无法满足要求,并发量特别大时,服务器容易崩。
很明显这是由于服务器性能的瓶颈造成的问题,除了堆机器之外,最重要的做法就是负载均衡。
请求爆发式增长的情况下,单个机器性能再强劲也无法满足要求了,这个时候集群的概念产生了,单个服务器解决不了的问题,可以使用多个服务器,然后将请求分发到各个服务器上,将负载分发到不同的服务器,这就是 负载均衡 ,核心是「分摊压力」。
Nginx
提供以下 负载均衡
方式,默认为 轮询
。
- 轮询:无需配置,每个请求根据时间顺序逐一分配到不同服务器,若其中一个服务挂了会自动被剔除
- weight:根据权重分配,指定每个服务器的轮询几率,权重越高其被访问的概率越大,可解决服务器性能不均的问题
- ip_hash:根据访问
IP
的Hash结果
分配,每个访客固定访问一个服务器,可解决动态网页Session共享
的问题 - fair:根据服务器响应时间分配,响应时间短的服务器会优先分配,需安装
nginx-upstream-fair
http {
upstream firstdemo {
# ip_hash; # IpHash方式
# fair; # Fair方式
server http://127.0.0.1:9999; # 负载均衡目的服务地址:可设置多个服务器
server http://127.0.0.1:8888;
server http://127.0.0.1:7777 weight=10; # 配置权重:不配置默认为1
}
server {
location / {
proxy_pass firstdemo;
proxy_connect_timeout 10;
}
}
}
上面配置各个服务器中都指明了传输协议为 http://
, 但是如果上面的接口没有指明协议的话,那么我们需要在 proxy_pass 上加上了,proxy_pass http://proxy_xxx
这样的,如下配置代码:
http {
upstream firstdemo {
# ip_hash; # IpHash方式
# fair; # Fair方式
server 127.0.0.1:9999; # 负载均衡目的服务地址:可设置多个服务器
server 127.0.0.1:8888;
server 127.0.0.1:7777 weight=10; # 配置权重:不配置默认为1
}
server {
location / {
proxy_pass http://firstdemo;
proxy_connect_timeout 10;
}
}
}
动静分离
动静分离分离的是后端的动态资源和静态资源,而不是前端的资源。前端的都是静态资源。。
动静分离就是根据一定规则静态资源的请求全部请求 Nginx 服务器,后台数据请求转发到 Web 应用服务器上。从而达到动静分离的目的。目前比较流行的做法是将静态资源部署在 Nginx 上,而 Web 应用服务器只处理动态数据请求。这样减少 Web 应用服务器的并发压力。
动静分离,说白了,就是将网站静态资源(HTML,JavaScript,CSS,img 等文件)与后台应用分开部署,静态资源的请求全部请求 Nginx 服务器,后台数据请求转发到 Web 应用服务器上。提高用户访问静态代码的速度,降低对后台应用服务器的请求。后台应用服务器只负责动态数据请求。
动静分离可通过 location 对请求 url 进行匹配,将网站静态资源(HTML,JavaScript,CSS,img 等文件)与后台应用分开部署,提高用户访问静态代码的速度,降低对后台应用访问。通常将静态资源放到 nginx 中,动态资源转发到 tomcat 服务器中。
目前动静分离的方式有两种解决方案。
- 将静态资源单独部署到一个域名。
- 将静态资源放在项目之外的某个文件夹,通过
Nginx
配置区分。(比如上述在www
文件夹中创建的client
文件夹用于存放Web
应用源码,创建的static
文件夹用于存放静态资源。)
server {
listen 80;
server_name 101.200.146.230; # 指定IP或域名
location / {
root /www/client/daotin;
index index.html;
}
# 拦截静态资源
location ~ .*\.(gif|jpg|jpeg|bmp|png|ico|txt){
root /www/static;
expires 7d;
}
}
示例:
比如当我们代码中访问资源文件的时候,就会自动去 /www/static
目录寻找对应的文件。
<el-image src="/touxiang.jpg"></el-image>
需要注意的是,如果是下面的写法
<el-image src="/static/touxiang.jpg"></el-image>
当我们使用路径匹配代理到 /www/static
后,它其实访问的是 /www/static/static/touxiang.jpg
,而不是 /www/static/touxiang.jpg
,这个千万要注意了。
location /static/ {
# 访问的是 /www/static/static/touxiang.jpg
root /www/static;
# 访问的是 /www/static/touxiang.jpg
# alias /www/static/;
}
这也是 root 和 alias 的区别。
参考:
适配 PC 或移动设备
现在很多网站都是有了 PC 端和 H5 站点的,因为这样就可以根据客户设备的不同,显示出体验更好的,不同的页面了。
除了自适应之外,很多大型网站使用分开制作的方式来呈现 PC 和 H5 站点内容。
Nginx 通过内置变量 $http_user_agent
,可以获取到请求客户端的 userAgent,就可以用户目前处于移动端还是 PC 端,进而展示不同的页面给用户。
server{
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
if ($http_user_agent ~* '(Android|iPhone|iPod)') {
root /usr/share/nginx/mobile;
}
index index.html;
}
}
当然,对于单页面应用,入口都是 index.html,PC 和移动端会使用组件来区分,所以不会用到上面的配置。
Gzip 压缩
首先,gzip 是需要服务器和浏览器同时支持的。当浏览器支持 gzip 压缩时,会在请求消息中包含 Accept-Encoding: gzip
,这样 Nginx 就会向浏览器发送经过 gzip 后的内容,同时在相应信息头中加入 Content-Encoding:gzip
,声明这是 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 压缩。
http {
.....
gzip on;
# 需要压缩的类型
gzip_types text/plain application/javascript text/css;
.....
}
❓ 如果 Nginx 已经做了 gzip,那么 Vue 还需要做吗?
1、其实 Vue 本身不能做压缩打包之类的功能,他是靠 webpack 进行打包,而 webpack 有插件可以生产 gz 类型的文件。
2、当你把一个包含 gz 的静态资源放到 nginx 上,有 web 请求过来时,nginx 如果开启了 gzip,那么它会检测你的静态资源文件夹里面有没有 gz 文件,如果有的话,nginx 会直接返回 gz 文件,如果没有,nginx 会动态的压缩成 gz 返回到浏览器。
因此,当服务器配置了 gzip,那么前端可以不用做 gzip,但是你做好了 gz 文件放到服务器上, 可以为服务器省下实时压缩成 gz 文件的计算资源,所以推荐还是前端做好 gzip 然后放到服务器上。
> Tips: nginx 检测 gz 文件需要手动配置开启,也可以不检测,每次都实时压缩为 gzip
Nginx 静态压缩和动态压缩
Nginx 中配置前端的 gzip 压缩,有两种思路:
- Nginx 动态压缩,静态文件还是普通文件,请求来了再压缩,然后返回给前端。
- Nginx 静态压缩,提前把文件压缩成 .gz 格式,请求来了,直接返回即可。
Nginx 静态压缩需要设置:
gzip_static on;
❓ 如何判断 gzip_static 是否生效?
在请求的 response headers 里面的 Etag 里面,没有 `W/`就表明使用的是我们自己的 .gz 文件。
图片防盗链
防盗链的原理其实很简单,目前比较流行的做法就是通过 Referer 来进行判断和限制,Referer 的解释说明如下:
HTTP Referer 是 header 的一部分,当浏览器向 web 服务器发送请求的时候,一般会带上 Referer,告诉服务器我是从哪个页面链接过来的,服务器基此可以获得一些信息用于处理。——引用自百度百科
简单来说,假如我博客域名是 devler.cn,我在 nginx 中设置,只允许 Referer 为 *.devler.cn
的来源请求图片,其它网站来的一律禁止。这里我们需要用到 ngx_http_referer_module
模块和 $invalid_referer
变量,请看下面进一步解释。
ngx_http_referer_module 模块
ngx_http_referer_module
模块用于阻止对“Referer”头字段中具有无效值的请求访问站点。应该记住,使用适当的“Referer”字段值来构造请求非常容易,因此本模块的预期目的不是要彻底阻止此类请求,而是阻止常规浏览器发送的请求的大量流量。还应该考虑到,即使对于有效请求,常规浏览器也可能不发送“Referer”字段。
语法:valid_referers none | blocked | server_names | string ...;
可用于:server,location
可以看到 valid_referers 指令中存在一些参数,比如 none|blocked,含义如下:
- none:请求标头中缺少“Referer”字段,也就是说 Referer 为空,浏览器直接访问的时候 Referer 一般为空。
- blocked: Referer”字段出现在请求标头中,但其值已被防火墙或代理服务器删除; 这些值是不以“http://” 或 “https://” 开头的字符串;
- server_names: 服务器名称,也就是域名列表。
$invalid_referer
变量
我们设置 valid_referers 指令后,会将其结果传递给一个变量 invalid_referer,其值为 0 或 1,可以使用这个指令来实现防盗链功能,如果 $valid_referers
列表中没有包含 Referer 头的值,$invalid_referer
将被设置为 1。
设置防盗链白名单
白名单就是只允许白名单内的域名访问,其余一律禁止。
location ~ .*.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico|webp)$ {
valid_referers none blocked *.devler.cn;
if ($invalid_referer) {
return 403;
}
}
上面的配置含义是先用 location 匹配出需要的格式(图片和视频),然后用 valid_referers 指令设置允许的域名,其它域名没有包含在 valid_referers 列表中,$invalid_referer 变量返回的值为 1,最终返回 403,禁止访问。以上就是防盗链白名单的设置。
防盗链黑名单
黑名单与白名单正好相反,就是只禁止黑名单中的域名请求,其余一律放行,相比白名单,黑名单的限制更加宽松。网上大部分教程只提到了防盗链白名单的设置,了解原理后黑名单的设置方法也差不多。
location ~ .*.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico|webp)$ {
valid_referers *.baidu.com;
if ($invalid_referer = 0) {
return 403;
}
}
上面的配置中我们用 valid_referers 指令设置黑名单域名*.baidu.com,获取到指定的 Referer 头之后,$invalid_referer 返回值为 0,最终返回 403,禁止百度的域名来访问。
参考文档
- nginx 中文文档:docshome.gitbook.io/nginx-docs/
- location 匹配规则:www.cnblogs.com/woshimrf/p/…
Nginx 可视化配置
进阶学习资料
最后,欢迎大家踊跃交流心得~