nginx
配置说简单也简单,说复杂也复杂,入门简单,精通难(怎么感觉有点像javascript
?),主要是nginx
自身有很多专属的语法和命令以及让人捉摸不透的运行规则,不系统的学习一下很容易出错,有时候即使工作很多年的同学想配置一些特殊场景时可能也会折腾半天才能搞定。
一个nginx配置文件大体长下面这样,绝大部分配置都写在http
里面(省略了很多默认配置):
http {
include mime.types;
default_type application/octet-stream;
server {
listen 80;
server_name localhost;
charset utf-8;
location / {
root html;
index index.html index.htm;
}
location /test {
proxy_pass https://wangmiaozero.cn;
}
}
}
一般开发者需要经常打交道的大部分配置都在server
和location
里面。
一些注意事项:
#
;;
结尾,不写会报错;http
、server
、location
等多个上下文,有的则只能出现在某一种上下文里面;常见命令简要说明:
include
:引入外部文件,当配置文件较多时可以分开写在不同文件,然后一次性引入即可,例如include servers/*;
default_type
:设置默认内容类型;server_name
:域名,支持空格配置多个;listen
:监听端口;proxy_pass
:转发;root
:指定根目录;rewrite
:重写;location粗略看只有4种配置:精确匹配、^~开头匹配、正则匹配、/匹配,细分有7种,优先级从高到低依次是:
=
:精确匹配,优先级最高;/test/aaa/bbb.html
:完整路径匹配,如果访问路径和location配置完全相等,那么这条配置自然会优先匹配,它的优先级仅次于精确匹配;^~ /test
开头:匹配以/test
开头的地址,如果匹配成功会停止向后继续搜索;~
:区分大小写的正则匹配,如果匹配成功会停止向后继续搜索;~*
:不区分大小写的正则匹配,如果匹配成功会停止向后继续搜索,2种正则匹配优先级相同;/test
:匹配以/test
开头的地址,匹配成功后还会继续往后匹配,直至结束,以最后一个匹配为准;/
:匹配以/
开头的地址,由于任何地址都是以/
开头,所以所有地址都会命中这个规则,但它优先级最低,一般放在最后做backup;记住以下2点就可以了
精确匹配
> 完整路径匹配
> ^~
> 正则匹配 >/
起始路径 > /
;/
开头配置是匹配成功还会继续往后匹配之外,其它所有配置都是成功就立即停止;
- 多个正则之间、多个
^~
之间,第一个匹配生效,与顺序有关;- 都是/开头时,最长匹配生效,和先后顺序无关;
关于正则写法,除了开头结尾不需要/
之外,和JS
的正则差不多,要实现精确匹配的话也需要^
和$
配合,例如:
# 访问 /ggg/test/aaa/bbb.txt时返回222
# 访问 /test/aaa/bbb.txt时返回111
location ~ ^/test/aaa/.*\.txt$ {
return 200 '111';
}
location ~ /test/aaa/.*\.txt$ {
return 200 '222';
}
所以,可以把^~
看成一种特殊的正则,只不过它的优先级高于正则,^~
能实现的,普通正则一定也能实现:
# 下面2中效果完全相同,唯一不同是优先级不同
# 访问 /test/aaa/bbb.txt 时返回222
location ~ ^/test/aaa/ {
return 200 '111';
}
location ^~ /test/aaa/ {
return 200 '222';
}
/hello.json
时,第二个生效(返回222
):location /hello.json {
default_type text/html;
return 200 '111';
}
location =/hello.json {
default_type text/html;
return 200 '222';
}
/test/aaa/bbb.json
时,第二个生效(返回222
):# 虽然222在后面,但是由于^~优先级更高,所以第二个生效
location ~ /test/aaa {
return 200 '111';
}
location ^~ /test/aaa {
return 200 '222';
}
/test/aaa/bbb.json
时,第1个生效(返回111
):# 多个正则之间,第一个匹配生效,与顺序有关
# 虽然越往后匹配越精确,但是由于是正则,匹配到第一个就停止匹配
location ~ / {
return 200 '111';
}
location ~ /test/aaa {
return 200 '222';
}
location ~ /test/aaa/.*\.(gif|jpg|jpeg)$ {
return 200 '333';
}
/test/aaa/bbb.json
时,第2个生效(返回222
):# 不管二者如何交换顺序,始终都是第二个生效
location /test/aaa {
return 200 '111';
}
location ~ /test/aaa {
return 200 '222';
}
/test/aaa/bbb/ccc.json
时,第2个生效(返回222
):# 都是/开头时,最长匹配生效,和先后顺序无关
location /test/ {
return 200 '111';
}
location /test/aaa/ccc {
return 200 '222';
}
location /test/aaa/ {
return 200 '333';
}
location / {
return 200 '444';
}
rewrite
和proxy_pass
的区别:
前者只能重写相同域名,后者可以转发任意网址;
server {
listen 443 ssl;
server_name localhost;
ssl_certificate cert.crt;
ssl_certificate_key cert.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
root html;
index index.html index.htm;
}
}
例如,将 http://www.test.com
转发到 http://127.0.0.1:6666
:
server {
listen 80;
server_name www.test.com;
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;
# 代理连接超时设置为10秒钟,默认60秒太久了
proxy_connect_timeout 10;
proxy_pass http://127.0.0.1:6666;
}
}
如果还需要增加跨域配置,只需要再在上面加上:
server {
listen 80;
server_name www.test.com;
location / {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers' 'Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With';
add_header 'Access-Control-Allow-Methods' 'POST, GET, OPTIONS';
# 省略其它部分...
proxy_pass http://127.0.0.1:6666;
}
}
例如,将带www的域名自动跳转到没有www的域名。
下面的例子是将 http://www.wangmiaozero.cn 永久跳转到 http://wangmiaozero.cn ,丢弃域名后面的路径:
server {
listen 80;
server_name www.wangmiaozero.cn;
location / {
rewrite ^(.*) http://wangmiaozero.cn permanent;
}
}
将 http://www.wangmiaozero.cn 永久跳转到 https://wangmiaozero.cn ,不丢弃域名后面的路径:
server {
listen 80;
server_name www.wangmiaozero.cn;
location / {
rewrite ^(.*) https://wangmiaozero.cn$document_uri permanent;
}
}
location /test {
default_type application/json;
return 200 '{"code": 0, "message": "ok"}';
}
所谓泛二级域名配置,就是将 *.xxx.com
根据子域名的不同自动进行不同配置,而不需要手动一个个写。
下面的例子中,将 http://*.wangmiaozero.cn/
转发到 http://127.0.0.1:8080/*/
http {
include mime.types;
default_type application/octet-stream;
# 使用变量来构造server地址时必须设置resolver
resolver 8.8.8.8;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name www.wangmiaozero.cn www.test.com test.com;
location / {
rewrite ^(.*) http://wangmiaozero.cn permanent;
}
}
server {
listen 80;
server_name demo.wangmiaozero.cn;
location / {
root D:\Workspace\github\demo;
index index.html index.jsp;
}
}
server {
listen 80;
server_name ~^(.+)?\.wangmiaozero\.cn$;
location / {
# 注意这里用localhost的话会报错,必须用127.0.0.1
proxy_pass http://127.0.0.1:8080/$1$request_uri;
}
}
}
正如上面所配置的,可能某几个特殊子域名需要特殊配置,所以可以在以上配置之前加上特殊配置(注意nginx的优先级,找到第一个匹配的就不会再继续往下匹配了)。
如下:
server {
listen 80;
server_name ~^(.+)?\.wangmiaozero\.cn;
location / {
root D:\Workspace\github\test\$1;
index index.html index.jsp;
}
}
原因是Nginx0.6.18
以后的版本中启用了一个resolver
指令,在使用变量来构造某个server
地址的时候一定要用resolver
指令来制定DNS
服务器的地址,所以解决这个问题的方法很简单:
在nginx
的配置文件中的http{}部分添加一行resolver 8.8.8.8;
即可
如果server_name
配置很多的话可能会报错如下:
could not build the server_names_hash, you should increase server_names_hash_bucket_size: 64
解决方法:在http{}
里面增加server_names_hash_bucket_size
设置,一般是2的指数,比如我这里512:
http {
include mime.types;
default_type application/octet-stream;
# 域名过多时需要配置这个参数
server_names_hash_bucket_size 512;
}
server {
listen 80;
server_name www.test.com;
location / {
root E:/Workspace/test/htdocs/;
index index.html;
}
location /test_root {
# 如果访问 www.test.com/test_root/aaa/index.html,实际访问的是 htdocs/test_root/aaa/index.html
# 经测试,无论test_root后面结尾是否加斜杠,还是htdocs结尾是否加斜杠,test_root 都会出现在真实访问路径里面
root E:/Workspace/test/htdocs/;
index index.html;
}
location /test_proxy1 {
# 如果访问 www.test.com/test_proxy1/aaa/index.html,实际访问的是 www.proxy.com/test_proxy1/aaa/index.html
proxy_pass http://www.proxy.com;
proxy_buffering off;
}
location /test_proxy2 {
# 如果访问 www.test.com/test_proxy2/aaa/index.html,实际访问的是 www.proxy.com//aaa/index.html
proxy_pass http://www.proxy.com/;
proxy_buffering off;
}
location /test_proxy3 {
# 如果访问 www.test.com/test_proxy3/aaa/index.html,实际访问的是 www.proxy.com/bbb/aaa/index.html
proxy_pass http://www.proxy.com/bbb;
proxy_buffering off;
}
}
假设IP是:192.168.1.111
,然后将浏览器代理设置成:192.168.1.111:6587
即可。
# 代理服务器
server {
listen 6587;
resolver 8.8.8.8;
location / {
proxy_pass http://$http_host$request_uri;
#allow 127.0.0.1;
#deny all;
}
}
但是这种转发有一个很大缺点,就是不支持HTTPS,而且好像也不怎么稳定,一般不推荐使用nginx来做代理服务器。
以360急速浏览器为例:
nginx
内置了大量的 开 头 的 全 局 变 量 , 这 些 变 量 在 有 些 时 候 会 非 常 有 用 , 而 且 网 上 很 少 有 介 绍 的 很 全 面 的 文 章 , 比 如 我 今 天 想 找 一 个 获 取 ‘ o r i g i n ‘ 的 变 量 , 找 半 天 没 找 到 , 最 后 自 己 根 据 规 律 随 便 蒙 一 个 ‘ 开头的全局变量,这些变量在有些时候会非常有用,而且网上很少有介绍的很全面的文章,比如我今天想找一个获取`origin`的变量,找半天没找到,最后自己根据规律随便蒙一个` 开头的全局变量,这些变量在有些时候会非常有用,而且网上很少有介绍的很全面的文章,比如我今天想找一个获取‘origin‘的变量,找半天没找到,最后自己根据规律随便蒙一个‘http_origin竟然对了! 以下是我已亲自验证过的(测试版本:
v1.11.8`):
$remote_addr
:客户端IP地址;http_host
:request.getHeader(‘Host’);http_origin
:request.getHeader(‘Origin’);http_referer
:request.getReferer(‘Referer’);origin
和host
的区别是,我在A页面调用B页面的接口,那么A是origin,B是host;origin
和Referer
的区别是,前者是调用页面域名(如http://www.aaa.com
),后者则是不包括哈希的完整URL(如http://www.aaa.com/index.html
)
以下摘自网络,未亲自验证:
KaTeX parse error: Expected 'EOF', got '#' at position 8: args : #̲这个变量等于请求行中的参数,同query_string
$content_length : 请求头中的Content-length字段。
$content_type : 请求头中的Content-Type字段。
$document_root : 当前请求在root指令中指定的值。
$host : 请求主机头字段,否则为服务器名称。
$http_user_agent : 客户端agent信息
$http_cookie : 客户端cookie信息
$limit_rate : 这个变量可以限制连接速率。
$request_method : 客户端请求的动作,通常为GET或POST。
$remote_addr : 客户端的IP地址。
$remote_port : 客户端的端口。
$remote_user : 已经经过Auth Basic Module验证的用户名。
$request_filename : 当前请求的文件路径,由root或alias指令与URI请求生成。
$scheme : HTTP方法(如http,https)。
$server_protocol : 请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
$server_addr : 服务器地址,在完成一次系统调用后可以确定这个值。
$server_name : 服务器名称。
$server_port : 请求到达服务器的端口号。
$request_uri : 包含请求参数的原始URI,不包含主机名,如:”/foo/bar.php?arg=baz”。
u r i : 不 带 请 求 参 数 的 当 前 U R I , uri : 不带请求参数的当前URI, uri:不带请求参数的当前URI,uri不包含主机名,如”/foo/bar.html”。
d o c u m e n t u r i : 与 document_uri : 与 documenturi:与uri相同。
前端架构师官方群:634196762
参考 https://segmentfault.com/a/1190000002797606 和 http://www.cnblogs.com/AloneSword/archive/2011/12/10/2283483.html
安利下这篇文章(写得很不错):https://www.cnblogs.com/redirect/p/10140267.html