# Nginx ## 基础篇 ### Nginx服务器启停命令 #### Nginx服务的信号控制 1. Nginx中的master和worker进程? worker负责处理连接,master负责管理worker。 2. Nginx的工作方式? 多进程架构,一个master进程和多个worker进程,worker负责处理连接,master负责管理worker。 3. 如何获取进程的PID? `ps -ef | grep nginx` 4. 信号有哪些? | 信号 | 描述 | | -------- | ----------------------------------- | | TERM/INT | 立即关闭Nginx服务 | | QUIT | 优雅地关闭Nginx服务 | | HUP | 重新读取配置文件,使服务对新配置项生效 | | USR1 | 重新打开日志文件,用于日志切割 | | USR2 | 平滑升级到最新版本的Nginx | | WINCH | 所有子进程不再接收处理新连接,等同于给worker进程发送QUIT指令 | 5. 如何通过信号控制Nginx的启停等操作? `kill -signal PID`:signal即为信号,如TERM、QUIT、WINCH等。PID即为master进程ID。 ```shell kill -TERM PID kill -QUIT PID ``` #### Nginx的命令行控制 ```shell ./nginx ./nginx -s stop ./nginx -s reload ./nginx -t ``` ### Nginx服务器版本升级 > https://www.bilibili.com/video/BV1ov41187bq?p=20 版本升级就是将新版本的nginx可执行文件,拷贝旧版本中,然后使用命令在启动一个nginx进程,保留原进程,两者都能对外提供服务,最后将旧nginx服务关闭即可。即可完成版本升级 #### Nginx核心配置文件结构 - 全局块:events和http之外的配置。主要设置Nginx服务器运行的配置指令。 - events:nginx服务与用户的网络连接的配置。 - http:代理、缓存、日志、第三方模块配置... ```conf worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } } ``` 更多指令可以参考`https://www.bilibili.com/video/BV1ov41187bq?p=22` ## 进阶篇 当server_name被匹配多次时的执行顺序: 1. 精确匹配server_name。 2. 通配符在开始时的匹配server_name。 3. 通配符在结束时的匹配server_name。 4. 正则匹配server_name。 5. default_server,若没有指定default_server则默认找第一个server。 ### 静态资源优化配置 #### sendfile 可以理解为零拷贝。 - 语法:sendfile on | off - 默认值:sendfile off - 可配置的位置:http、server、location 一般配置到http块即可。 #### tcp_nopush - 语法:tcp_nopush on | off - 默认值:tcp_nopush off - 可配置的位置:http、server、location 提升网络包的传输效率。该值需要在sendfile开启的状态才会生效。 #### tcp_nodeplay - 语法:tcp_nodelay on | off - 默认值:tcp_nodelay on - 可配置的位置:http、server、location 提升网络包传输的实时性。该值需要在keep-alive开启的情况下才会生效。 ### 静态资源压缩 > 具体参数配置参考:https://www.bilibili.com/video/BV1ov41187bq?p=51 语法:gzip on | off 根据响应页类型开启gzip压缩功能:`gzip_types application/javascript text/html` 一般`gzip_comp_level 6;`压缩级别设置为6即可。 Gzip压缩功能配置模板 ```conf gzip on; # 开启 Gzip 功能 gzip_types *; # 压缩源文件类型,根据具体的访问资源类型设定 gzip_comp_level 6; # Gzip 压缩级别 gzip_min_length 1k; # 进行压缩响应页面的最小长度,content-length gzip_buffers 4 16K; # 缓存空间大小 gzip_http_version 1.1; # 指定压缩响应所需要的最低 HTTP 请求版本 gzip_vary on; # 往头信息中添加压缩标识 gzip_disable "MSIE [1-6]\."; # 对 IE6 以下的版本都不进行压缩 gzip_proxied off; # Nginx 作为反向代理压缩服务端返回数据的条件 ``` Gzip和sendfile共存问题: sendfile不需要经过用户缓冲区,而开启Gzip后,需要在用户缓冲区中进行压缩,那么两者就互斥了。 解决方案:提前将资源压缩好,等待获取静态资源时将压缩文件发送即可。关闭gzip,手动压缩。 指令:gzip_static on | off | always,默认off。 gzip_static在 ngx_http_gzip_static_module 模块下。需要添加模块。 ### 静态资源缓存配置 Expires 缓存过期的日期和时间 ```conf location ~ .*\.(html|js|css|png|jpg|jpeg|gif)$ { # ... expires max # ... } #expires 30s; # 表示把数据缓存 30 秒 #expires 30m; # 表示把数据缓存 30 分 #expires 10h; # 表示把数据缓存 10 小时 #expires 1d; # 表示把数据缓存 1 天 ``` Cache-Control 设置和缓存相关的配置信息 ```conf add_header Cache-control must-revalidate; # 可缓存但必须再向源服务器进行确认 add_header Cache-control no-cache; # 数据内容不能被缓存,每次请求都重新访问服务器,若有 max-age,则缓存期间不访问服务器 add_header Cache-control no-store; # 不缓存请求或响应的任何内容,暂存也不可以(临时文件夹中不能暂存该资源) add_header Cache-control no-transform; # 代理不可更改媒体类型 add_header Cache-control public; # 可以被任何缓存区缓存,如: 浏览器、服务器、代理服务器等 add_header Cache-control private; # 只能在浏览器中缓存,只有在第一次请求的时候才访问服务器,若有 max-age,则缓存期间不访问服务器 add_header Cache-control proxy-revalidate; # 要求中间缓存服务器对缓存的响应有效性再进行确认 add_header Cache-Control max-age=; # 秒, 过期时间,即以秒为单位的缓存时间 add_header Cache-control s-maxage=; # 秒, 公共缓存服务器响应的最大 Age 值 ``` ### Nginx跨域 ```conf location /getUser { # Access-Control-Allow-Origin:允许跨域访问的源地址信息,可以配置多个(多个用逗号分隔),也可以使用 * 代表所有源。 add_header Access-Control-Allow-Origin *; # Access-Control-Allow-Methods:允许跨域访问的请求方式,值可以为 GET、POST、PUT、DELETE ......,可以全部设置,也可以根据需要设置,多个用逗号分隔。 add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE; default_type application/json; # return 的格式是 json return 200 '{"id":1,"name":"TOM","age":18}'; } ``` ### Nginx防盗链 当浏览器向 Web 服务器发送请求的时候,一般都会带上 Referer,来告诉浏览器该网页是从哪个页面链接过来的。后台服务器可以根据获取到的这个 Referer 信息来判断是否为自己信任的网站地址,如果是则放行继续访问,如果不是则可以返回 403(服务端拒绝访问)的状态信息。 ```conf location ~ *\.(png|jpg|gif){ valid_referers none blocked www.baidu.com 192.168.91.200; # valid_referers none blocked *.example.com example.* www.example.org ~\.google\.; if ($invalid_referer){ return 403; } root /usr/local/nginx/html; } # 针对目录防盗链 location /images { valid_referers none blocked www.baidu.com 192.168.199.27; # valid_referers none blocked *.example.com example.* www.example.org ~\.google\.; if ($invalid_referer){ return 403; } root /usr/local/nginx/html; } ``` 上方代码如果没有匹配上 www.baidu.com 和 192.168.91.200,则 $invalid_referer 为 1(true),返回 403,代表不允许获取资源。 但Referer 的限制比较粗,比如浏览器发送请求时恶意加一个 Referer,上面的方式是无法进行限制的。 ### Rewrite > 更多配置可参考:https://www.bilibili.com/video/BV1ov41187bq?p=70 Rewrite相关命令: - set:该指令用来设置一个新的变量。 - 语法:`set <$key> ;` ```conf server { listen 8081; server_name localhost; location /server { set $name TOM; set $age 18; default_type text/plain; return 200 $name=$age; } } ``` - if:该指令用来支持条件判断,并根据条件判断结果选择不同的 Nginx 配置。 ```conf if ($request_method = POST){ return 405; } ``` - break:该指令用于中断当前相同作用域中的其他 Nginx 配置。终止当前的匹配并把当前的URI在本location进行重定向访问处理。 - return:该指令用于完成对请求的处理,直接向客户端返回响应状态代码。在 return 后的所有 Nginx 配置都是无效的。 - 语法: ```conf return [text]; return ; return ; code:返回给客户端的 HTTP 状态代理。可以返回的状态代码为 0 ~ 999 的任意 HTTP 状态代理 text:返回给客户端的响应体内容,支持变量的使用和 JSON 字符串 URL:跳转给客户端的 URL 地址。 ``` - rewrite:该指令通过正则表达式的使用来改变 URI。可以同时存在一个或者多个指令,按照顺序依次对 URL 进行匹配和处理。 - 语法:`rewrite regex replacement [flag];` - regex:用来匹配 URI 的正则表达式。 - replacement:匹配成功后,用于替换 URI 中被截取内容的字符串。如果该字符串是以 『 http:// 』或者『 https:// 』开头的,则不会继续向下对URI 进行其他处理,而是直接返回重写后的 URI 给客户端。 - flag:用来设置 Rewrite 对 URI 的处理行为。 ```conf # ...... listen 8081; location /rewrite { rewrite ^/rewrite/url\w*$ https://www.baidu.com; # (括号的值会作为 $1 的值) ^代表匹配输入字符串的起始位置 rewrite ^/rewrite/(test)\w*$ /$1; # 如果是 /rewrite/testxxx,则重写 url 为 test rewrite ^/rewrite/(demo)\w*$ /$1; # 如果是 /rewrite/demoxxx,则重写 url 为 demo } location /test { # 重写后的 url 如果为 test,触发 location default_type text/plain; return 200 test_sucess; } location /demo { # 重写后的 url 如果为 demo,触发 location default_type text/plain; return 200 demo_sucess; } ``` - rewrite_log:该指令配置是否开启 URL 重写日志的输出功能,默认关闭。开启后,URL 重写的相关日志将以 notice 级别输出到 error_log 指令配置的日志文件汇总。 ```conf location /rewrite_log { rewrite_log on; # 开启重写日志 error_log logs /error.log notice; # 切换为 notice 模式,因为只支持这个模式 return 200 '开启了重写日志'; } ``` Rewrite的应用场景: - 域名跳转 - 域名镜像 - 独立域名 - 目录自动添加`/` ```conf server { listen 80; server_name localhost; server_name_in_redirect on; location /frx { if (-d $request_filename){ # 如果请求的资源目录存在 rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent; # $2 获取第二个括号的值:/ } } } ``` - 合并目录 - 防盗链 ```conf # 基于文件类型实现防盗链配置 server{ listen 80; server_name www.web.com; locatin ~* ^.+\.(gif|jpg|png|swf|flv|rar|zip)$ { valid_referers none blocked server_names *.web.com; # server_names 后指定具体的域名或者 IP if ($invalid_referer){ rewrite ^/ http://www.web.com/images/forbidden.png; # 跳转到默认地址 } } } # 基于目录实现防盗链配置 server{ listen 80; server_name www.web.com; location /file { root /server/file; # 资源在 server 目录下的 file 目录里 valid_referers none blocked server_names *.web.com; # server_names 后指定具体的域名或者 IP if ($invalid_referer){ rewrite ^/ http://www.web.com/images/forbidden.png; # 跳转到 file 目录下的图片 } } } ``` - 多级域名 重写和转发的区别: - 地址重写浏览器地址会发生变化而地址转发则不变 - 一次地址重写会产生两次请求而一次地址转发只会产生一次请求 - 地址重写到的页面必须是一个完整的路径而地址转发则不需要 - 地址重写因为是两次请求,所以 request 范围内属性不能传递给新页面,而地址转发因为是一次请求所以可以传递值 - 地址转发速度快于地址重写 #### Rewrite常用全局变量 | 变量 | 说明 | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------- | | $args | 变量中存放了请求 URL 中的请求指令。比如 `http://192.168.200.133:8080?arg1=value1&args2=value2` 中的『 arg1=value1&arg2=value2 』,功能和 $query_string 一样 | | $http_user_agent | 变量存储的是用户访问服务的代理信息(如果通过浏览器访问,记录的是浏览器的相关版本信息) | | $host | 变量存储的是访问服务器的 server_name 值 | | $document_uri | 变量存储的是当前访问地址的URI。比如 `http://192.168.200.133/server?id=10&name=zhangsan`中的『 /server 』,功能和 $uri 一样 | | $document_root | 变量存储的是当前请求对应 location 的 root 值,如果未设置,默认指向 Nginx 自带 html 目录所在位置 | | $content_length | 变量存储的是请求头中的 Content-Length 的值 | | $content_type | 变量存储的是请求头中的 Content-Type 的值 | | $http_cookie | 变量存储的是客户端的 cookie 信息,可以通过 add_header Set-Cookie 'cookieName=cookieValue' 来添加 cookie 数据 | | $limit_rate | 变量中存储的是 Nginx 服务器对网络连接速率的限制,也就是 Nginx 配置中对 limit_rate 指令设置的值,默认是 0,不限制。 | | $remote_addr | 变量中存储的是客户端的 IP 地址 | | $remote_port | 变量中存储了客户端与服务端建立连接的端口号 | | $remote_user | 变量中存储了客户端的用户名,需要有认证模块才能获取 | | $scheme | 变量中存储了访问协议 | | $server_addr | 变量中存储了服务端的地址 | | $server_name | 变量中存储了客户端请求到达的服务器的名称 | | $server_port | 变量中存储了客户端请求到达服务器的端口号 | | $server_protocol | 变量中存储了客户端请求协议的版本,比如 『 HTTP/1.1 』 | | $request_body_file | 变量中存储了发给后端服务器的本地文件资源的名称 | | $request_method | 变量中存储了客户端的请求方式,比如『 GET 』,『 POST 』等 | | $request_filename | 变量中存储了当前请求的资源文件的路径名 | | $request_uri | 变量中存储了当前请求的 URI,并且携带请求参数,比如 `http://192.168.200.133/server?id=10&name=zhangsan` 中的 『 /server?id=10&name=zhangsan 』 | ### 访问限流 ```conf # 限制用户连接数来预防 DOS 攻击 limit_conn_zone $binary_remote_addr zone=perip:10m; limit_conn_zone $server_name zone=perserver:10m; # 限制同一客户端 ip 最大并发连接数 limit_conn perip 2; # 限制同一server最大并发连接数 limit_conn perserver 20; # 限制下载速度,根据自身服务器带宽配置 limit_rate 300k; ``` ### 链接超时 ```conf # 客户端、服务端设置 server_names_hash_bucket_size 128; server_names_hash_max_size 512; # 长连接超时配置 keepalive_timeout 65; client_header_timeout 15s; client_body_timeout 15s; send_timeout 60s; # 代理设置 # 与后端服务器建立连接的超时时间。注意这个一般不能大于 75 秒 proxy_connect_timeout 30s; proxy_send_timeout 120s; # 从后端服务器读取响应的超时 proxy_read_timeout 120s; ``` ### 反向代理 - 正向代理:代理的是客户端。 - 反向代理:代理的是服务端。 反向代理中常用的指令: - proxy_pass:配置代理的服务器地址。 - 语法:`proxy_pass ;` ```conf # 例子 location /server { # 结尾不加斜杠 proxy_pass http://192.168.200.146; # 访问的是:http://192.168.200.146/server/index.html # 结尾加斜杠 proxy_pass http://192.168.200.146/; # 访问的是:http://192.168.200.146/index.html } ``` - proxy_set_header:转发给被代理服务器时,设置一些请求头信息。 ```conf # 目标服务器 server { listen 8080; server_name localhost; default_type text/plain; return 200 $http_username; # 获取代理服务器发送过来的 http 请求头的 username 值 } # 代理服务器 server { listen 8080; server_name localhost; location /server { # 访问 /server 触发代理 proxy_pass http://192.168.200.146:8080/; # 配置服务器 B 的地址 proxy_set_header username TOM; # 发送 key 为 username,value 为 TOM 的请求头给服务器 B } } ``` - proxy_redirect:防止客户端可以看到被代理服务器的地址。 - 语法: ```conf # redirect:被代理服务器返回的 Location 值 # replacement:要替换 Location 的值 proxy_redirect redirect replacement; proxy_redirect default; proxy_redirect off; ``` - 为什么要使用`proxy_redirect`指令? - 避免将真实的目标地址暴露给客户端。该指令是用来重置头信息中的『 Location 』和『 Refresh 』的值,防止客户端可以看到被代理服务器的地址。 ```conf # 目标服务器 server { listen 8081; server_name localhost; if (!-f $request_filename){ return 302 http://192.168.200.146; # 2.如果请求的资源不存在,则重定向到服务器 B } } # 代理服务器 server { listen 8081; server_name localhost; location / { proxy_pass http://192.168.200.146:8081/; # 1.转发给服务器 B proxy_redirect http://192.168.200.146 http://192.168.200.133; # 3.修改服务器 B 的地址 } } # 该 server 去请求服务器 B 的欢迎页面 server { listen 80; server_name 192.168.200.133; location / { proxy_pass http://192.168.200.146; # 4.重新发送请求给服务器 B,获取欢迎页面 } } ``` 思考:在编写 proxy_pass 的时候,后面的值要不要加`/`。 ```conf server { listen 80; server_name localhost; location / { # 下面两个地址加不加斜杠,效果都一样,因为 location 后的 / 会添加在代理地址后面 proxy_pass http://192.168.200.146; proxy_pass http://192.168.200.146/; } } server{ listen 80; server_name localhost; location /server { # 下面两个地址必须加斜杠,因为 location 后的 /server 会添加在代理地址后面,第一个将没有 / 结尾 #proxy_pass http://192.168.200.146; proxy_pass http://192.168.200.146/; } } # 上面的 location /server:当客户端访问 http://localhost/server/index.html # 第一个 proxy_pass 就变成了 http://localhost/server/index.html # 第二个 proxy_pass 就变成了 http://localhost/index.html 效果就不一样了。 ``` 如果不以 / 结尾,则 location 后的 /server 会添加在地址后面,所以第一个 proxy_pass 因为没有 / 结尾而被加上 /server,而第二个自带了 / ,所以不会添加 /server。 一般来说:访问任意请求如 /server 时,想要代理到其他服务器的首页,则加 /,否则你如果真的想访问 /server 下的资源,那么不要加 /。所以加了 / 后,请求的是服务器根目录下的资源。 > 斜杠总结参考:https://frxcat.fun/middleware/Nginx/Nginx_Reverse_proxy/#%E6%96%9C%E6%9D%A0%E6%80%BB%E7%BB%93 #### Nginx添加SSL的支持 SSL的支持需要添加`--with-http_ssl_module`模块。ssl指令用来在指定的服务器开启HTTPS,默认关闭。如需开始则使用配置`listen 443 ssl; 或者 ssl on | off;`。 HTTPS 是一种通过计算机网络进行安全通信的传输协议。它经由 HTTP 进行通信,利用 SSL/TLS 建立全通信,加密数据包,确保数据的安全性。 ##### SSL相关指令 - `ssl_certificate`:指令是为当前这个虚拟主机指定一个带有 PEM 格式证书的证书。 - 语法:`ssl_certificate ;` - `ssl_certificate_key`:指令用来指定 PEM secret key 文件的路径。 - 语法:`ssl_ceritificate_key ;` - `ssl_session_cache`:指令用来配置用于 SSL 会话的缓存。 - 语法:`ssl_sesion_cache ` - off:严格禁止使用会话缓存:Nginx 明确告诉客户端会话不能被重用。 - none:禁止使用会话缓存,Nginx 告诉客户端会话可以被重用,但实际上并不在缓存中存储会话参数。默认值。 - builtin:内置 OpenSSL 缓存,仅在一个工作进程中使用。缓存大小在会话中指定。如果未给出大小,则等于 20480 个会话。使用内置缓存可能会导致内存碎片。 - shared:所有工作进程之间共享缓存,缓存的相关信息用 name 和 size 来指定,同 name 的缓存可用于多个虚拟服务器。name 是允许缓存的数据名,size 是允许缓存的数据大小,以字节为单位。 ```conf ssl_session_cache builtin:1000 shared:SSL:10m; ``` - `ssl_session_timeout`:指令用于开启 SSL 会话功能后,设置客户端能够反复使用储存在缓存中的会话参数时间,默认值超时时间是 5 秒。 - 语法:`ssl_session_timeout