[关闭]
@dungan 2018-10-16T06:23:07.000000Z 字数 10215 阅读 106

Nginx

Nginx 配置实战

虚拟主机配置

虚拟主机的意义在于区分不同的项目,所以虚拟主机的原理就是你通过某种方式将其映射到服务器上的一个目录而已,你可以通过域名,或者端口,甚至ip来区分这些项目,只要导到项目所在的目录就行了!

基于域名的虚拟主机

通过域名区分不同主机,端口是一样的!

  1. server {
  2. listen 80;
  3. server_name a.com;
  4. access_log /data/wwwlogs/access_nginx.log combined;
  5. root /data/wwwroot/default;
  6. index index.html index.htm index.php;
  7. }
  1. server {
  2. listen 80;
  3. server_name b.com;
  4. access_log /data/wwwlogs/access_nginx.log combined;
  5. root /data/wwwroot/test/;
  6. index index.html index.htm index.php;
  7. }

如果你想多个域名对应同一个站点,那么只需要空格分割多个域名就可以了,例如
server_name a.com admain.com

基于ip的虚拟主机

第一步 : 在服务器网卡上添加几个不同的 IP,例如

  1. ip addr add 172.16.0.18 dev eth0
  2. ip addr add 172.16.0.19 dev eth0
  3. # 添加完后 ping 一下
  4. ping 172.16.0.18
  5. ping 172.16.0.19

或者编辑网卡文件

  1. vim /etc/sysconfig/network-scripts/ifcfg-eth0
  2. # 添加
  3. IPADDR1="172.16.0.18"
  4. IPADDR2="172.16.0.19"

检查是否添加成功

  1. [root@VM_0_17_centos wwwroot]# ip addr
  2. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
  3. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  4. inet 127.0.0.1/8 scope host lo
  5. valid_lft forever preferred_lft forever
  6. 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
  7. link/ether 52:54:00:ae:26:2b brd ff:ff:ff:ff:ff:ff
  8. inet 172.16.0.17/20 brd 172.16.15.255 scope global eth0
  9. valid_lft forever preferred_lft forever
  10. inet 172.16.0.18/32 scope global eth0
  11. valid_lft forever preferred_lft forever
  12. inet 172.16.0.19/32 scope global eth0
  13. valid_lft forever preferred_lft forever

这样一台计算机就有好几个 IP 了,接下来就可以把这些 IP 分给不同的虚拟主机了!

第二步 : 把刚刚创建的 ip 写入server_name 处

  1. server {
  2. listen 80;
  3. server_name 172.16.0.19;
  4. access_log /data/wwwlogs/access_nginx.log combined;
  5. root /data/wwwroot/test/;
  6. index index.html index.htm index.php;
  7. }

配置文件修改完以后你就可以使用 nginx -s reload 平滑重启 nginx 了,如果不确信配置文件是否有误,可以使用 nginx -t 来检查!

如果你是前两种方式配置的虚拟主机,并且你需要在本地测试的话还需要在 hosts 文件中为域名绑定本地 ip

  1. # /etc/hosts
  2. 127.0.0.1 www.a.com

计算机在键入域名『比如www.a.com』,首先会去看看hosts文件有没有关于此域名IP地址的记录。如果有,就直接登陆该网站;如果没有再查询DNS服务器,因此如果没有hosts,你的域名会被dns被解析到公网上的某个地址而非你本地!

常用功能配置

虚拟主机子文件

虚拟主机是在server区块设置的,既然是这样,那么可以将虚拟主机单独放到某个配置文件,然后在主配置文件中include 进来就行了,例如

  1. http {
  2. ...
  3. ######################## default ############################
  4. server {
  5. ....
  6. }
  7. ########################## vhost #############################
  8. include vhost/*.conf;
  9. }

这样以后有新的虚拟主机,只需要创建对应的conf文件,将相关的server信息写入就行了!

nginx 状态信息

可以在虚拟主机里添加一个 location 开启状态信息记录;另外 nginx 的状态信息由 ngx_http_status_module 模块记录,编译时记得添加该模块,可以使用 nginx -V 来查看该模块有没有安装!

  1. server {
  2. ...
  3. location /nginx_status {
  4. stub_status on;
  5. access_log off;
  6. allow 127.0.0.1;
  7. deny all;
  8. }
  9. ....
  10. }

开启状态信息记录后,我们查看状态信息了

nginx 日志配置

错误日志

配置错误日志的参数是 error_log,可以放在main区块中全局配置,也可以放在不同的虚拟主机中单独配置!

例如

  1. user www www;
  2. worker_processes auto;
  3. error_log /data/wwwlogs/error_nginx.log crit;
  4. ...

error_log 的语法为

  1. error_log file level
  2. -------------------------------------------------------------
  3. 参数名 日志文件(自定义) 日志级别

error_log 默认值为: error_log logs/error.logs error,可以放置的区块有 『main,http,server,location』!

常见的错误日志级别有:『debug | info | notice | warn | error | crit | alert | emerg』,级别越高记录的信息越少, 生产场景一般是 warn | error | crit 这三个级别之一!

访问日志

访问日志由两个参数组成,控制日志格式的 log_format 和日志文件存放位置的 access_log 组成,access_log 一般放在 server区块用来记录对应网站的访问信息!

log_format 通过一堆变量来控制日志格式,一般位于 http 区块内

  1. http {
  2. ...
  3. log_format debug '$remote_addr\t$remote_user\t[$time_local]\t"$request"\t"$content_type"\t$status\t$bytes_sent\t'
  4. '"$http_referer"\t"$http_user_agent"\t"$http_cookie"\t"$request_body"';
  5. ...
  6. }

变量说明如下:

  1. - $remote_addr:客户端地址
  2. - $remote_user:客户端用户名
  3. - $time_local:访问时间与时区
  4. - $request:用户的 http 请求起始行信息
  5. - $status http 状态码
  6. - $bytes_sent:发给客户端的字节数
  7. - $http_referer:此次请求从哪个网站过来的
  8. - $http_user_agent:客户端代理信息
  9. - $http_cookie:客户端携带的回话信息
  10. - $request_body:客户端携带的参数

access_log 默认值为 :access_log logs/access.log combined;

一般位于 server 区块,语法格式为

  1. server {
  2. ...
  3. access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]]
  4. # 例如 access_log /data/wwwlogs/mysite_access.log combined;
  5. ...
  6. }

参数说明如下:

  1. - buffer=size 为访问日志的缓冲区大小;
  2. - flush=time 为同步buffer中的数据到磁盘的时间;
  3. - gzip=level 为压缩级别,跟linux的文件压缩级别一样,分 1-9 级;
  4. - if=condition 表示记录日志的条件,。如果指定的条件计算为0或空字符串不会记录;
  5. * access_log off 表示不记录访问日志;

一般情况下这些参数都无需配置,如果不指定日志格式就会用默认的 combined 格式记录日志;

日志切割

磁盘空间有限,为了节省空间和方便整理,可以将日志文件按时间或大小切割成多份,删除时间久远的日志文件,这就是所谓的日志轮替或日志切割!

第一种:自己写切割脚本跑定时任务,命名为 rotate_access_log.sh

  1. #! /bin/bash
  2. log_dir="/usr/local/nginx/log/"
  3. date_dir=`date +%Y%m%d`
  4. /bin/mkdir -p ${log_dir}/${date_dir} > /dev/null 2>&1
  5. /bin/mv ${log_dir}/access.log ${log_dir}/${date_dir}/access.log
  6. sharedscripts
  7. postrotate
  8. kill -USR1 `cat /usr/local/nginx/run/nginx.pid`
  9. endscript
  10. # 上面这段 sharedscripts 还可以换成
  11. # /sbin/nginx -s reload

可能你对 sharedscripts 那部分的脚本有疑问,是这样的,由于进程操作文件使用的是 inode,这就导致你即使通过 mv 将文件名给变了,可是文件的 inode 还是不变,所以为了避免 nginx 主进程往归档文件写入数据,nginx主进程接到信号后会从配置文件中读取日志文件名称,这样就生成了新的 inode 文件!

kill -USR1 等于 nginx -s reopen,kill -USR2 等于 nginx -s reload !

接着

  1. chmod +x rotate_access_log.sh

然后将该脚本跑定时任务,每日0点归档

  1. 0 0 * * * bash /usr/local/nginx/rotate_access_log.sh >/dev/null 2>&1

第二种:linux 自带的 logrotate(日志轮替)

  1. /var/log/nginx/*.log {
  2. daily # 多长时间切割一次,weekly,monthly,yearly
  3. rotate 30 # 保存几份切割的数据,超过的则删除
  4. size +100M # 超过100M时分割,单位K,M,G,优先级高于daily
  5. compress # 切割后压缩,也可以为nocompress
  6. delaycompress # 切割时对上次的日志文件进行压缩
  7. dateext # 日志文件切割时添加日期后缀
  8. missingok # 如果没有日志文件也不报错
  9. notifempty # 日志为空时不进行切换,默认为ifempty
  10. create 640 nginx nginx # 使用该模式创建日志文件
  11. sharedscripts # 所有的文件切割之后只执行一次下面脚本
  12. postrotate
  13. if [ -f /var/run/nginx.pid ]; then
  14. kill -USR1 `cat /var/run/nginx.pid`
  15. fi
  16. endscript
  17. }

location

location 的功能是通过正则实现 url 的匹配!

location 语法为

  1. location [ =|~|~*|^~|@]

location 相关规则和指令:

  1. * = 精确匹配
  2. * ~ 表示区分大小写匹配
  3. * ~* 表示不区分大小写匹配
  4. * ^~ 表示匹配 url 段,nginx不对url做编码,因此请求为/static/20%/aa,可以被规则^~ /static/ /aa匹配到!

示例:

  1. location ^~ /static/ {
  2. # 请求/static/a.txt 将被映射到实际目录文件:/webroot/res/static/a.txt
  3. root /webroot/res/;
  4. }
  5. location ~* \.(gif|jpg|jpeg|png|css|js|ico)${
  6. root /webroot/res/;
  7. }
  8. # 当代理是绝对路径, 访问 http://site.com/assets/css/test.css ,
  9. # 实际访问的是 http://10.0.0.8/css/test.css !
  10. location /assets/ {
  11. proxy_pass http://10.0.0.8/;
  12. }
  13. # 当代理是相对路径时(没/), 访问 http://site.com/assets/css/test.css,
  14. # 实际访问的是 http://10.0.0.8/assets/css/test.css !
  15. location /assets/ {
  16. proxy_pass http://10.0.0.8;
  17. }
  18. # 返回htto code 并附带错误信息
  19. location = /image404.html {
  20. return 404 "image not found\n";
  21. }
  22. # 禁止访问多个目录
  23. location ~ ^/(cron|templates)/
  24. {
  25. deny all;
  26. break;
  27. }

rewrite

rewrite 的功能是结合兼容 pcre,perl 的正则表达式实现 url 重定向和重写;rewrite只能放在server{},location{},if{} 区块中 !

rewrite 的语法为:

  1. rewrite 规则 定向路径 重写类型;

示例

  1. # if
  2. #判断访问的是否ie;
  3. if ($http_user_agent ~ MSIE) {
  4. rewrite ^.*$ /ie.htm;
  5. break; #不break会循环重定向(是ie重写到ie.htm,然后又发现是ie,又重写到ie.htm...)
  6. }
  7. #跳转到404
  8. if (!-e $document_root$fastcgi_script_name) {
  9. rewrite ^.*$ /404.html;
  10. break;
  11. }
  12. # 跳转到代理
  13. if (!-e $request_filename) {
  14. proxy_pass http://127.0.0.1;
  15. }
  16. # return
  17. # 返回 http code
  18. if ($remote_addr = 192.168.197.142) {
  19. return 403;
  20. }
  21. # rewrite
  22. # 资源不存在跳转到首页
  23. if (!-e $document_root$fastcgi_script_name) {
  24. rewrite ^.*$ /index.html;
  25. break;
  26. }
  27. # 如果请求为/download/eva/media/op1.mp3则请求被rewrite到 /download/eva/mp3/op1.mp3
  28. rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last;
  29. # 301 域名跳转
  30. server
  31. {
  32. listen 80;
  33. server_name admin.test.com api.test.com;
  34. index index.html index.htm index.php;
  35. root /data/wwwroot/test.com;
  36. rewrite ^/ http://www.aaa.com permanent;
  37. }
  38. # set
  39. if ($http_user_agent ~* msie) {
  40. set $isie 1;
  41. }
  42. if ($fastcgi_script_name = ie.html) {
  43. set $isie 0;
  44. }
  45. if ($isie 1) {
  46. rewrite ^.*$ ie.html;
  47. }
  48. # 获取协议
  49. if ($http_host ~* "^(.*)\.aaa.com$")
  50. {
  51. set $protocol $1;
  52. rewrite ^(.*) $protocol://bbb.com permanent;
  53. }
  54. # 其他
  55. if (-f $request_filename) {
  56. expires max;
  57. break;
  58. }
  59. # 单入口
  60. if (!-e $request_filename) {
  61. rewrite ^/(.*)$ /index.php/$1 last;
  62. }

rewrite 只能对域名后边不含请求参数的部分起作用,请求参数会自动追加到重定向后的 url;
rewrite 重写的后的路径如果不是远程路径,那么都是相对于 root 目录的;

rewrite 全局变量说明:

  1. $args, 请求中的参数;
  2. $content_length, HTTP请求信息里的"Content-Length";
  3. $content_type, 请求信息里的"Content-Type";
  4. $document_root, 针对当前请求的根路径设置值;
  5. $document_uri, $uri相同;
  6. $host, 请求信息中的"Host",如果请求中没有Host行,则等于设置的服务器名;
  7. $limit_rate, 对连接速率的限制;
  8. $request_method, 请求的方法,比如"GET""POST"等;
  9. $remote_addr, 客户端地址;
  10. $remote_port, 客户端端口号;
  11. $remote_user, 客户端用户名,认证用;
  12. $request_filename, 当前请求的文件路径名;
  13. $request_body_file;
  14. $request_uri, 请求的URI,带查询字符串;
  15. $query_string, $args相同;
  16. $scheme, 所用的协议,比如http或者是https,比如rewrite ^(.+)$ $scheme://example.com$1 redirect;
  17. $server_protocol, 请求的协议版本,"HTTP/1.0""HTTP/1.1";
  18. $server_addr, 服务器地址,如果没有用listen指明服务器地址,使用这个变量将发起一次系统调用以取得地址(造成资源浪费);
  19. $server_name, 请求到达的服务器名;
  20. $server_port, 请求到达的服务器端口号;
  21. $http_x_forwarded_for, 记录客户端的ip地址
  22. $http_referer, 记录用户是从哪个链接访问过来的
  23. 例如访问链接是 http://localhost:88/test1/test2/test.php :
  24. 网站路径是:/var/www/html
  25. $hostlocalhost
  26. $server_port88
  27. $request_urihttp://localhost:88/test1/test2/test.php
  28. $document_uri:/test1/test2/test.php
  29. $document_root:/var/www/html
  30. $request_filename:/var/www/html/test1/test2/test.php

rewrite 相关指令和规则:

  1. ~ 区分大小写匹配;
  2. ~* 不区分大小写匹配;
  3. !~和!~*分别为区分大小写不匹配及不区分大小写不匹配;
  4. -f和!-f用来判断是否存在文件;
  5. -d和!-d用来判断是否存在目录;
  6. -e和!-e用来判断是否存在文件或目录;
  7. -x和!-x用来判断文件是否可执行;
  8. set 设置变量;
  9. return 返回 http 状态码 ;
  10. if 用作条件判断,注意,if 后面一定要有空格;

防盗链

为什么防盗链?

因为某些不法网站在其自身嵌入别的网站的资源(盗用别的网站连接),会造成资源所在网站的流量升高,带宽和服务压力上升,甚至宕机!

valid_referers

nginx 配置中有一个指令 valid_referers 用来获取 Referer 头中的值,并且根据该值的情况给 nginx 全局变量 $invalid_referer 赋值,如果 Referer 头中没有符合 valid_referers 中的值,$invalid_referer 就会被赋值为 1!

  1. valid_referers none | blocked | server_names | string ...
  2. 1. none 用来检测 Refer 头是否不存在
  3. 2. blocked 用来检测 Refer 头的值是否被防火墙删除或伪装过,这种情况下该值不以 "http://" "https://" 开头
  4. 3. server_names 用来检测 Refer 头的值是否属于 server_names 中的一个

有了 valid_referers 和 $invlid_referer 就能通过 rewrite 功能实现防盗链

  1. location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
  2. valid_referers none blocked localhost 10.0.0.8 test.com;
  3. if ($invalid_referer)
  4. {
  5. # return 404;
  6. rewrite ^.*$ /pics/404.jpg;
  7. break;
  8. }
  9. rewrite ^/(.*)$ /pics/$1;
  10. expires 30d;
  11. access_log off;
  12. }

访问认证

访问认证指的是当你请求一些资源的时候服务器要求你输入账号和密码,一般主要用在一些内网环境,可以放置的区块有 http,server,location,limit_except!

nginx 的认证主要依靠两个字段 auth_baisc 和 auth_basic_user_file

第1步:配置auth

  1. location ~ /auth {
  2. auth_basic "test auth";
  3. auth_basic_user_file pwd;
  4. }

第2步:使用 openssl 生成加密密码

  1. [root@VM_0_17_centos conf]# openssl passwd
  2. Password:
  3. Verifying - Password:
  4. eFIwNPT.5mxMU

第3步:创建认证文件 pwd,文件中写入用户名和刚刚生成的加密密码,这里我们输入123

  1. # 基本格式这样的,新用户的话就新增一行
  2. # name1:password1
  3. # name2:password2:comment
  4. tcl:eFIwNPT.5mxMU

测试

  1. [root@VM_0_17_centos conf]# curl --user tcl:456 localhost/auth
  2. <html>
  3. <head><title>401 Authorization Required</title></head>
  4. <body bgcolor="white">
  5. <center><h1>401 Authorization Required</h1></center>
  6. <hr><center>nginx</center>
  7. </body>
  8. </html>
  9. [root@VM_0_17_centos conf]#
  10. [root@VM_0_17_centos conf]# curl --user tcl:123 localhost/auth
  11. <html>
  12. <head><title>404 Not Found</title></head>
  13. <body bgcolor="white">
  14. <center><h1>404 Not Found</h1></center>
  15. <hr><center>nginx</center>
  16. </body>
  17. </html>

第一次,用错误密码请求时,提示我们 401 需要 auth;
第二次,输入正确密码后认证通过了,出现 404 是因为我们之后没有重定向该请求,认证通过后 nginx 不知道接下来去哪里,所以返回了404!

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注