目录

1、Nginx服务器基础配置实例

2、Nginx服务操作的问题

2.1、Nginx配置成系统服务

2.2、Nginx命令配置到系统环境

3、Nginx静态资源部署

3.1、Nginx静态资源概述

3.1.1、静态资源的配置指令

2、server_name指令

2.1、配置方式1——精确匹配

2.2、配置方式2——使用通配符配置

2.3、配置方式3——使用正则表达式配置

2.3.1、匹配执行顺序

3、location指令

4、设置请求资源的目录root / alias

4.1、root:设置请求的根目录

4.2、alias:用来更改location的URI

5、index指令

6、error_page指令

7、静态资源优化配置语法

7.1、sendfile

7.2、tcp_nopush

7.3、tcp_nodelay

8、静态资源压缩

8.1、Gzip模块配置指令

8.2、Gzip压缩功能的配置

8.3、Gzip和sendfile的冲突解决

8.4、浏览器不支持Gzip的解决方案

9、浏览器的缓存处理

9.1、什么是缓存

9.2、什么是web缓存

9.3、web缓存的种类

9.4、浏览器缓存

9.5、为什么要用浏览器缓存

9.6、浏览器缓存的执行流程

9.7、Nginx浏览器缓存相关指令

9.7.1、expires指令

9.7.2、add_header指令

10、Nginx的跨域问题

10.1、同源策略

10.2、跨域问题

11、静态资源防盗链

11.1、Nginx防盗链的实现原理

12、Rewrite

12.1、Rewrite功能配置

12.2、Rewrite的相关指令

12.2.1、set指令

12.2.2、if指令

12.2.3、break指令

12.2.4、return指令

12.2.5、rewrite指令

12.2.6、rewrite_log指令

12.3、Rewrite常用全局变量

12.4、Rewrite的案例

12.4.1、域名跳转

12.4.2、域名镜像

12.4.3、独立域名

12.4.4、目录自动加 /

12.4.5、合并目录

12.4.6、防盗链


1、Nginx服务器基础配置实例

前面我们已经对Nginx服务器默认配置文件的结构和涉及的基本指令做了详细的阐述。通过这些指令的合理配置,我们就可以让一台Nginx服务器正常工作,并且提供基本的web服务器功能。

接下来将通过一个比较完整和最简单的基础配置实例,来巩固下前面所学习的指令及其配置。

需求如下:

1、有如下访问
    http://192.168.200.133:8081/server1/location1
        访问的是:index_sr1_location1.html
    http://192.168.200.133:8081/server1/location2
        访问的是:index_sr1_location2.html
    http://192.168.200.133:8082/server1/location1
        访问的是:index_sr2_location1.html
    http://192.168.200.133:8082/server1/location2
        访问的是:index_sr2_location2.html

2、如果访问的资源不存在
    返回自定义的404页面。

3、将/server1和/server2的配置使用不同的配置文件分割
    将文件放到/home/www/conf.d目录下,然后使用include进行合并。

4、为/server1和/server2各自创建一个访问日志文件

先建立好目录结构:

ngnix-1.20.2目录
    html目录
        conf.d目录
            server1.conf
            server2.conf
        myweb目录
            server1目录
                location1目录
                    index_sr1_location1.html
                location2目录
                    index_sr1_location2.html
                logs目录
                    access.log
            server2目录
                location1目录
                    index_sr2_location1.html
                location2目录
                    index_sr2_location2.html
                logs目录
                    access.log
            404.html
nginx.conf:

## 全局块 begin ##
# 配置允许运行Nginx工作进程的用户和用户组
# user	www

# 配置运行Nginx进程生成的worker进程数
worker_processes  2;

# 配置Nginx服务器运行对错误日志存放的路径
error_log	logs/error.log;

# 配置Nginx服务器允许时记录Nginx的master进程的PID文件路径和名称
pid logs/nginx.pid;

# 配置Nginx服务是否以守护进程方法启动
# deamon on;
## 全局块 end ##


## events块 begin ##
events {
	# 设置nginx网络序列化,解决惊群现象
	accept_mutex on;
	
	# 设置允许worker可以同时接收多个请求
	multi_accept on;
	
	# 设置nginx进程最大能接收的连接数
    worker_connections  1024;
	
	# 设置nginx的事件驱动模型
	# use epoll;
}
## events块 end ##


## http块 begin ##
http {
	# 解决MIME类型的配置
    include       mime.types;
    default_type  application/octet-stream;

	# 配置允许使用sendfile方式运输,用来提升nginx处理静态资源的效率
    sendfile        on;

	# 设置长连接的超时时间
    keepalive_timeout  65;
	
	# 配置请求处理日志格式
	log_format server1 '===>server1' access log format;
	log_format server2 '===>server2' access log format;
	
	
	## server块 begin ##
	# 通过include来指定额外的配置文件(相对路径从该文件所在目录开始)
	include ../html/conf.d/*.conf;
	## server块 end ##
}
## http块 end ##
server1.conf:

server {
	# 配置监听端口和主机名称
	listen 8079;
	server_name localhost;
	
	# 配置请求处理日志存放路径
	access_log html/myweb/server1/logs/access.log;
	
	# 配置处理/server1/location1请求的location
	location /server1/location1{
		# 根的相对路径从nginx所在目录开始
		root html/myweb;
		index index_sr1_location1.html;
	}
	
	# 配置处理/server1/location2请求的location
	location /server1/location2 {
		root html/myweb;
		index index_sr1_location2.html;
	}
	
	# 配置错误页面
	error_page 404 /404.html;
	# 配置错误页面转向
	location = /404.html {
		root html/myweb;
		index 404.html;
	}
}
server2.conf:

server {
	listen 8078;
	server_name localhost;
	access_log html/myweb/server2/logs/access.log;
	
	location /server2/location1{
		root html/myweb;
		index index_sr2_location1.html;
	}
	
	location /server2/location2 {
		root html/myweb;
		index index_sr2_location2.html;
	}
	
	error_page 404 /404.html;
	location = /404.html {
		root html/myweb;
		index 404.html;
	}
}

2、Nginx服务操作的问题

经过前面的操作我们会发现,如果想要启动、关闭或重新加载nginx配置文件,都需要先进入到nginx的安装目录的sbin目录,然后使用nginx的二进制可执行文件来操作,相对来说操作比较繁琐,应该如何优化?另外如果我们想把Nginx设置成随着服务器启动就自动完成启动操作,又该如何来实现?

2.1、Nginx配置成系统服务

把Nginx应用服务设置称为系统服务,方便对Nginx服务的启动和停止等相关操作,具体实现步骤:

在/usr/lib/systemd/system目录下添加nginx.service,内容如下:

2.2、Nginx命令配置到系统环境

3、Nginx静态资源部署

3.1、Nginx静态资源概述

静态资源即指在服务器端真实存在并且能直接拿来展示的一些文件,比如常见的html页面、css文件、js文件、图片、视频等资源。

动态资源即指在服务器端真实存在但是要想获取需要经过一定的业务逻辑处理,根据不同的条件展示在页面不同这一部分内容,比如说报表数据展示、根据当前登录用户展示相关具体数据等资源。

Nginx处理静态资源的内容,我们需要考虑下面的这几个问题:

  • 静态资源的配置指令;
  • 静态资源的配置优化;
  • 静态资源的压缩配置指令;
  • 静态资源的缓存处理;
  • 静态资源的访问控制,包括跨域问题和防盗链问题。

3.1.1、静态资源的配置指令

1、listen指令

listen:用来配置监听端口。

语法

listen address[:port][default_server]...;

listen port [default_server];

默认值

listen *:80 | *.8080

位置

server

listen的设置比较灵活,我们通过几个例子来吧常用的设置方式熟悉下:

listen 127.0.0.1:8080; //listen localhost:8080 监听指定的IP和端口
listen 127.0.0.1; //监听指定IP的所有端口
listen 8000; //监听指定端口上的连接
listen *:8000; //监听指定端口上的连接

default_server属性是标识符,用来将此虚拟主机设置成默认主机。所谓的默认主机指的是如果没有匹配到对应的address:port,则会默认执行的。如果不指定默认使用的是第一个server。

2、server_name指令

server_name:用来设置虚拟主机服务名称。

语法

server_name name ...;

默认值

server_name "";

位置

server

关于server_name的配置方式有三种,分别是:① 精确匹配;② 通配符匹配;③ 正则表达式匹配。

2.1、配置方式1——精确匹配

server {
    listen    80;
    server_name    www.baidu.com www.abc.com
}

hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应网页,如果没有找到,则系统会再将网址提交DNS域名解析服务器进行IP地址的解析。

  • Windows系统下的hosts位置:C:\Windows\System32\drivers\etc\hosts
  • Centos系统下的hosts位置:/etc/hosts

因为域名是要收取一定的费用,所以我们可以使用修改hosts文件来制作一些虚拟域名来使用。需要修改/etc/hosts文件来添加:

hosts:

127.0.0.1 www.abc.com
127.0.0.1 www.www.com

2.2、配置方式2——使用通配符配置

server_name中支持通配符 * ,但是要注意的是通配符不能出现在域名的中间,只能出现在首段或尾段,如:

server {
    listen    80;
    server_name    *.abc.com www.abc.* 
}

下面的配置会报错:
server {
    listen    80;
    server_name    www.*.com;
}

2.3、配置方式3——使用正则表达式配置

server_name中可以使用正则表达式,并且使用~作为正则表达式字符串的开始标记。

常见的正则表达式:

代码

说明

^

匹配搜索字符串开始位置

$

匹配搜索字符串结束位置

.

匹配除换行符\n之外的任何单个字符

\

转义字符,将下一个字符标记为特殊字符

[xyz]

字符集,与任意一个指定字符匹配

[a-z]

字符范围,匹配指定范围内的任何字符

\w

与以下任意字符匹配 A-Z a-z 0-9 和下划线,等效于[A-Za-z0-9]

\d

数字字符匹配,等效于[0-9]

{n}

正好匹配n次

{n,}

至少匹配n次

{n,m}

匹配至少n次至多m次

*

零次或多次,等效于{0,}

+

一次或多次,等效于{1,}

配置如下:

server {
    listen 80;
    server_name ~^www\.(\w+)\.com&;

    location / {
        default_type text/plain;
        return 200 $1;      
        #root html;
        #index index.html index.htm;
    }

}

注意:~后面不能加空格,括号可以取值。

2.3.1、匹配执行顺序

由于server_name指令支持通配符和正则表达式,因此在包含多个虚拟主机的配置文件中,可能会出现一个名称被多个虚拟主机的server_name匹配成功,当遇到这种情况,当前的请求交给谁来处理呢?

顺序:
1、精确匹配server_name
2、通配符在开始时匹配server_name成功
3、通配符在结束时匹配server_name成功
4、正则表达式匹配server_name成功
5、被默认的dafault_server处理,如果没有指定的默认找第一个server

3、location指令

server {
    listen 80;
    server_name localhost;
    
    location / {
        
    }

    location /abc {
        
    }
}

location:用来设置请求的URI。

语法

location [ = | ~ | ~* | ^~ | @ ] uri{...};

默认值

——

位置

server,location

uri变量是待匹配的请求字符串,可以不包含正则表达式,也可以包含正则表达式,那么nginx服务器在搜索匹配location的时候,是先使用不包含正则表达式进行匹配,找到一个匹配度最高的一个,然后再通过包含正则表达式的进行匹配,如果能匹配到直接访问,匹配不到,就使用刚才匹配度最高的那个location来处理请求。

属性介绍:不带符号,要求必须以指定模式开始。

server {
    listen    80;
    server_name    localhost;

    location /abc {
        default_type text/plain;
        return 200 "access success";
    }
}

以下访问都是正确的:
http://192.168.200.133/abc
http://192.168.200.133/abc?p1=TOM

匹配不到:
http://192.168.200.133/abc/
http://192.168.200.133/abcdef

=:用于不包含正则表达式的uri前,必须与指定的模式精确匹配。

server {
    listen    80;
    server_name    localhost;

    location =/abc {
        default_type text/plain;
        return 200 "access success";
    }
}

以下访问都是正确的:
http://192.168.200.133/abc
http://192.168.200.133/abc?p1=TOM

匹配不到:
http://192.168.200.133/abc/
http://192.168.200.133/abcdef

~:用于表示当前uri中包含了正则表达式,并且区分大小写。

~*:用于表示当前uri中包含了正则表达式,并且不区分大小写。

换句话说,如果uri包含了正则表达式,需要用上述两个符号来标识。

server {
    listen    80;
    server_name    localhost;

    location ~^/abc\w$ {
        default_type text/plain;
        return 200 "access success";
    }
}

server {
    listen    80;
    server_name    localhost;

    location ~*^/abc\w$ {
        default_type text/plain;
        return 200 "access success";
    }
}

^~:用于不包含正则表达式的uri前,功能和不加符号的一致,唯一不同的是,如果模式匹配,那么就停止搜索其他模式了。

server {
    listen    80;
    server_name    localhost;

    location ^~/abc {
        default_type text/plain;
        return 200 "access success";
    }
}

4、设置请求资源的目录root / alias

4.1、root:设置请求的根目录

语法

root path;

默认值

root html;

位置

http、server、location

path为Nginx服务器接收到请求以后查找资源的根目录路径。

4.2、alias:用来更改location的URI

语法

alias path;

默认值

——

位置

location

path为修改后的根路径。

以上两个指令都可以来指定访问资源的路径,那么两者之间的区别是什么?

举例说明:
1、在/usr/local/nginx/html目录下创建一个images目录并在该目录下放入一张图片mv.png
location /images {
    root /usr/local/nginx/html;
}

2、访问图片的路径为:
http://localhost/images/mv.png

3、如果把root改为alias
location /images {
    alias /usr/local/nginx/html;
}
再次访问上述地址,页面会出现404错误,查看错误日志会发现是因为地址不对,所以验证了:
    root的处理结果是:root路径+location路径
    alias的处理结果是:使用alias路径替换location路径
需要在alias后面路径改为:
location /image {
    alias /usr/local/nginx/html/images;
}

4、如果alias方式的location路径是以/结尾,则alias也必须是以/结尾,root没有要求。
将上述配置修改为:
location /images/ {
    alias /usr/local/nginx/html/images;
}
访问就会出现问题,查看错误日志是因为路径不对,所以需要吧alias后面加上/。

小结:

  • root的处理结果是:root路径 + location路径;
  • alias的处理结果是:使用alias路径替换location路径;
  • alias是一个目录别名的定义,root则是最上层目录的含义;
  • 如果location路径是以/结尾,则alias也必须是以/结尾,root没有要求。

5、index指令

index:设置网站的默认首页。

语法

index file ...;

默认值

index index.html;

位置

http、server、location

index后面可以跟多个设置,如果访问的时候没有指定具体访问的资源,则会依次进行查找,找到第一个为止。

举例说明;

location / {
    root /usr/local/nginx/html;
    index index.html index.htm;
}

访问该location的时候,可以通过http://ip:port/,地址后面如果不添加任何内容,
则默认依次访问index.html和index.htm,找到第一个来进行返回。

6、error_page指令

error_page:设置网站的错误页面。

语法

error_page code ...[=[response]] uri;

默认值

——

位置

http、server、location...

当出现对应的响应code后,如何来处理。

举例说明:

1、可以指定具体跳转的地址
server {
    error_page 404 http://www.baidu.com;
}

2、可以指定重定向地址
server {
    error_page 404 /50x.html;
    error_page 500 502 503 504 /50x.html;
    location =/50x.html {
        root html;
    }
}

3、使用location的@符合完成错误信息展示
server {
    error_page 404 @jump_to_error;
    location @jump_to_error {
        default_type text/plain;
        return 404 'Not Found Page ...';
    }
}

可选项=[response]的作用是用来将相应代码更改为另外一个。

server {
    # 将404更改为200状态码(=response,response为200)
    error_page 404 =200 /50x.html;
    location =/50x.html {
        root html;
    }
}

这样的话,当返回404找不到对应的资源的时候,在浏览器上可以看到,最终返回的状态码是200,这块需要注意下,编写error_page后面的内容,404后面需要加空格,200前面不能加空格。

7、静态资源优化配置语法

Nginx对静态资源如何进行优化配置,这里从三个属性配置进行优化。

  • sendfile on;
  • tcp_nopush on;
  • tcp_nodeplay on;

7.1、sendfile

sendfile:用来开启高效的文件传输模式。

语法

sendfile on | off;

默认值

sendfile off;

位置

http、server、location...

请求静态资源的过程:客户端通过网络接口向服务端发送请求,操作系统将这些客户端请求传递给服务器端应用程序,服务器端应用程序会处理这些请求,请求处理完成以后,操作系统还需要将处理得到的结果通过网络适配器传递回去。

7.2、tcp_nopush

tcp_nopush:该指令必须在sendfile打开的状态下才会生效,主要是用来提升网络包的传输“效率”。

语法

tcp_nopush on | off;

默认值

tcp_nopush off;

位置

http、server、location

7.3、tcp_nodelay

tcp_nodelay:该指令必须在keep_alive连接开启的情况下才会生效,来提高网络包传输的“实时性”。

语法

tcp_nodelay on | off;

默认值

tcp_nodelay on;

位置

http、server、location

经过刚才的分析,“tcp_nopush”和“tcp_nodelay”看起来是“互斥的”,那么为什么要将这两个值都打开,需要知道的是在linux2.5.9以后的版本中两者是可以兼容的,三个指令的好处是,sendfile可以开启高效的文件传输模式,tcp_nopush开启可以确保在发送到客户端之前数据包已经充分“填满”,这大大减少了网络开销,并加快了文件发送的速度。然后,当它到达最后一个可能因为没有“填满”而暂停的数据包时,Nginx会忽略tcp_nopush参数,然后,tcp_nodelay强制套接字发送数据。由此可知,tcp_nopush和tcp_nodelay一起设置,它比单独配置tcp_nodelay具有更强的性能。所以我们可以使用如下配置来优化Nginx静态资源的处理。

8、静态资源压缩

在Nginx的配置文件中可以通过配置gzip来对静态资源进行压缩,相关的指令可以配置在http块、server块和location块中,Nginx可以通过

  • ngx_http_gzip_module模块;
  • ngx_http_gzip_static_module模块;
  • ngx_http_gunzip_module模块。

对这些指令进行解析和处理。

8.1、Gzip模块配置指令

8.2、Gzip压缩功能的配置

8.3、Gzip和sendfile的冲突解决

8.4、浏览器不支持Gzip的解决方案

9、浏览器的缓存处理

9.1、什么是缓存

缓存(cache),原始意义是指访问速度比一般随机存取存储器(RAM)快的一种高速存储器,通常它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术。缓存的设置是所有现代计算机系统发回高性能的重要因素之一。

9.2、什么是web缓存

web缓存是指一个web资源(如html页面,图片,js,数据等)存在于web服务器和客户端(浏览器)之间的副本。缓存会根据进来的请求保存输出内容的副本;当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个URL地址的时候,如果网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页,只有当网站明确表示资源已经更新,浏览器才会再次下载网页。

9.3、web缓存的种类

客户端缓存
    浏览器缓存
服务端缓存
    Nginx / Redis / Memcached

9.4、浏览器缓存

是为了节约网络的资源加速浏览,浏览器在用户磁盘上对最近请求过的文档进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览。

9.5、为什么要用浏览器缓存

  • 成本最低的一种缓存实现;
  • 减少网络带宽消耗;
  • 降低服务器压力;
  • 减少网络延迟,加快页面打开速度。

9.6、浏览器缓存的执行流程

HTTP协议中和页面缓存相关的字段如下:

header

说明

Expires

缓存过期的日期和时间

Cache-Control

设置和缓存相关的配置信息

Last-Modified

请求资源最后修改时间

ETag

请求变量的实体标签的当前值,比如文件的MD5值

9.7、Nginx浏览器缓存相关指令

Nginx需要进行缓存相关设置,就需要用到如下的指令。

9.7.1、expires指令

expires:该指令用来控制页面缓存的作用,可以通过该指令控制HTTP应答中的“Expires”和“Cache-Control”。

语法

expires [modified] time

expires epoch | max | off;

默认值

expires off;

位置

http、server、location

  • time:可以整数也可以是负数,指定过期时间,如果是负数,Cache-Control则为no-cache,如果为整数或0,则Cache-Control的值为max-age=time;
  • apoch:指定Expires的值为'1 January,1970,00:00:01 GMT',Cache-Control的值为no-cache;
  • max:指定Expires的值为'31 December2037 23:59:59GMT',Cache-Control的值为10年;
  • off:默认不缓存。

9.7.2、add_header指令

add_header:用来添加指定的响应头和响应值。

语法

add_header name value [always];

默认值

——

位置

http、server、location、...

Cache-Control作为响应头信息,可以设置如下值:

缓存响应指令:
Cache-Control:must-revalidate
Cache-Control:no-cache
Cache-Control:no-store
Cache-Control:no-transform
Cache-Control:public
Cache-Control:private
Cache-Control:proxy-revalidate
Cache-Control:max-age=<seconds>
Cache-Control:s-maxage=<seconds>

指令

说明

must-revalidate

可缓存但必须再让源服务器进行确认

no-cache

缓存前必须确认其有效性

no-store

不缓存请求或响应的任何内容

no-transform

代理不可更改媒体类型

public

可向任意方提供响应的缓存

private

仅向特定用户返回响应

proxy-revalidate

要求中间缓存服务器对缓存的响应有效性再进行确认

max-age=<秒>

响应最大Age值

s-maxage=<秒>

公共缓存服务器响应的最大Age值

10、Nginx的跨域问题

我们主要从以下方面进行解决:

  • 什么情况下会出现跨域问题?
  • 实例演示跨域问题
  • 具体的解决方案是什么?

10.1、同源策略

浏览器的同源策略:是一种约定,是浏览器最核心也是最基本的安全功能,如果浏览器少了同源策略,则浏览器的正常功能可能都会受到影响。

同源:协议、域名(IP)、端口相同即为同源。

10.2、跨域问题

简单描述:有两台服务器分别为A,B,如果从服务器A的页面发送异步请求到服务器B获取数据,如果服务器A和服务器B不满足同源策略,就会出现跨域问题。

解决方案:使用add_header指令,该指令可以用来添加一些头信息。

此处用来解决跨域问题,需要添加两个头信息,一个是Access-Control-Allow-Origin,Access-Control-Allow-Methods。

  • Access-Control-Allow-Origin:直译过来就是允许跨域访问的源地址信息,可以配置多个(用多个逗号分隔),也可以使用*代表所有源。
  • Access-Control-Allow-Methods:直译过来就是允许跨域访问的请求方式,值可以是GET、POST、PUT、DELETE...,可以全部设置,也可以根据需要设置,用多个逗号分隔。

具体配置方式:

location /getUser {
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE;
    default_type application/json;
    return 200 '{"id":1, "name":"TOM","age":18}';
}

11、静态资源防盗链

资源盗链:指的是此内容不在自己服务器上,而是通过技术手段,绕过别人的限制将别人的内容放到自己页面上最终展示给用户。以此来盗取大网站的空间和流量。

11.1、Nginx防盗链的实现原理

HTTP有一个头信息Referer,当浏览器向web服务器发送请求的时候,一般都会带上Referer来告诉服务器该网页是从哪个页面链接过来的。

后台服务器可以根据获取到的这个Referer信息来判断是否为自己信任的网站地址,如果是则放行继续访问,如果不是则可以返回403(服务端拒绝访问)的状态信息。

Nginx防盗链的具体实现:valid_referers:nginx会通过查看referer自动和valid_referers后面的内容进行匹配,如果匹配到了就将$invalid_referer变量置0,如果没有匹配到,则将$invalid_referer变量置为1,匹配的过程中不区分大小写。

语法

valid referers none | blocked | server_names | string ...;

默认值

——

位置

server、location

  • none:如果Header中的Referer为空,允许访问;
  • blocked:在Header中的Referer不为空,但是该值被防火墙或代理进行伪装过,如不带"http://"、"https://"等协议头的资源允许访问;
  • server_names:指定具体的域名或者IP;
  • string:可以支持正则表达式和*的字符串。如果是正则表达式,需要以~开头表示。例如
# 对特定类型的文件进行防盗链操作
location ~*\.(png|jpg|gif) {
    valid_referers none blocked www.baidu.com 192.168.200.222 *.example.com;
    # if后面要带空格
    if ($invalid_referer) {
        return 403;
    }
    root html;
}

# 对一个目录下的所有资源防盗链操作
location /images {
    valid_referers none blocked www.baidu.com 192.168.200.222 *.example.com;
    # if后面要带空格
    if ($invalid_referer) {
        return 403;
    }
    root html;
}

遇到的问题:Referer的限制比较粗,比如随意加一个Referer,上面的方式是无法进行限制的,那么这个问题该如何解决?

此处我们需要用到Nginx的第三方模块ngx_http_accesskey_module。

12、Rewrite

12.1、Rewrite功能配置

Rewrite是Nginx服务器提供的一个重要基本功能,是Web服务器产品中几乎必备的功能,主要的作用是用来实现URL的重写。

注意:Nginx服务器的Rewrite功能的实现依赖于PCRE(正则表达式库)的支持,因此在编译安装nginx服务器之前,需要安装PCRE库。Nginx使用的是ngx_http_rewrite_module模块来解析和处理Rewrite功能的相关配置。

-Rewrite相关命令:
    set指令
    id指令
    break指令
    return指令
    return指令
    rewrite指令
    rewrite_log指令

-Rewrite的应用场景
    域名跳转
    域名镜像
    独立域名
    目录自动添加"/"
    合并目录

12.2、Rewrite的相关指令

12.2.1、set指令

set:该指令用来设置一个新的变量。

语法

set $variable value;

默认值

——

位置

server、location、...

  • variable:变量的名称,该变量名称要用 $ 作为变量的第一个字符,且不要与Nginx服务器预设的全局变量同名。
  • value:变量的值,可以是字符串、其他变量或者变量的组合等。
server {
		listen	8000;
		server_name	localhost;
		
		location /server {
			set $name TOM;
			set $age 18;
			default_type text/plain;
			return 200 $name=$age;
		}
	}

12.2.2、if指令

if:该指令用来支持条件判断,并根据条件判断结果选择不同的Nginx配置。

语法

if (condition) {...}

默认值

——

位置

server、location

condition为判定条件,可以支持以下写法:

  • 变量名。如果变量名对应的值为空字符串或0,if都判断为false,其它条件为true;
if ($param) {
    ...
}
  • 使用 = 和 != 比较变量和字符串是否相等,满足条件为true,不满足为false。注意:此处和Java不太一样的地方是字符串不需要添加引号,并且等号和不等号前后都需要加空格。
if ($request_method = POST) {
    ...
}
  • 使用正则表达式对变量进行匹配,匹配成功返回true,否则返回false。变量与正则表达式之间使用“~”,“~*”,“!~”,“!~*”来连接。
~:代表匹配正则表达式过程中区分大小写
~*:代表匹配正则表达式过程中不区分大小写
!~ 和 !~* :刚好和上面取相反值,如果匹配上返回false,匹配不上返回true

-示例:
if ($http_user_agent ~ MSIE) {
    # $http_user_agent的值中是否包含MSIE字符串,如果包含返回true
}

-注意:
正则表达式字符串一般不需要加引号,但是如果字符串中包含 } 或者是 ; 等字符时,就要把引号带上
  • 判断请求的文件是否存在使用 -f 和 !-f 。
if (-f $request_filename) {
    # 判断请求的文件是否存在
}

if (!-f $request_filename) {
    # 判断请求的文件是否不存在
}
  • 判断请求的目录是否存在使用 -d 和 ~-d
  • 判断请求的目录或者文件是否存在使用 -e 和 !-e
  • 判断请求的文件是否可执行使用 -x 和 !-x

12.2.3、break指令

该指令用于中断当前相同作用域中的其他Nginx配置,与该指令处于同一作用域的Nginx配置中,位于它前面的指令配置生效,位于后面的指令配置无效。并且break还有另外一个功能就是终止当前的匹配并把当前的URI在本location进行重定向访问处理。

语法

break;

默认值

——

位置

server、location、if

例子:
		location /testbreak {
			default_type text/plain;
			set $username TOM;
			if ($args) {
				set $username JERRY;
				break;
				set $username ROSE;
			}
			add_header username $username;
			return 200 $username;
		}

12.2.4、return指令

return:该指令用于完成对请求的处理,直接向客户端返回。在return后的所有Nginx配置都是无效的。

语法

return code [text];

return code URL;

return URL;

默认值

——

位置

server、location、if

  • code:为返回给客户端的HTTP状态代理。可以返回的状态码为0-999的任意HTTP状态代理。
  • text:为返回给客户端的响应体内容,支持变量的使用。
  • URL:为返回给客户端的URL地址。
server {
		listen	8000;
		server_name	localhost;
		
		location /server {
			root html;
			set $name TOM;
			set $age 18;
			default_type text/plain;
			return 200 $name=$age=$request_uri;
		}
		
		location /testif1 {
			default_type text/plain;
			# 如果args有值会返回success
			if ($args) {
				return 200 success=$args;
			}
			# 如果没有值返回error
			return 200 error;
		}
		
		location /testif2 {
			default_type text/plain;
			if ($request_method = POST) {
				return 405;
			}
			return 200 error;
		}
		
		location /testif3 {
			root html;
			default_type text/plain;
			#	if (-f $)
			return 200 welcome.html;
		}
		
		location /testbreak {
			default_type text/plain;
			set $username TOM;
			if ($args) {
				set $username JERRY;
				break;
				set $username ROSE;
			}
			add_header username $username;
			return 200 $username;
		}
		
		location /testreturn {
			default_type application/json;
			return 200 '{"id":1,"name":"zhangsan"}';
		}
		
		location /testbaidu {
			return 302 https://www.baidu.com;
		}
	}

12.2.5、rewrite指令

rewrite指令:该指令通过正则表达式的使用来改变URI。可以同时存在一个或者多个指令,按照顺序依次对URL进行匹配和处理。

语法

rewrite regex replacement [flag];

默认值

——

位置

server、location、if

  • regex:用来匹配URI的正则表达式。
  • replacement:匹配成功后,用于替换URI中被截取内容的字符串。如果该字符串是以“http://”或者“https://”开头的,则不会继续向下对URI进行其他处理,而是直接返回重写后的URI给客户端。
server {
		listen	8000;
		server_name	localhost;
		
		location /rewrite {
			# /rewrite/urlxxx(以url开头)
			rewrite ^/rewrite/url\w*$ https://www.baidu.com;
			# /rewrite/textxxx(以test开头)
			rewrite ^/rewrite/(test)\w*$ /$1;
			# /rewrite/demoxxx(以demo开头)
			rewrite ^/rewrite/(demo)\w*$ /$1;
		}
		
		location /test {
			default_type text/plain;
			return 200 test_success;
		}
		
		location /demo {
			default_type text/plain;
			return 200 demo_success;
		}
	}

flag:用来设置rewrite对URI的处理行为,可选值如下:

  • last:终止继续在本location块中处理接收到的URI,并将此处重写的URI作为一个新的URI,使用各location块进行处理。该标志将重写后的URI重新在server块中执行,为重写后的URI提供了转入到其他location块的机会。
  • break:将此处重写的URI作为一个新的URI,在本块中继续进行处理。该标志将重写后的地址在当前的location块中执行,不会将新的URI转向其他的location块。
server {
		listen	8000;
		server_name	localhost;
		
		location /rewrite1 {
			rewrite ^/rewrite1/(test)\w*$ /$1 last;
			rewrite ^/rewrite1/(demo)\w*$ /$1 last;
		}
		
		location /rewrite2 {
			rewrite ^/rewrite2/(test)\w*$ /$1 break;
			rewrite ^/rewrite2/(demo)\w*$ /$1 break;
		}
		
		location /test {
			default_type text/plain;
			return 200 test_success;
		}
		
		location /demo {
			default_type text/plain;
			return 200 demo_success;
		}
	}
  • redirect:将重写后的URI返回给客户端,状态码为302,指明是临时重定向URI,主要用在replacement变量不是以"http://"或者"https://"开头的情况。
server {
		listen	8000;
		server_name	localhost;
		
		location /rewrite {
			# 重定向
			rewrite ^/rewrite/(test)\w*$ /$1 redirect;
			rewrite ^/rewrite/(demo)\w*$ /$1 redirect;
		}
		
		location /test {
			default_type text/plain;
			return 200 test_success;
		}
		
		location /demo {
			default_type text/plain;
			return 200 demo_success;
		}
	}
  • permanent:将重写后的URI返回给客户端,状态码为301,指明是永久重定向URI,主要用在replacement变量不是以"http://"或者"https://"开头的情况。

12.2.6、rewrite_log指令

rewrite_log:该指令配置是否开启URL重写日志的输出功能。

语法

rewrite_log on | off;

默认值

rewrite_log off;

位置

http、server、location、if

开启后,URL重写的相关日志将以notice级别输出到error_log指令配置的日志文件汇总。

rewrite_log on;
error_log logs/error.log notice;

12.3、Rewrite常用全局变量

变量

说明

$args

变量中存放了请求URL中的请求参数。比如http://192.168.200.133/server?arg1=value1&arg2=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=cooikeValue'来添加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”

上述参数还可以在日志文件中使用,用到log_format指令。

log_format main '$remote_addr - $request - $status - $request_uri $http_user_agent';

access_log logs/access.log main;

12.4、Rewrite的案例

12.4.1、域名跳转

问题分析:我们想访问京东网站,可以输入www.jd.com,也可以输入www.360buy.com,后面由于各种原因把自己的域名换成了www.jd.com,虽然说域名变了,但是对于以前只记住了www.360buy.com的用户来说,如何把这部分用户也迁移到新域名的访问上来,针对这个问题,我们可以使用Nginx中的Rewrite的域名跳转来解决。

环境准备:

-准备三个域名(hosts)
127.0.0.1 www.abc.com
127.0.0.1 www.abc1.com
127.0.0.1 www.abc2.com

-通过Nginx实现访问www.abc.com
server {
    listen 80;
    server_name www.abc.com;
    location / {
        default_type text/html;
        return 200 '<h1>welcome to abc</h1>';
    }
}

-域名跳转
server {
	listen 80;
	server_name www.abc1.com www.abc2.com;
	# rewrite ^/ http://www.abc.com;
	rewrite ^(.*) http://www.abc.com$1;
}

12.4.2、域名镜像

镜像网站指定是将一个完全相同的网站分别放置到几台服务器上,并分别使用独立的URL进行访问,其中一台服务器上的网站叫主站,其他的为镜像网站。镜像网站和主站没有太大的区别,可以把镜像网站理解为主站的一个备份结点。可以通过镜像网站提供网站在不同地区的响应速度。镜像网站可以平衡网站的流量负载、可以解决网络带宽限制、封锁等。上述案例中,将www.abc1,com和www.abc2.com都能跳转到www.abc.com,那么www.abc.com我们就可以把它称为主域名,其他两个就是我们所说的镜像域名,当然如果我们不想把整个网站做镜像,只想为其中某一个子目录下的资源做镜像,我们可以在location块中配置rewrite功能。

server {
		listen 80;
		server_name www.abc1.com www.abc2.com;
		
		# 如果是/user,则跳转
		location /user {
			rewrite ^/user(.*)$ http://www.abc.com$1;
		}
		
		# 如果是/emp,则进行处理
		location /emp {
			default_type text/html;
			return 200 '<h1>emp access</h1>';
		}
	}

12.4.3、独立域名

一个完整的项目包含多个模块,比如购物网站有商品搜索模块、商品详情模块和购物车模块等,那么我们如何为每一个模块设置独立的域名?

需求:

-访问商品搜索模块
http://search.abc.com:81 

-访问商品详情模块
http://item.itcast.com:82

-访问商品购物车模块
http://cart.itcast.com:83
server {
		listen 81;
		server_name search.abc.com;
		rewrite ^(.*) http://www.abc.com/search$1;
	}
	server {
		listen 82;
		server_name item.abc.com;
		rewrite ^(.*) http://www.abc.com/item$1;
	}
	server {
		listen 83;
		server_name cart.abc.com;
		rewrite ^(.*) http://www.abc.com/cart$1;
	}

12.4.4、目录自动加 /

问题描述:

例子:

server {
    listen 8082;
    server_name localhost;
    location /abc {
        root html;
        index index.html;
    }
}

通过192.168.200.133:8082/abc和通过192.168.200.133:8082/abc/访问的区别?

如果不加 / ,Nginx服务器内部会自动做一个301的重定向,重定向的地址会有一个指令叫server_name_in_redirect on | off; 来决定重定向的地址:

如果该指令为on
    重定向的地址为:http://server_name:8082/目录名/;
    http://localhost:8082/abc/

如果该指令为off
    重定向的地址为:http://源URL中的域名:8082/目录名/;
    http://192.168.200.133:8082/abc/

所以就拿刚才的地址来说,http://192.168.200.133:8082/abc如果不加 / ,那么按照上述规则,如果指令server_name_in_redirect为on,则301重定向地址变为http://localhost:8082/abc/,如果为off,则301重定向地址变为http://192.168.200.133:8082/abc/。后面这个是正常的,前面地址就有问题。

注意server_name_in_redirect指令在Nginx的0.8.48版本之前默认都是on,之后改成了off,所以现在我们这个版本不需要考虑这个问题,但是如果是0.8.48以前的版本并且server_name_in_redirect设置为on,我们如何通过rewrite来解决这个问题?

解决方案:我们可以使用rewrite功能为末尾没有 / 的URL自动添加一个 / 。

server {
		listen 8082;
		server_name localhost;
		server_name_in_redirect on;
		
		location /abc {
			root html;
			index index.html;
			if (-d $request_filename) {
				rewrite ^(.*)([^/])$ http://$host:$server_port$1$2/ permanent;
			}
		}
	}

12.4.5、合并目录

搜索引擎优化(SEO)是一种搜索引擎的搜索规则来提高目的网站在有关搜索引擎内排名的方式。我们在创建自己的站点时,可以通过很多种方式来有效地提供搜索引擎优化的程度。其中有一项就包含URL的目录层级一般不要超过三层,否则的话不利于搜索引擎的搜索,也给客户端的输入带来了负担,但是将所有的文件放在一个目录下又会导致文件资源管理混乱并且访问文件的速度也会随着文件的增多而慢下来,这两个问题是相互矛盾的,那么使用rewrite如何解决上述问题?

举例:网站中有一个资源文件的访问路径时/server/11/22/33/44/20.html,也就是说20.html存在于第五级目录下,如果想要访问该资源文件,客户端的URL地址就要写成http://www.web.name/server/11/22/33/44/20.html,

server {
    listen 8083;
    server_name localhost;
    location /server {
        root html;
    }
}

但是这个是非常不利于SEO搜索引擎优化的,同时客户端也不好记,使用rewrite我们可以进行如下的配置:

server {
		listen 8083;
		server_name localhost;
		
		location /server {
			root html;
			index index.html;
			rewrite ^/server-([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /server/$1/$2/$3/$4/$5.html last;
		}
	}

12.4.6、防盗链

防盗链之前我们已经介绍了相关知识,在rewrite中的防盗链和之前讲的原理都是一样的,只不过通过rewrite可以将防盗链的功能进行完善,当出现防盗链的情况,我们可以使用rewrite将请求转发到自定义的一张图片和页面,给用户比较好的提示信息。下面我们就通过根据文件类型实现防盗链的一个配置实例:

server {
		listen 80;
		server_name www.abc1.com www.abc2.com;
		
		location /images {
			root html;
			# 指定三种合法情况,如果不满足则为盗链
			valid_referers none blocked www.baidu.com;
			if ($invalid_referer) {
				# 以/开头的都进行转发
				rewrite ^/	/images/forbidden.png break;
			}
		}
	}