nginx(2)
上一篇: nginx(1)
负载均衡:
linux集群的一种常见方式,即由多台服务器组成一个服务器集合实现某个特定需求,其中每台服务器都是等价的,从而实现负载均摊的目的。
反向代理:
是指以代理服务器来接收用户的请求,然后将请求发给内部网络中的服务器,此时代理服务器向外表现为一个服务器,真正处理请求的是内部RS(Real Server)。
注意nginx的作用之一是可以隔离后端主机,以下的演示为了图简单,都没有这样配置。只要在nginx主机上配置2块网卡就可以,虚拟机里一块桥接,一块hostonly即可。
一、常见的负载均衡
1.用户手动选择:
在网站主页入口提供不同线路,不同服务器的连接方式,非常古老。
2.DNS轮询:
为一个域名例如www.demo.com配置多个DNS解析,就自动实现了负载均衡,例如114.80.81.71,114.80.81.72,114.80.81.73,则用户请求会自动分摊到这多个IP上。
例如,使用dig命令,看下baidu
dig www.baidu.com
得到:
...
;; ANSWER SECTION:
www.baidu.com. 30 IN CNAME www.a.shifen.com.
www.a.shifen.com. 30 IN A 14.215.177.38
www.a.shifen.com. 30 IN A 14.215.177.37
...
可以发现域名被均摊在不同的IP上,虽然DNS方式成本低,但是存在明显的缺点:
可靠性低:
假设有一台服务器发生故障,依旧会分发到该服务器。即使你马上从DNS中抹去,由于DNS缓存的存在,生效也需要一定时间,一般是几个小时。
负载分配不均衡:
DNS采用的是简单的轮询负载算法,不能区分服务器的差异等等。
3. 四/七层负载均衡设备
这里的层是指OSI网络模型。
物理层->数据链路层->网络层(IP)->传输层(TCP/UDP)->会话层->表示层->应用层。
现代的负载均衡技术通常位于第四层或者第七层,二者各有优劣,因此这里简单描述一下第四层和第7层:
第四层: 传输层
最重要的一层,它是二台计算机经过网络进行数据通信时,第一个端到端的层次,起到缓冲作用。
- 当网络层的服务质量不能满足需求时,它将提高服务,以满足高层需求;
- 当网络层的服务质量较好时,它只需进行很少的工作。
它还需要处理端到端的差错控制和流量控制等问题,最终为会话提供可靠的、无误的数据传输。
传输层的协议主要有: TCP UDP SPX等。
在IP协议栈中的第四层是TCP(传输控制协议)和UDP(用户数据报协议)所在的协议层,TCP和UDP包含端口号,四层交换机利用TCP/UDP端口提供的附加信息可以为网络交换机所利用,四层交换机利用这种信息来区分包中的数据,这是第四层交换的基础。
第7层:应用层
应用程序,常见的Telnet FTP HTTP SNMP等
第四层的负载均衡将一个Internet上合法注册的IP地址映射多个内部服务器的IP地址,对每次TCP连接请求动态使用其中一个内部IP地址,达到负载均衡的目的。
在第四层交换机中,此种均衡技术得到广泛的应用,一个目标地址是服务器群VIP(虚拟IP,Virtual IP address)连接请求数据包流经交换机,交换机根据源端和目的IP地址、TCP和UDP端口和一定的负载均衡策略,在服务器IP和VIP间进行映射,选取服务器群中最好的服务器来处理连接请求。
第7层的负载均衡控制应用层服务的内容,适合HTTP,通过检查HTTP包头,根据报头内的信息实现均衡。
常见的设备:
- 硬件交换机:有F5 BIG-IP, Citrix NetScaler, Redward, Cisco CSS ,Foundry等产品,价格相当不菲。中国采用F5的最多,有:新浪网、百度、搜狐等等。
- 软件L4 : LVS,详见http://www.cnblogs.com/carl10086/p/5934147.html
- 软件L7: Ningx、L7SW、HAProxy,由于可以在L7解析HTTP报头,因此往往配置性很强,比如可以按照轮询、权重、Hash等等放置均衡,同时可以检测后端应用服务器的健康状况。目前Nginx和HAProxy都很主流。
4. 多线多地区智能DNS解析与混合负载均衡方式
以新浪首页(www.sina.com.cn)为例,负载均衡同时用到了"多线多地区智能DNS解析"+"DNS轮询"+"四/七层负载"等等技术。
智能DNS解析能够根据用户本地设置的DNS服务器线路和地区,将对同一个域名解析到不同的IP上。
例如:
当北京电信用户访问www.sina.com.cn时,会被新浪的DNS服务器解析到北京电信机房的IP上;
当北京网通用户访问www.sina.com.cn时,会被解析到北京网通机房的IP上;
当广东电信用户访问www.sina.com.cn时,会被解析到广东电信机房的IP上;
等等...
将DNS服务器设置为219.141.136.10
vim /etc/resolv.conf
修改nameserver 219.141.136.10
使用dig www.sina.com.cn,得到:
;; ANSWER SECTION: www.sina.com.cn. 3220 IN CNAME jupiter.sina.com.cn. jupiter.sina.com.cn. 533 IN CNAME hydra.sina.com.cn. hydra.sina.com.cn. 534 IN A 218.30.108.226
修改nameserver 202.101.226.69,再查看
;; ANSWER SECTION: www.sina.com.cn. 30 IN CNAME jupiter.sina.com.cn. jupiter.sina.com.cn. 30 IN CNAME auriga.sina.com.cn. auriga.sina.com.cn. 30 IN A 61.172.201.239 auriga.sina.com.cn. 30 IN A 61.172.201.194 auriga.sina.com.cn. 30 IN A 61.172.201.195 auriga.sina.com.cn. 30 IN A 61.172.201.237
二、Nginx实现负载均衡
直接看一个负载均衡的配置文件:
# For more information on configuration, see: # * Official English Documentation: http://nginx.org/en/docs/ # * Official Russian Documentation: http://nginx.org/ru/docs/ # 使用的用户和组 user nginx; # 指定工作衍生的进程数(一般等于CPU的总数或者总核数的2倍) worker_processes 2; # 指定错误日志的存放路径,级别有{debug|info|notice|warn|error|crit} error_log /var/log/nginx/error.log crit; # 指定pid存放的路径 pid /run/nginx.pid; # Load dynamic modules. See /usr/share/nginx/README.dynamic. include /usr/share/nginx/modules/*.conf; #指定文件描述符的数量 worker_rlimit_nofile 51200; events { # 指定使用的网络I/O模型,Linux推荐使用epoll,FreeBSD推荐kqueue use epoll; # 允许的连接数 worker_connections 51200; } http { include /etc/nginx/mime.types; default_type application/octet-stream; # 设置字符集,如果一个网站有多种字符集这里不设置,让程序员在HTML通过Meta标签设置 #charset utf8; client_header_buffer_size 32k; large_client_header_buffers 4 32k; sendfile on; # tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; # 开启gzip压缩 gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.1; gzip_comp_level 2; gzip_types text/plain application/x-javascript text/css application/xml; gzip_vary on; # limit_zone crawler $binary_remote_addr 10m; # 允许客户端请求的最大单个文件字节数 client_max_body_size 300m; # 缓冲区代理缓冲用户端请求的最大字节数,可以理解为先保存到本地再传给用户 client_body_buffer_size 128k; # 跟后端服务器连接的超时时间_发起握手等候响应超时时间 proxy_connect_timeout 600; # 连接成功后_等候后端服务器响应时间_其实已经进入了后端的排队之中等待处理 proxy_read_timeout 600; # 后端服务器数据回传时间_就是在规定时间内服务器必须传完所有数据 proxy_send_timeout 600; # 代理请求缓存区_这个缓存区间会保存用户的头信息以提供Nginx进行规则处理_一般只要能够保存下头信息即可 proxy_buffer_size 16k; # 同上 告诉nginx保存单个用几个Buffer 最大用多大空间 proxy_buffers 4 32k; # 如果系统很忙的时候可以申请更大的proxy_buffers 官方推荐*2 proxy_busy_buffers_size 64k; # proxy缓存临时文件的大小 proxy_temp_file_write_size 64k; upstream java_server_pool { server 192.168.1.10:80 weight=4 max_fails=2 fail_timeout=30s; server 192.168.1.11:80 weight=4 max_fails=2 fail_timeout=30s; server 192.168.1.12:80 weight=2 max_fails=2 fail_timeout=30s; } upstream message_server_pool { server 192.168.1.13:3245; server 192.168.1.14:3245 down; } upstream bbs_server_pool { server 192.168.1.15:80 weight=1 max_fails=2 fail_timeout=30s; server 192.168.1.16:80 weight=1 max_fails=2 fail_timeout=30s; server 192.168.1.17:80 weight=1 max_fails=2 fail_timeout=30s; server 192.168.1.18:80 weight=1 max_fails=2 fail_timeout=30s; } # Load modular configuration files from the /etc/nginx/conf.d directory. # See http://nginx.org/en/docs/ngx_core_module.html#include # for more information. include /etc/nginx/conf.d/*.conf; # 第一台虚拟主机,即第一个应用 server { listen 80; server_name www.yourdomain.com; # Load configuration files for the default server block. access_log /var/log/nginx/server1.access.log combined; location / { # 如果后端服务器返回502、504、执行超时等错误、自动将请求转发到upstream负载均衡池中的另一台服务器,实现故障转移 proxy_next_upstream http_502 http_504 error timeout invalid_header; proxy_pass http://java_server_pool; proxy_set_header Host www.yourdomain.com; proxy_set_header X-Forward-For $remote_addr; } } # 第二台虚拟主机,即第二个应用 server { listen 80; server_name web.yourdomain.com; access_log /var/log/nginx/server2.access.log combined; # 访问http://web.yourdomain.com/message/***地址,反向代理message_server_pool这组服务器 location /message/ { proxy_pass http://message_server_pool; proxy_set_header Host $host; } # 除了/message/之外的请求,反向代理到java_server_pool这组服务器 location / { proxy_pass http://java_server_pool; proxy_set_header Host $host; proxy_set_header X-Forward-For $remote_addr; } } # 第三个应用 bbs应用 server { listen 80; server_name bbs.yourdomain.com *.bbs.yourdomain.com; location / { proxy_pass http://bbs_server_pool; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $remote_addr; } access_log off; } # Settings for a TLS enabled server. # # server { # listen 443 ssl http2 default_server; # listen [::]:443 ssl http2 default_server; # server_name _; # root /usr/share/nginx/html; # # ssl_certificate "/etc/pki/nginx/server.crt"; # ssl_certificate_key "/etc/pki/nginx/private/server.key"; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 10m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # # # Load configuration files for the default server block. # include /etc/nginx/default.d/*.conf; # # location / { # } # # error_page 404 /404.html; # location = /40x.html { # } # # error_page 500 502 503 504 /50x.html; # location = /50x.html { # } # } }
上面示例中,upstream指令用来设置一组在proxy_pass指令中使用的代理服务器。默认的负载方式是轮询。
upstream中的server指令用来指定后端服务器的名称和参数,服务器的名称可以是一个域名、一个Ip地址、端口号或者UNIX Socket。
proxy_set_header指令用于在反向代理的后端Web服务器发起请求时添加指定的Header头信息。
当后端服务器上有多个基于域名的虚拟主机时,要通过添加Header头信息Host,用于指定请求的域名,这样后端服务器才能识别由哪一个虚拟主机来处理。
可以使用Nginx反向代理实现动、静网页分离:
首先看看NetScaler怎么实现负载均衡的。
NetScaler的7层负载时基于TCP的,因此可以用于Web Server、MySQL数据库等基于TCP服务器的负载均衡。
HaProxy同时支持tcp mode和http mode,而Nginx仅仅支持HTTP、邮件协议。
三、proxy模块的相关指令
1. proxy_pass
Syntax: proxy_pass URL;
Default: —
Context: location, if in location, limit_except
配置一: location /hello { proxy_pass http://192.168.1.106:8080; }
此时客户端请求http://www.ysz202.com/hello/222 反代为 http://192.168.1.106:8080/hello/222,注意此时proxy_pass后的uri字符串中,只有host地址,后面连/都没有
配置二: location /bbs { proxy_pass http://192.168.1.106:8080/hello;}
此时客户端请求http://www.ysz202.com/bbs/222 才反代为 http://192.168.1.106:8080/hello/222,注意二者区别
配置三: location ~ /hello { proxy_pass http://192.168.1.106:8080; }
注意此时proxy_pass后面只能这样用,不能使用uri,否则是语法错误 ,使用nginx -t检测错误大致为:
nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block in /etc/nginx/nginx.conf:50
nginx: configuration file /etc/nginx/nginx.conf test failed
而动静分离,比如说java端开发只接收以.jhtml结尾的请求就可以通过简单配置就可以实现...
location ~* \.jhtml$ {
proxy_pass http://192.168.1.106:8080;
}
2. proxy_set_header
http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header
Syntax: proxy_set_header field value;
Default:
proxy_set_header Host $proxy_host;
proxy_set_header Connection close;
Context: http, server, location
修改nginx -> RS 的请求首部
proxy_set_header X-Real-IP $remote_addr; 增加自定义请求首部X-Real-IP,赋值为真正的客户端地址,在后端RS日志格式中可以使用该首部。
当使用动静分离且静态资源被代理到一台RS上时,可以为这个静态资源做缓存,这里只简单介绍,因为nginx的缓存功能确实比较粗糙
3. proxy_cache_path
Syntax: proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
Default: —
Context: http
Example: proxy_cache_path /var/cache/nginx/proxy levels=2:2 keys_zone=pcache:10m inactive=10m maxsize=1g;
- levels=2:1 表示2级目录,第一级目录使用特征码前二位,第二级目录为第三位,文件名称为剩下的
- keys_zone: 缓存空间名称为pache,最大使用内存上限为10m
- inactive: 默认非活动时间就是10min
- maxsize:使用磁盘空间最大为1g,默认没有限制
4.proxy_cache
Syntax: proxy_cache_bypass string ...;
Default: —
Context: http, server, location
proxy_cache pcache;
即在前面定义的keys_zone中定义的缓存空间可以在这里使用,默认是off
5.proxy_cache_key
Syntax: proxy_cache_key string; Default: proxy_cache_key $scheme$proxy_host$request_uri; Context: http, server, location
缓存中的key是什么,很关键
proxy_cache_key $request_uri;
6.proxy_cache_valid
Syntax: proxy_cache_valid [code ...] time;
Default: —
Context: http, server, location
不同的响应码使用不同的缓存策略
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
还有很多指令比如 proxy_cache_use_stale... 缓存相关的..
与连接相关的指令
7. proxy_connect_timeout;
Syntax: proxy_connect_timeout time;
Default:
proxy_connect_timeout 60s;
Context: http, server, location
Defines a timeout for establishing a connection with a proxied server. It should be noted that this timeout cannot usually exceed 75 seconds.
与后端建立连接的时长,默认是60s
8. proxy_read_timeout;
Syntax: proxy_read_timeout time;
Default:
proxy_read_timeout 60s;
Context: http, server, location
(两个响应报文之间的等待时长)等待后端服务器返回报文的时长,如果超过时长有可能返回502 Bad Gateway错误...
9. proxy_send_timeout time;
Syntax: proxy_send_timeout time;
Default:
proxy_send_timeout 60s;
Context: http, server, location
(两个请求报文发送之间的时长)向后端服务器发送请求报文超时时长
四、UpStream模块指令
http://nginx.org/en/docs/http/ngx_http_upstream_module.html
UpStream模块是Nginx负载的主要模块、它提供了一个简单方法来实现轮询和客户端IP之间的负载均衡,并可以对后端服务器进行后端检查。
upstream backend {
server backend1.example.com weight=5;
server backend2.example.com:8080;
server unix:/tmp/backend3;
}
server {
location / {
proxy_pass http://backend.com
}
}
1. 指令:upstream
Syntax: upstream name { ... }
Default: —
Context: http
定义后端服务器组
2.指令:server
Syntax: server address [parameters];
Default: —
Context: upstream
The following parameters can be defined
weight=number: 权重,默认是1
max_fails=number: 最大重试次数,默认是1
fail_timeout=time:失败超时时长,默认是10s
backup:跟keepalived中的sorry_server一样,备用主机
down: 手动标记服务器下线
3. ip_hash
Syntax: ip_hash;
Default: —
Context: upstream
指明调度方法为源地址hash,跟lvs调度方法中的sh一样,调度粒度太粗糙
Example:
upstream backend {
ip_hash;
server backend1.example.com;
server backend2.example.com;
server backend3.example.com down;
server backend4.example.com;
}
4. least_conn
Syntax: least_conn;
Default: —
Context: upstream
This directive appeared in versions 1.3.1 and 1.2.2.
动态调度方法,考虑到当前服务器的实时负载,最少连接算法
5. keepalive
Syntax: keepalive connections;
Default: —
Context: upstream
This directive appeared in version 1.1.4.
前面提到过nignx->client最好也使用keepalive,这里定义的不是时长,而是连接个数,一般后端有多少个主机,看着给.
Example:
upstream memcached_backend {
server 127.0.0.1:11211;
server 10.0.0.2:11211;
keepalive 32;
}
server {
...
location /memcached/ {
set $memcached_key $uri;
memcached_pass memcached_backend;
}
}
6.health_check
Syntax: health_check [parameters];
Default: —
Context: location
健康状态监测机制,可以定义的非常详尽,有参数match, uri, passes, fails等等。
Example:
http {
server {
...
location / {
proxy_pass http://backend;
health_check match=welcome;
}
}
match welcome {
status 200;
header Content-Type = text/html;
body ~ "Welcome to nginx!";
}
}
7. sticky
Syntax: sticky cookie name [expires=time] [domain=domain] [httponly] [secure] [path=path];
sticky route $variable ...;
sticky learn create=$variable lookup=$variable zone=name:size [timeout=time];
Default: —
Context: upstream
This directive appeared in version 1.5.7.
也是调度算法的一种,调度粒度非常的精细,基于cookie进行绑定,使得每个client请求到相同的server...实例如下:
upstream backend {
server backend1.example.com;
server backend2.example.com;
sticky cookie srv_id expires=1h domain=.example.com path=/;
}
8. hash
Syntax: hash key [consistent];
Default: —
Context: upstream
This directive appeared in version 1.7.2.
也是调度方法的一种,基于某一个key进行绑定,比如ip_hash就是一种基于源地址IP进行hash的调度策略,比如ip_hash相当于 hash $remote_addr;
ip_hash $request_uri等等...
补充:
nginx还可以设置一些额外参数,比如可以使用 add_header X-Cache $upstream_cache_status 来表明缓存是否命中...
nginx还可以实现读写分离,将PUT,GET请求等等分离...
五、Nginx ReWrite模块
Rewrite的主要功能是实现URL的重写,依赖于PCRE库(正则表达式库)。
通过Rewrite规则,可以实现规范的URL,根据变量来做URL转向以及选择配置。例如,一些使用MVC框架的程序只有一个入口,可以通过Rewrite来实现。
一些动态的URL地址需要伪装成为静态HTML,便于搜索引擎抓取,也需要Rewrite来处理。由于一些目录结构、域名变化的旧URL,须要跳转到新的URL上,也可以使用Rewrite来处理。
5.1 相关指令
break
完成当前的规则集,不再处理rewrite指令
if ($slow){
limit_rate 10k;
break;
}
if
条件判断:
(1) 变量名:如果是空字符串"",或者任何以0开始的字符串表示false
(2) = 或者 !=
(3) ~ 区分大小写的正则匹配
(4) ~* 不区分大小的正则匹配
(5) "!~" 和 "!~*" 与"~"和"~*"相反,表示不匹配
(6) "-f"和-"!f"表示文件是否存在
(7) "-d"和-"!d"表示目录是否存在
(8)-e, !-e:是否存在;
(9) -x, !-x:是否存在且可执行;
if ($http_user_agent ~ MSIE) { rewrite ^(.*) /msie/$1 break; } if ($http_cookie ~* "id=([^;] +)(?:;|$)" ){ set $id $1; } if ($request_method =POST ){ return 405; } if (!-f $request_filename){ break; proxy_pass http://127.0.0.1; } if ($slow){ limit_rate 10k; } if (!invalid_referer){ return 403; } if ($args ^~ post=140){ rewrite ^ http://example.com/permanent; }
return
返回状态码给客户端
支持的状态码:
204 No Content
服务器成功处理了请求,但是无须返回任何实体内容,并且希望返回更新了的元信息。
400 Bad Request
由于包含语法错误,当前请求无法被服务器理解。客户端需要修改请求
402 Payment Required
为将来可能的需求而预留
403 Forbidden
服务器已经理解了请求,但是拒绝执行。与401不同,身份验证不能提供任何帮助。
404 Not Found
资源在服务器上不存在
405 Method Not Allowed
请求行中指定方法不能被应用于当前资源。该响应必须有一个Allow头信息列出允许的方法。
406 Not Acceptable
请求资源内容特性无法满足请求头的条件。
408 Requset Timeout
请求超时
410 Gone
被请求的资源在服务器上不再可用,且没有已知道的转发地址。
一般用于管理员维护网站资源,用于通知某个资源已经不可用
411 Lengh Required
服务器拒绝在没有定义Content-Length头信息的情况下接受请求。
413 Request Entity Too Large
请求实体超过了服务器愿意处理的范围
416 Requested Range Not Satisfiable
如果请求中包含了Range头,并且Range中指定的任何数据范围都与当前资源的可用范围不重合,同时请求中又没有定义If-Range请求头。
500 Internal Server Error
服务器遇到了未预料的错误
501 Not Implemented
服务器不支持当前请求所需要的某个功能
502 Bad Gateway
作为网管或者代理服务器从上游接收到无效的响应。
503 Service Unaviable
临时服务器维护或者过载,服务器无法处理请求。
504 Gateway timeout
上游服务器未能及时给出响应
5.2 rewrite指令
根据表达式来重定向URI,或者修改字符串。指令根据配置文件中的顺序来执行。
注意重写表达式只对相对路径有效。如果想要配对主机名,应该使用if语句。
if ($host ~* www\.(.*)) {
set $host_without_www $1;
rewrite ^(.*)$ http://$host_without_www$1 permanent;
}
- last: 重写完成之后停止对当前uri在当前location中的后续其他重写操作,改为对新url的新一轮处理;
- break: 重写完成之后停止对当前uri在当前locatioin中后续其他重写操作;
- redirect: 重写完成之后会返回一个客户端的临时重定向,由客户端重新发起请求(302)
- permanent:重写完成之后会返回给客户端一个永久的重定向,由客户端重新对新的url发送请求(301)
last和break有细微的差别,使用alias要使用last,使用proxy_pass要使用break。
last在本条rewrite执行完成后,会对其所在的server重新发生请求,而break标记则在匹配后,终止匹配,不再匹配其后面的规则。
例如以下的语句如果把break换成last会死循环,不断的对自己发送请求。
location /cms/ {
proxy_pass http://test.yourdomain.com;
rewrite "^/cms/(.*)\.html$" /cms/index.html break;
}
如果要把类似url /photos/123456重定向到
/path/to/photos/123456,可以使用:
rewrite "/photos/([0-9]{2})([0-9]{2})([0-9]{2})" /path/to/photos/$1/$1$2/$1$2$3.png;
下面是一些例子:
if (!-e $request_filename){ rewrite ^/(.*)$ /index.php last; } #多目录转成参数abc.domain.com/sort/2=>abc.domain.com/index.php?act=sort&name=abc&id=2; if ($host ~ (.*)\.domain.com) { set $sub_name $1; rewrite ^sort\/(\d+)\/?$ /index.php?act=sort&cid=&sub_name&id=$1 last; } #目录兑换/123456/xxxx -> /xxxx?id=123456; rewrite ^/(\d+)/(.d+)/(.+)/ /$2?id=$1 last; #如果客户端使用IE浏览器,则重定向到/nginx-ie目录下: if ($http_user_agent ~ MSIE) { rewrite ^(.*)$ /nginx-ie/$1 break; } #禁止访问多个目录 location ~ ^ /(cron|templates)/ { deny all; break; } location ~ ^/data { deny all; } location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; } location ~ .*\.(js|css)$ { expires 1h; }