目录
1. 安装
安装部分略
2.常用操作命令
Nginx 的命令在控制台中输入 nginx -h
就可以看到完整的命令,这里列举几个常用的命令:
nginx -s reload # 向主进程发送信号,重新加载配置文件,热重启 nginx -s reopen # 重启 Nginx nginx -s stop # 快速关闭 nginx -s quit # 等待工作进程处理完成后关闭 nginx -T # 查看当前 Nginx 最终的配置 nginx -t -c <配置路径> # 检查配置是否有问题,如果已经在配置目录,则不需要-c
systemctl
是 Linux 系统应用管理工具 systemd
的主命令,用于管理系统,我们也可以用它来对 Nginx 进行管理,相关命令如下:
systemctl start nginx # 启动 Nginx systemctl stop nginx # 停止 Nginx systemctl restart nginx # 重启 Nginx systemctl reload nginx # 重新加载 Nginx,用于修改配置后 systemctl enable nginx # 设置开机启动 Nginx systemctl disable nginx # 关闭开机启动 Nginx systemctl status nginx # 查看 Nginx 运行状态
3. nginx配置语法
就跟前面文件作用讲解的图所示,Nginx 的主配置文件是/etc/nginx/nginx.conf
(mac brew 安装的地址在/usr/local/Cellar/nginx/版本/bin/nginx.conf)
,你可以使用 cat -n nginx.conf
来查看配置。
3.1 nginx.conf结构图
main # 全局配置,对全局生效 ├── events # 配置影响 Nginx 服务器或与用户的网络连接 ├── http # 配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置 │ ├── upstream # 配置后端服务器具体地址,负载均衡配置不可或缺的部分 │ ├── server # 配置虚拟主机的相关参数,一个 http 块中可以有多个 server 块 │ ├── server │ │ ├── location # server 块可以包含多个 location 块,location 指令用于匹配 uri │ │ ├── location │ │ └── ... │ └── ... └── ...
3.2 配置文件的语法规则
- 配置文件由指令与指令块构成;
- 每条指令以
;
分号结尾,指令与参数间以空格符号分隔; - 指令块以
{}
大括号将多条指令组织在一起; -
include
语句允许组合多个配置文件以提升可维护性; - 使用
#
符号添加注释,提高可读性; - 使用
$
符号使用变量; - 部分指令的参数支持正则表达式;
3.4 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; 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.1; # 禁止访问的ip地址,可以为all allow 172.168.33.4; # 允许访问的ip地址,可以为all } error_page 500 502 503 504 /50x.html; # 默认50x对应的访问页面 error_page 400 404 error.html; # 同上 } }
server 块可以包含多个 location 块,location 指令用于匹配 uri,语法:
location [ = | ~ | ~* | ^~] uri { ... }指令后边:
-
=
精确匹配路径,用于不含正则表达式的 uri 前,如果匹配成功,不再进行后续的查找; -
^~
用于不含正则表达式的 uri; 前,表示如果该符号后面的字符是最佳匹配,采用该规则,不再进行后续的查找; -
~
表示用该符号后面的正则去匹配路径,区分大小写; -
~*
表示用该符号后面的正则去匹配路径,不区分大小写。跟~
优先级都比较低,如有多个location的正则能匹配的话,则使用正则表达式最长的那个;如果 uri 包含正则表达式,则必须要有~
或~*
标志
3.5 全局变量 Nginx 有一些常用的全局变量,你可以在配置的任何位置使用它们,如下表:
全局变量名 # 功能 $host # 请求信息中的 Host,如果请求中没有 Host 行,则等于设置的服务器名,不包含端口 $request_method # 客户端请求类型,如 GET、POST $remote_addr # 客户端的 IP 地址 $args # 请求中的参数 $arg_PARAMETER # GET 请求中变量名 PARAMETER 参数的值,例如:$http_user_agent(Uaer-Agent 值), $http_referer... $content_length # 请求头中的 Content-length 字段 $http_user_agent # 客户端agent信息 $http_cookie # 客户端cookie信息 $remote_addr # 客户端的IP地址 $remote_port # 客户端的端口 $http_user_agent # 客户端agent信息 $server_protocol # 请求使用的协议,如 HTTP/1.0、HTTP/1.1 $server_addr # 服务器地址 $server_name # 服务器名称 $server_port # 服务器的端口号 $scheme # HTTP 方法(如http,https)
还有更多的内置预定义变量,可以直接搜索关键字「nginx内置预定义变量」可以看到一堆博客写这个,这些变量都可以在配置文件中直接使用。
4. 设置二级域名虚拟主机
在某某云 ☁️ 上购买了域名之后,就可以配置虚拟主机了,一般配置的路径在域名管理 -> 解析 -> 添加记录
中添加二级域名,配置后某某云会把二级域名也解析到我们配置的服务器 IP 上,然后我们在 Nginx 上配置一下虚拟主机的访问监听,就可以拿到从这个二级域名过来的请求了
假如现在服务器上配置了一个 fe 的二级域名,也就是说在外网访问 fe.wahaha.club
的时候,也可以访问到我们的服务器了。
由于默认配置文件 /etc/nginx/nginx.conf
的 http 模块中有一句 include /etc/nginx/conf.d/*.conf
也就是说 conf.d
文件夹下的所有 *.conf
文件都会作为子配置项被引入配置文件中。为了维护方便,我在 /etc/nginx/conf.d
文件夹中新建一个 fe.wahaha.club.conf
:
# /etc/nginx/conf.d/fe.wahaha.club.conf server { listen 80; server_name fe.wahaha.club; location / { root /usr/share/nginx/html/fe; index index.html; } }然后在
/usr/share/nginx/html
文件夹下新建 fe 文件夹,新建文件 index.html
,内容随便写点,改完 nginx -s reload
重新加载,浏览器中输入 fe.wahaha.club
,发现从二级域名就可以访问到我们刚刚新建的 fe 文件夹:
5. 配置反向代理
反向代理是工作中最常用的服务器功能,经常被用来解决跨域问题,下面简单介绍一下如何实现反向代理。
首先进入 Nginx 的主配置文件:vim /etc/nginx/nginx.conf
为了看起来方便,把行号显示出来 :set nu
(个人习惯),然后我们去 http
模块的 server
块中的 location /
,增加一行将默认网址重定向到最大学习网站 Bilibili 的 proxy_pass
配置 :
改完保存退出,nginx -s reload
重新加载,进入默认网址,那么现在就直接跳转到 B 站了,实现了一个简单的代理。
实际使用中,可以将请求转发到本机另一个服务器上,也可以根据访问的路径跳转到不同端口的服务中。
比如我们监听 9001
端口,然后把访问不同路径的请求进行反向代理:
- 把访问
http://127.0.0.1:9001/edu
的请求转发到http://127.0.0.1:8080
- 把访问
http://127.0.0.1:9001/vod
的请求转发到http://127.0.0.1:8081
这种要怎么配置呢,首先同样打开主配置文件,然后在 http 模块下增加一个 server 块:
server { listen 9001; server_name *.sherlocked93.club; location ~ /edu/ { proxy_pass http://127.0.0.1:8080; } location ~ /vod/ { proxy_pass http://127.0.0.1:8081; } }
反向代理还有一些其他的指令,可以了解一下:
-
proxy_set_header
:在将客户端请求发送给后端服务器之前,更改来自客户端的请求头信息。 -
proxy_connect_timeout
:配置Nginx与后端代理服务器尝试建立连接的超时时间。 -
proxy_read_timeout
:配置Nginx向后端服务器组发出read请求后,等待相应的超时时间。 -
proxy_send_timeout
:配置Nginx向后端服务器组发出write请求后,等待相应的超时时间。 -
proxy_redirect
:用于修改后端服务器返回的响应头中的Location和Refresh。
6. 跨域CORS设置
要解决跨域问题,我们来制造一个跨域问题。首先和前面设置二级域名的方式一样,先设置好fe.wahaha.club
和 be.wahaha.club
二级域名,都指向本云服务器地址,虽然对应 IP 是一样的,但是在 fe.wahaha.club
域名发出的请求访问 be.wahaha.club
域名的请求还是跨域了,因为访问的 host 不一致。
6.1 使用反向代理解决跨域
在前端服务地址为 fe.wahaha.club
的页面请求 be.wahaha.club
的后端服务导致的跨域,可以这样配置:
server { listen 9001; server_name fe.wahaha.club; location / { proxy_pass be.wahaha.club; } }
这样就将对前一个域名 fe.wahaha.club
的请求全都代理到了 be.wahaha.club
,前端的请求都被我们用服务器代理到了后端地址下,绕过了跨域。
fe.wahaha.club
开始,不易区分,所以为了实现对后端服务请求的统一转发,通常我们会约定对后端服务的请求加上 /api/
前缀或者其他的 path 来和对静态资源的请求加以区分,此时我们可以这样配置:
# 请求跨域,约定代理后端服务请求path以/api/开头 location ^~/api/ { # 这里重写了请求,将正则匹配中的第一个分组的path拼接到真正的请求后面,并用break停止后续匹配 rewrite ^/api/(.*)$ /$1 break; proxy_pass be.wahaha.club; # 两个域名之间cookie的传递与回写 proxy_cookie_domain be.wahaha.club fe.wahaha.club; }
这样,静态资源我们使用 fe.wahaha.club/xx.html
,动态资源我们使用 fe.wahaha.club/apis/getAwo
,浏览器页面看起来仍然访问的前端服务器,绕过了浏览器的同源策略,毕竟我们看起来并没有跨域。
也可以统一一点,直接把前后端服务器地址直接都转发到另一个 server.wahaha.club
,只通过在后面添加的 path 来区分请求的是静态资源还是后端服务,看需求了。
6.2 配置header解决跨域
当浏览器在访问跨源的服务器时,也可以在跨域的服务器上直接设置 Nginx,从而前端就可以无感地开发,不用把实际*问后端的地址改成前端服务的地址,这样可适性更高
比如前端站点是 fe.wahaha.club
,这个地址下的前端页面请求 be.wahaha.club
下的资源,比如前者的 fe.wahaha.club/index.html
内容是这样的:
<html> <body> <h1>welcome fe.wahaha.club!!<h1> <script type='text/javascript'> var xmlhttp = new XMLHttpRequest() xmlhttp.open("GET", "http://be.wahaha.club/index.html", true); xmlhttp.send(); </script> </body> </html>
打开浏览器访问 fe.wahaha.club/index.html
的结果会跨域:
很明显这里是跨域请求,在浏览器中直接访问 http://be.wahaha.club/index.html
是可以访问到的,但是在 fe.wahaha.club
的 html 页面访问就会出现跨域。
在 /etc/nginx/conf.d/
文件夹中新建一个配置文件,对应二级域名 be.wahaha.club
:
# /etc/nginx/conf.d/be.wahaha.club.conf server { listen 80; server_name be.wahaha.club; add_header 'Access-Control-Allow-Origin' $http_origin; # 全局变量获得当前请求origin,带cookie的请求不支持* add_header 'Access-Control-Allow-Credentials' 'true'; # 为 true 可带上 cookie add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # 允许请求方法 add_header 'Access-Control-Allow-Headers' $http_access_control_request_headers; # 允许请求的 header,可以为 * add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; if ($request_method = 'OPTIONS') { add_header 'Access-Control-Max-Age' 1728000; # OPTIONS 请求的有效期,在有效期内不用发出另一条预检请求 add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; # 200 也可以 } location / { root /usr/share/nginx/html/be; index index.html; } }
然后 nginx -s reload
重新加载配置。这时再访问 fe.wahaha.club/index.html
结果如下,请求中出现了我们刚刚配置的 Header:
解决了跨域问题。
7. 开启gzip压缩
gzip 是一种常用的网页压缩技术,传输的网页经过 gzip 压缩之后大小通常可以变为原来的一半甚至更小(官网原话),更小的网页体积也就意味着带宽的节约和传输速度的提升,特别是对于访问量巨大大型网站来说,每一个静态资源体积的减小,都会带来可观的流量与带宽的节省。 百度可以找到很多检测站点来查看目标网页有没有开启 gzip 压缩,在下随便找了一个 <网页GZIP压缩检测> 输入掘金想要查询的网址
这里可以看到结果是否开启了 gzip 的,压缩率,例如压缩率达到了 52% ,本来 34kb
的网页体积,压缩完只需要 16kb
,可以想象网页传输速度提升了不少。
7.1 nginx配置gzip
使用 gzip 不仅需要 Nginx 配置,浏览器端也需要配合,需要在请求消息头中包含Accept-Encoding: gzip
(IE5 之后所有的浏览器都支持了,是现代浏览器的默认设置)。一般在请求 html 和 css 等静态资源的时候,支持的浏览器在 request 请求静态资源的时候,会加上 Accept-Encoding: gzip
这个 header,表示自己支持 gzip 的压缩方式,Nginx 在拿到这个请求的时候,如果有相应配置,就会返回经过 gzip 压缩过的文件给浏览器,并在 response 相应的时候加上 content-encoding: gzip
来告诉浏览器自己采用的压缩方式(因为浏览器在传给服务器的时候一般还告诉服务器自己支持好几种压缩方式),浏览器拿到压缩的文件后,根据自己的解压方式进行解析。
先来看看 Nginx 怎么进行 gzip 配置,和之前的配置一样,为了方便管理,还是在 /etc/nginx/conf.d/
文件夹中新建配置文件 gzip.conf
:
# /etc/nginx/conf.d/gzip.conf gzip on; # 默认off,是否开启gzip gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; # 上面两个开启基本就能跑起了,下面的愿意折腾就了解一下 gzip_static on; gzip_proxied any; gzip_vary on; gzip_comp_level 6; gzip_buffers 16 8k; # gzip_min_length 1k; gzip_http_version 1.1;
稍微解释一下:
- gzip_types:要采用 gzip 压缩的 MIME 文件类型,其中 text/html 被系统强制启用;
-
gzip_static:默认 off,该模块启用后,Nginx 首先检查是否存在请求静态文件的 gz 结尾的文件,如果有则直接返回该
.gz
文件内容; - gzip_proxied:默认 off,nginx做为反向代理时启用,用于设置启用或禁用从代理服务器上收到相应内容 gzip 压缩;
-
gzip_vary:用于在响应消息头中添加
Vary:Accept-Encoding
,使代理服务器根据请求头中的Accept-Encoding
识别是否启用 gzip 压缩; - gzip_comp_level:gzip 压缩比,压缩级别是 1-9,1 压缩级别最低,9 最高,级别越高压缩率越大,压缩时间越长,建议 4-6;
- gzip_buffers:获取多少内存用于缓存压缩结果,16 8k 表示以 8k*16 为单位获得;
-
gzip_min_length:允许压缩的页面最小字节数,页面字节数从header头中的
Content-Length
中进行获取。默认值是 0,不管页面多大都压缩。建议设置成大于 1k 的字节数,小于 1k 可能会越压越大; - gzip_http_version:默认 1.1,启用 gzip 所需的 HTTP 最低版本;
这个配置可以插入到 http 模块整个服务器的配置里,也可以插入到需要使用的虚拟主机的 server
或者下面的 location
模块中,当然像上面我们这样写的话就是被 include 到 http 模块中了。
其他更全的配置信息可以查看 <官网文档ngx_http_gzip_module>,配置前是这样的:
配置之后 response 的 header 里面多了一个 Content-Encoding: gzip
,返回信息被压缩了:
注意了,一般 gzip 的配置建议加上 gzip_min_length 1k
,不加的话:
检测结果:
由于文件太小,gzip 压缩之后得到了 -48% 的体积优化,压缩之后体积还比压缩之前体积大了,所以最好设置低于 1kb
的文件就不要 gzip 压缩了
7.2 webpack的gzip设置
当前端项目使用 Webpack 进行打包的时候,也可以开启 gzip 压缩:
// vue-cli3 的 vue.config.js 文件 const CompressionWebpackPlugin = require('compression-webpack-plugin') module.exports = { // gzip 配置 configureWebpack: config => { if (process.env.NODE_ENV === 'production') { // 生产环境 return { plugins: [new CompressionWebpackPlugin({ test: /\.js$|\.html$|\.css/, // 匹配文件名 threshold: 10240, // 文件压缩阈值,对超过10k的进行压缩 deleteOriginalAssets: false // 是否删除源文件 })] } } }, ... }
由此打包出来的文件如下图:
这里可以看到某些打包之后的文件下面有一个对应的 .gz
经过 gzip
压缩之后的文件,这是因为这个文件超过了 10kb
,有的文件没有超过 10kb
就没有进行 gzip
打包,如果你期望压缩文件的体积阈值小一点,可以在 compression-webpack-plugin
这个插件的配置里进行对应配置。
那么为啥这里 Nginx 已经有了 gzip 压缩,Webpack 这里又整了个 gzip 呢,因为如果全都是使用 Nginx 来压缩文件,会耗费服务器的计算资源,如果服务器的 gzip_comp_level
配置的比较高,就更增加服务器的开销,相应增加客户端的请求时间,得不偿失。
如果压缩的动作在前端打包的时候就做了,把打包之后的高压缩等级文件作为静态资源放在服务器上,Nginx 会优先查找这些压缩之后的文件返回给客户端,相当于把压缩文件的动作从 Nginx 提前给 Webpack 打包的时候完成,节约了服务器资源,所以一般推介在生产环境应用 Webpack 配置 gzip 压缩。
8. 负载均衡配置
主要思想就是把负载均匀合理地分发到多个服务器上,实现压力分流的目的。 主要配置如下:http { upstream myserver { # ip_hash; # ip_hash 方式 # fair; # fair 方式 server 127.0.0.1:8081; # 负载均衡目的服务地址 server 127.0.0.1:8080; server 127.0.0.1:8082 weight=10; # weight 方式,不写默认为 1 } server { location / { proxy_pass http://myserver; proxy_connect_timeout 10; } } }
Nginx 提供了好几种分配方式,默认为轮询,就是轮流来。有以下几种分配方式:
- 轮询,默认方式,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务挂了,能自动剔除;
- weight,权重分配,指定轮询几率,权重越高,在被访问的概率越大,用于后端服务器性能不均的情况;
- ip_hash,每个请求按访问 IP 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决动态网页 session 共享问题。负载均衡每次请求都会重新定位到服务器集群中的某一个,那么已经登录某一个服务器的用户再重新定位到另一个服务器,其登录信息将会丢失,这样显然是不妥的;
- fair(第三方),按后端服务器的响应时间分配,响应时间短的优先分配,依赖第三方插件 nginx-upstream-fair,需要先安装;
9. 配置动静分离
动静分离在之前也介绍过了,就是把动态和静态的请求分开。方式主要有两种,一种 是纯粹把静态文件独立成单独的域名,放在独立的服务器上,也是目前主流推崇的方案。另外一种方法就是动态跟静态文件混合在一起发布, 通过 Nginx 配置来分开。 通过 location 指定不同的后缀名实现不同的请求转发。通过 expires 参数设置,可以使浏览器缓存过期时间,减少与服务器之前的请求和流量。具体 expires 定义:是给一个资源设定一个过期时间,也就是说无需去服务端验证,直接通过浏览器自身确认是否过期即可,所以不会产生额外的流量。此种方法非常适合不经常变动的资源。(如果经常更新的文件,不建议使用 expires 来缓存),我这里设置 3d,表示在这 3 天之内访问这个URL,发送一个请求,比对服务器该文件最后更新时间没有变化。则不会从服务器抓取,返回状态码 304,如果有修改,则直接从服务器重新下载,返回状态码 200。server { location /www/ { root /data/; index index.html index.htm; } location /image/ { root /data/; autoindex on; } }
10.配置高可用集群(双机热备)
当主 Nginx 服务器宕机之后,切换到备份 Nginx 服务器
首先安装 keepalived:yum install keepalived -y 然后编辑
/etc/keepalived/keepalived.conf
配置文件,并在配置文件中增加 vrrp_script
定义一个外围检测机制,并在 vrrp_instance
中通过定义 track_script
来追踪脚本执行过程,实现节点转移:
global_defs{ notification_email { acassen@firewall.loc } notification_email_from Alexandre@firewall.loc smtp_server 127.0.0.1 smtp_connect_timeout 30 // 上面都是邮件配置,没卵用 router_id LVS_DEVEL // 当前服务器名字,用hostname命令来查看 } vrrp_script chk_maintainace { // 检测机制的脚本名称为chk_maintainace script "[[ -e/etc/keepalived/down ]] && exit 1 || exit 0" // 可以是脚本路径或脚本命令 // script "/etc/keepalived/nginx_check.sh" // 比如这样的脚本路径 interval 2 // 每隔2秒检测一次 weight -20 // 当脚本执行成立,那么把当前服务器优先级改为-20 } vrrp_instanceVI_1 { // 每一个vrrp_instance就是定义一个虚拟路由器 state MASTER // 主机为MASTER,备用机为BACKUP interface eth0 // 网卡名字,可以从ifconfig中查找 virtual_router_id 51 // 虚拟路由的id号,一般小于255,主备机id需要一样 priority 100 // 优先级,master的优先级比backup的大 advert_int 1 // 默认心跳间隔 authentication { // 认证机制 auth_type PASS auth_pass 1111 // 密码 } virtual_ipaddress { // 虚拟地址vip 172.16.2.8 } }
其中检测脚本 nginx_check.sh
,这里提供一个:
#!/bin/bash A=`ps -C nginx --no-header | wc -l` if [ $A -eq 0 ];then /usr/sbin/nginx # 尝试重新启动nginx sleep 2 # 睡眠2秒 if [ `ps -C nginx --no-header | wc -l` -eq 0 ];then killall keepalived # 启动失败,将keepalived服务杀死。将vip漂移到其它备份节点 fi fi
复制一份到备份服务器,备份 Nginx 的配置要将 state
后改为 BACKUP
,priority
改为比主机小。
设置完毕后各自 service keepalived start
启动,经过访问成功之后,可以把 Master 机的 keepalived 停掉,此时 Master 机就不再是主机了 service keepalived stop
,看访问虚拟 IP 时是否能够自动切换到备机 ip addr
。
再次启动 Master 的 keepalived,此时 vip 又变到了主机上。
11. 适配pc和移动设备
根据用户设备不同返回不同样式的站点,以前经常使用的是纯前端的自适应布局,但无论是复杂性和易用性上面还是不如分开编写的好,比如我们常见的淘宝、京东......这些大型网站就都没有采用自适应,而是用分开制作的方式,根据用户请求的 user-agent
来判断是返回 PC 还是 H5 站点。
首先在 /usr/share/nginx/html
文件夹下 mkdir
分别新建两个文件夹 PC
和 mobile
,vim
编辑两个 index.html
随便写点内容。
cd /usr/share/nginx/html mkdir pc mobile cd pc vim index.html # 随便写点比如 hello pc! cd ../mobile vim index.html # 随便写点比如 hello mobile!
然后和设置二级域名虚拟主机时候一样,去 /etc/nginx/conf.d
文件夹下新建一个配置文件 fe.wahaha.club.conf
:
# /etc/nginx/conf.d/fe.wahaha.club.conf server { listen 80; server_name fe.wahaha.club; location / { root /usr/share/nginx/html/pc; if ($http_user_agent ~* '(Android|webOS|iPhone|iPod|BlackBerry)') { root /usr/share/nginx/html/mobile; } index index.html; } }
配置基本没什么不一样的,主要多了一个 if
语句,然后使用 $http_user_agent
全局变量来判断用户请求的 user-agent
,指向不同的 root 路径,返回对应站点。
12. 配置https
具体配置过程网上挺多的了,也可以使用你购买的某某云,一般都会有免费申请的服务器证书,安装直接看所在云的操作指南即可。 我购买的阿里云提供的亚洲诚信机构颁发的免费证书只能一个域名使用,二级域名什么的需要另外申请,但是申请审批比较快,一般几分钟就能成功,然后下载证书的压缩文件,里面有个 nginx 文件夹,把xxx.crt
和 xxx.key
文件拷贝到服务器目录,再配置下:
server { listen 443 ssl http2 default_server; # SSL 访问端口号为 443 server_name sherlocked93.club; # 填写绑定证书的域名 ssl_certificate /etc/nginx/https/1_wahaha.club_bundle.crt; # 证书文件地址 ssl_certificate_key /etc/nginx/https/2_wahaha.club.key; # 私钥文件地址 ssl_session_timeout 10m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #请按照以下协议配置 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; ssl_prefer_server_ciphers on; location / { root /usr/share/nginx/html; index index.html index.htm; } }
写完 nginx -t -q
校验一下,没问题就 nginx -s reload
,现在去访问 https://wahaha.club/
就能访问 HTTPS 版的网站了。
一般还可以加上几个增强安全性的命令:
add_header X-Frame-Options DENY; # 减少点击劫持 add_header X-Content-Type-Options nosniff; # 禁止服务器自动解析资源类型 add_header X-Xss-Protection 1; # 防XSS攻击
13. 一些常用技巧
13.1 静态服务
server { listen 80; server_name static.wahaha.club; charset utf-8; # 防止中文文件名乱码 location /download { alias /usr/share/nginx/html/static; # 静态资源目录 autoindex on; # 开启静态资源列目录 autoindex_exact_size off; # on(默认)显示文件的确切大小,单位是byte;off显示文件大概大小,单位KB、MB、GB autoindex_localtime off; # off(默认)时显示的文件时间为GMT时间;on显示的文件时间为服务器时间 } }
13.2 图片防盗链
server { listen 80; server_name *.wahaha.club; # 图片防盗链 location ~* \.(gif|jpg|jpeg|png|bmp|swf)$ { valid_referers none blocked server_names ~\.google\. ~\.baidu\. *.qq.com; # 只允许本机 IP 外链引用,感谢 @木法传 的提醒,将百度和谷歌也加入白名单 if ($invalid_referer){ return 403; } } }
13.3 请求过滤
# 非指定请求全返回 403 if ( $request_method !~ ^(GET|POST|HEAD)$ ) { return 403; } location / { # IP访问限制(只允许IP是 192.168.0.2 机器访问) allow 192.168.0.2; deny all; root html; index index.html index.htm; }
13.4 配置图片、字体等静态文件缓存
由于图片、字体、音频、视频等静态文件在打包的时候通常会增加了 hash,所以缓存可以设置的长一点,先设置强制缓存,再设置协商缓存;如果存在没有 hash 值的静态文件,建议不设置强制缓存,仅通过协商缓存判断是否需要使用缓存。
# 图片缓存时间设置 location ~ .*\.(css|js|jpg|png|gif|swf|woff|woff2|eot|svg|ttf|otf|mp3|m4a|aac|txt)$ { expires 10d; } # 如果不希望缓存 expires -1;
13.5 单页面项目 history 路由配置
server { listen 80; server_name fe.wahaha.club; location / { root /usr/share/nginx/html/dist; # vue 打包后的文件夹 index index.html index.htm; try_files $uri $uri/ /index.html @rewrites; expires -1; # 首页一般没有强制缓存 add_header Cache-Control no-cache; } # 接口转发,如果需要的话 #location ~ ^/api { # proxy_pass http://be.wahaha.club; #} location @rewrites { rewrite ^(.+)$ /index.html break; } }
13.6 HTTP 请求转发到 HTTPS
配置完 HTTPS 后,浏览器还是可以访问 HTTP 的地址 http://wahaha.club/
的,可以做一个 301 跳转,把对应域名的 HTTP 请求重定向到 HTTPS 上
server { listen 80; server_name www.wahaha.club; # 单域名重定向 if ($host = 'www.wahaha.club'){ return 301 https://www.wahaha.club$request_uri; } # 全局非 https 协议时重定向 if ($scheme != 'https') { return 301 https://$server_name$request_uri; } # 或者全部重定向 return 301 https://$server_name$request_uri; # 以上配置选择自己需要的即可,不用全部加 }
13.7 泛域名路径分离
这是一个非常实用的技能,经常有时候我们可能需要配置一些二级或者三级域名,希望通过 Nginx 自动指向对应目录,比如:
-
test1.doc.wahaha.club
自动指向/usr/share/nginx/html/doc/test1
服务器地址; -
test2.doc.wahaha.club
自动指向/usr/share/nginx/html/doc/test2
服务器地址;server { listen 80; server_name ~^([\w-]+)\.doc\.sherlocked93\.club$; root /usr/share/nginx/html/doc/$1; }
13.8 泛域名转发
和之前的功能类似,有时候我们希望把二级或者三级域名链接重写到我们希望的路径,让后端就可以根据路由解析不同的规则:
-
test1.serv.wahaha.club/api?name=a
自动转发到127.0.0.1:8080/test1/api?name=a
; -
test2.serv.wahaha.club/api?name=a
自动转发到127.0.0.1:8080/test2/api?name=a
;server { listen 80; server_name ~^([\w-]+)\.serv\.wahaha\.club$; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_pass http://127.0.0.1:8080/$1$request_uri; } }
14. 最佳实践
- 为了使 Nginx 配置更易于维护,建议为每个服务创建一个单独的配置文件,存储在
/etc/nginx/conf.d
目录,根据需求可以创建任意多个独立的配置文件。 - 独立的配置文件,建议遵循以下命名约定
<服务>.conf
,比如域名是wahaha.club
,那么你的配置文件的应该是这样的/etc/nginx/conf.d/wahaha.club.conf
,如果部署多个服务,也可以在文件名中加上 Nginx 转发的端口号,比如 wahaha.club.8080.conf
,如果是二级域名,建议也都加上fe.wahaha.club.conf
。 - 常用的、复用频率比较高的配置可以放到
/etc/nginx/snippets
文件夹,在 Nginx 的配置文件中需要用到的位置 include 进去,以功能来命名,并在每个 snippet 配置文件的开头注释标明主要功能和引入位置,方便管理。比如之前的gzip
、cors
等常用配置,我都设置了 snippet。 - Nginx 日志相关目录,内以
域名.type.log
命名(比如be.wahaha.club.access.log
和be.wahaha.club.error.log
)位于/var/log/nginx/
目录中,为每个独立的服务配置不同的访问权限和错误日志文件,这样查找错误时,会更加方便快捷。
------------恢复内容结束------------