Nginx 随记(二)—— 配置
Nginx配置文件
在Nginx官方文档上有更为详细的配置说明,在此将我用到的部分进行介绍。
本文介绍的版本是目前最新的稳定版nginx-1.10.3
内容基础,基本上临近的版本都可以适用。
Nginx安装完后,默认的总配置文件在/etc/nginx/nginx.conf
修改前,建议先copy一份进行备份。
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
以下是初始的内容:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
#mail {
# # See sample authentication script at:
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
# # auth_http localhost/auth.php;
# # pop3_capabilities "TOP" "USER";
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
# server {
# listen localhost:110;
# protocol pop3;
# proxy on;
# }
#
# server {
# listen localhost:143;
# protocol imap;
# proxy on;
# }
#}
在此对配置文件中的一些配置项进行说明。其他没介绍到的地方以后会开新的博文介绍。这个配置文件若无必要的改动,保持默认即可,为了增加配置文件的可读性,我们具体的网站配置在另一个配置文件上。
第1行 user www-data
此处的www-data是Nginx要使用的Linux用户名,当后面配置Nginx进行静态资源缓存时,出现访问静态资源404等情况时,首先检查文件的权限能否被这个用户所读取,最起码是需要读和执行权限的。
gzip on;
此处是打开gzip压缩,在用户和上游服务器之间进行gzip压缩有利于减少带宽。值得一提的是,如果上游服务器是IIS,并且在IIS里配置了对URL出入站深度重写的话,是不允许打开gzip压缩的,否则IIS无法解析html的内容并且改写里面的超链接、静态资源引用等地址。
第63行 include /etc/nginx/conf.d/*.conf;
这行是引入我们网站的配置文件,为此我们需要在/etc/nginx/conf.d/
下面新建一个配置文件,文件名要以.conf
结尾,比如yyq.conf
。
接下来就是配置我们网站了。
#网站配置
在/etc/nginx/conf.d/yyq.conf
里最基础的内容如下:
server
{
listen 80;
access_log /var/log/nginx/myblogaccess.log;
error_log /var/log/nginx/myblogerror.log;
proxy_ignore_client_abort on;
charset utf-8;
location /
{
proxy_set_header Host $host;
proxy_set_header X-real-ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://localhost:1234/;
#此处的地址是上游服务器Tomcat的地址,注意要带上末尾分号前的/号,否则会出现意想不到的结果O(∩_∩)O哈哈~,后面再讲
}
}
listen 80;
让Nginx去监听80端口,将用户的请求反向代理到下面配置的服务器上。
access_log
error_log
是指定我们网站的访问日志、Nginx代理过程中产生的错误日志存放路径
location /
这是一个location块,此处的/是匹配所有URL,也是通常情况下需要匹配的。
proxy_set_header Host $host;
经过了Nginx代理后,上游服务器无法知道用户请求时,Http协议里的Host是什么,在此把用户请求的Host带到上游服务器。
proxy_set_header X-real-ip $remote_addr;
由于经过了代理,上游服务器同样是没办法知道用户IP的,获取到的都是Nginx所在的服务器内网IP。所以在此把用户真实的IP放到Http协议头里。
上游服务器,比如tomcat,在Java代码中可以通过
HttpServletRequest.getHeader("X-real-ip")
获取到具体IP,其中X-real-ip
是可以自定义命名的,但按照惯例是命名如此,注意区分大小写,并且一定要和nginx配置文件里保持一致。
这样配置好后,用户在浏览器上访问Nginx所在的服务器,端口号80时,就可以通过反向代理的方式进行访问我们架设在本机localhost,端口号为1234上的Web服务器了。
接下来介绍Nginx核心的几个命令:location
、rewrite
、root
、alias
location
语法:
location [=|~|~*|^~] /uri/
{
...
}
[ ]表示可省。其中每个参数的含义如下:
=
表示精确匹配后面的URL路径,要求用户输入的路径必须和此处输入的完全相同,才能进入这个Location块处理。同时优先级是最高的。
^~
表示URL以某个常规字符串开头进行匹配。同时注意,nginx不对url做编码。
最常用的两项:
~
表示区分
大小写地进行正则匹配
~*
表示不区分
大小写地进行正则匹配
!~
和!~*
是对上面两种写法正则匹配的取反,分别为区分大小写不匹配及不区分大小写不匹配
当有多个Location存在时,他们优先级=
>^~
>无正则的常规匹配
当匹配到一个Location时,就进入Location内部处理,若无rewrite等命令时,不会再去匹配其他Location。
echo调试输出
看了上面的匹配规则后,我是手痒想写几条来体验体验,但在这之前,我们遵循由简到繁的原则,用最简单的方式去验证我们写的规则是否正确,能否如期进行匹配。
为此,使用echo模块进行快速的调试输出。echo模块是国人开发的模块,默认不集成在nginx源码中,需要重新编译nginx源码并加入此模块才能被识别。
先来个Hello World
location /hello {
echo "hello, world!";
}
每一行记得都必须有一个英文半角的分号;
结束。
加入了这一段location后,配置文件如下:
server
{
listen 80;
access_log /var/log/nginx/myblogaccess.log;
error_log /var/log/nginx/myblogerror.log;
proxy_ignore_client_abort on;
charset utf-8;
location /hello
{
echo "hello, world!";
}
location /
{
proxy_set_header Host $host;
proxy_set_header X-real-ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://localhost:1234/;
}
}
首先让nginx检查我们编写的配置文件有没有语法错误。
使用命令nginx -t
检查,若显示OK,则继续使用/etc/init.d/nginx reload
命令让nginx去reload我们的配置文件。
若检查不通过,请按照它给提示修改,通常是语法错误,逻辑错误才不会提示你呢 ( ̄┰ ̄*)
这样,我们做的修改才会生效。
服务器IP此处假定为192.168.1.100
然后用浏览器去访问 http://192.168.1.100/hello
看看是不是出现了hello, world!
有个比较麻烦的地方是,部分浏览器,比如Google Chrome浏览器,会将这样的echo内容当成一个文件去下载。
那么就要再这基础上稍微修改,让它告诉浏览器这是一个文本,不是一个文件。
location /hello {
default_type text/plain;
echo "hello";
}
我在我的服务器上进行测试,效果如图
这样,我们就实现了在nginx上向浏览器输出hello world。同时,我们也实现了一个最简单的常规匹配。
正则匹配
正则表达式贯穿整个nginx系统,nginx使用的是PCRE
的正则表达式库。
关于正则表达式请见 wiki百科
继续阅读下面之前,建议先掌握正则表达式的写法。而不是一味的在网上随处复制、尝试。正则表达式别看标点符号乱七八糟混乱一片,当你耐下心来,多做几次尝试,或许你就会掌握其中的奥秘,并感慨原来正则是多么伟大的一个存在。 可参见 菜鸟教程
location 正则匹配
现在来尝试一下在nginx中使用正则匹配。
想要匹配 /hello/1
这样的路径。末尾的数字1可以是其他任意长度、范围的数字。
修改刚才的location区段如下:
location ~ ^/hello/\d+$ {
default_type text/plain;
echo "hello";
}
我们在URL路径上的子目录加了一个子目录分隔符/
以及正则表达式的元字符\d
和+
。正则表达式匹配以^
开始,$
结束。
~
标志表示区分大小写进行匹配,元字符\d
表示匹配一个一位的数字,紧跟其后的+
表示匹配一个或多个前面的元字符(\d
),这样组合起来\d+
就表示匹配任意数字了
这样,我们在浏览器输入/hello/888
就会看到nginx向我们say hello.
也表示成功匹配到这块location。
同样,如果我们要阻止一个非法的URL进来,通常是拒绝对一些文件的直接访问,可以使用deny all;
location ~ ^/hello/\d+$ {
deny all;
}
这样,用户访问/hello/888时,就会被nginx拒绝,返回页面代码403
基本上只要经过工具测试过的正则表达式,直接复制放在nginx里使用是没有什么问题的。
rewrite
Nginx最强大的一个模块之一,就是rewrite。 rewrite是用来对URL正则匹配然后重写,有了它可以对URL进行优化、更好地优化SEO提升网站排名等。
语法 | 使用区段 |
---|---|
rewrite regex replacement flag | server, location, if |
参数 | 说明 |
---|---|
regex | 正则表达式,指明要匹配的URL路径,无论在哪个区段使用rewrite,都是从根目录开始匹配 |
replacement | 要替换的内容,会讲匹配到的路径重写成这个内容 |
flag | 标志位 |
flag | 说明 |
---|---|
last | 停止处理当前重写模块指令,之后搜索location与更改后的URI匹配。 |
break | 完成当前设置的重写规则,停止执行其他的重写规则。 |
redirect | 返回302临时重定向,如果替换字段用 http:// 开头则被使用。 |
permanent | 返回301永久重定向。 |
通常在最开始的时候,会把rewrite写在server区段,让它能够匹配的范围是所有URL,然后经过重写后的URL会进入下面的location处理。
例子
Hello World
server
{
listen 80;
access_log /var/log/nginx/myblogaccess.log;
error_log /var/log/nginx/myblogerror.log;
proxy_ignore_client_abort on;
charset utf-8;
rewrite /hi /hello last;
location /hello
{
default_type text/plain;
echo "hello";
}
location /
{
proxy_set_header Host $host;
proxy_set_header X-real-ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://localhost:1234/;
}
}
继续之前的例子,我们对/hi
这个URL重写成/hello
。标志位是last
,指明重写完后重新匹配其他location,在这里自然会匹配到下面的location /hello
,然后echo "hello";
这样,我们就可以实现通过访问/hi
来访问/hello
,而这样的重写,用户是完全不知情的,浏览器地址栏也不会变,因为不是重定向,只是在Nginx内部改变。
伪静态
将动态网页伪装成静态网页的话,对搜索引擎是非常友好的。因为静态网页不怎么需要改变,搜索引擎可以比较长久地缓存。同时抓取时,也比较方便。而对于搜索引擎,最重要的区别在于这个URL地址上。
一个带参数的URL地址,比如/hi?id=1
很明显通常这表明是一个动态网页。而且参数只有一个,这样如果我们美化一下URL,把参数去掉变成/hi/1
或者直接伪装成静态文件/hi/1.html
,这样是很不错的。
如果不用nginx,那么就只能靠上游服务器它们自己去处理了,但这样做的话,首先因为上游是处理动态页面的程序,性能不如直接由nginx直接来得快。其次如果上游是一个集群服务器,配置时就比较麻烦了。 在tomcat中,可以使用 URL Rewrite 这个模块。
在Nginx中利用rewrite很轻松就可以做到伪静态。
继续上面的例子,在server区段中
rewrite ^/hi/(\d+)\.html$ /hi?id=$1 last;
上述正则表达式表示匹配 /hi/任意数字.html
这样的URL,其中的(\d+)
括号( )表示括号内的字符串作为一个新的子字符串返回,那么表达式的匹配结果里就会有两个结果:
其中第0个结果表示完整的原字符串,只要成功匹配,就会有这个第0结果。
然后第1个结果正是我们用小括号括起来的子串,即1.html里的1
然后在rewrite的重写部分,使用$结果索引
获取匹配到的结果,这里使用$1
表示取第1个结果。
因为正则表达式里的.
是有特殊含义的,表示任意一个除了\n
换行以外的字符,所以1.html里的文件后缀名分隔符.
需要转义表达,因此写为\.
这样就会把带有参数的原始URL传给上游服务器了。我这里做了一个简单测试页面,上游服务器里动态页面获取参数后返回出来。
如果担心上游服务器可能配置出问题,无法判断是rewrite写错还是上游服务器代码写错的话,推荐把rewrite后面的标志位last
改为redirect
这样,会请求一个临时重定向操作,当浏览器访问/hi/123.html
时,如果重写成功,地址栏上会变成重写后的URL地址
这样,就可以很清楚地知道我们写的rewrite有没有正常工作。
当有多个参数需要伪静态时,比如一篇博客文章按照年月归档,那么可以这样写:
rewrite ^/hi/(\d{4})/(\d{1,2})/(\d+)\.html$ /hi?y=$1&m=$2&id=$3 redirect;
其中,大括号{}表示前一个元字符重复的次数。\d{4}
表示四个\d
即\d\d\d\d
,即匹配年份的四位数字。
{1,2}
表示至少重复1次,最多重复2次。即匹配月份的数字1位或者2位数。
然后,其实匹配结果的索引是按括号堆栈的顺序排列的。
当我们输入/hi/2017/1/123.html
时,就会被替换成/hi?y=2017&m=1&id=123
这正是我们想要的多参数的情况。
alias
alias和root一样的功能,均是让nginx去访问一些静态文件。 其中alias本身的功能是对目录起一个别名。
例子
假设我们nginx服务器上/opt/yyq/hi
这个目录下有一个1.jpg
文件,
而我们这样最简单地使用alias
location /hi/
{
alias /opt/yyq/hi/;
#记得必须加上末尾分号前面的 / 斜杠。
}
当用户访问/hi/1.jpg
时,nginx会直接在本机上的/opt/yyq/hi
目录下寻找1.jpg
,如果找到,将直接把这个图片返回给用户浏览器,不再将请求发往上游服务器。
正如其功能所说,alias只是一个目录的别名,nginx会很粗暴地将
URL/hi/
换成本地路径/opt/yyq/hi/
,然后把location匹配的url后半截
1.jpg
给加到本地路径后面,最终形成/opt/yyq/hi/1.jpg
这样的本地路径。
所以,如果alias末尾的分号前没有/
的话,会形成/opt/yyq/hi1.jpg
这样的错误路径,大家使用tail命令去观察nginx的错误日志文件时,就会看见。
tail -300f 文件路径
默认在/var/log/nginx目录里面,具体看server里的error_log配置项
同理,若/hi/yyq/1.jpg
是这样的一个访问请求,使用刚才的alias,也一样会去寻找/opt/yyq/hi/
目录下的yyq
子目录里的1.jpg
所以,这可以非常方便用在静态资源文件上。
root
root也是同alias,但有点不同的是,root会比alias更加粗暴 (^__^)
location /hi
{
root /opt/yyq/hello;
}
把刚才的例子里的alias改写成root后,当用户访问/hi
时,nginx会将location里的整段url拼接到root指定的本地路径的后面
,形成/opt/yyq/hello/hi
这样的路径。
访问/hi/1.jpg
我们可以在错误日志上很清楚地看到路径被神奇地写成了/opt/yyq/hello/hi/1.jpg
这就是root
与alias
的最大不同之处,另外在使用root的时候,location最后面的斜杠有或者无(/hi/
和/hi
)以及root后面的路径末尾的斜杠有或者无(root /opt/yyq/hello/;
和root /opt/yyq/hello;
)均是没有任何影响,都是同样的匹配结果,而相比alias就不一样了。
##使用root和alias的好处 我们知道nginx对静态资源的访问,无论是在高并发还是多连接的情况下,与后台的tomcat等程序相比,性能是相当出众的。 tomcat是基于Java虚拟机的,每当有一个请求到来,就会启动一个servlet,分配大量内存空间,却仅仅是为了一个硬盘上的静态资源文件,没有任何数据库调用、业务逻辑处理,就做了很多无用的事情。 有了nginx在前面罩着的话,tomcat就可以更加专注于处理动态页面,而不用再去处理静态资源。
同时,建议在静态资源的location里,除了有root或者alias外,再加上一个expires 7d;
指示资源的缓存过期时间,其中7d
表示缓存七天。
location /hi/
{
alias /opt/yyq/hi/;
expires 7d;
}
这样浏览器就会把这个资源缓存下来,当用户再次访问这个资源的时候,浏览器会去检查本地缓存,如果有,而且没过期,那么就不会再向服务器发一次GET请求,而是直接从本地获取,速度更加快了。 个人建议一些图片文件,恰巧也是不经常修改的,而且体积也很大,所以可以长时间缓存,几天一个月,而一些js、css文件等,文件比较小,也经常修改,缓存时间稍微短一点,比如1h一个小时。