Nginx 配置入门

2016/03/24 nginxserverrewrite

# 基本配置结构

# main 全局块

# events 块
events {
}

# http 块
http {
    # http 全局块

    # server 块
    server {
        # server全局块

        # location 块
        location [PATTERN] {
        }

        location [PATTERN] {
        }
    }

    server {
        # server全局块

        # location 块
        location [PATTERN] {
        }

        location [PATTERN] {
        }
    }

    # http 全局块
}
  1. main 全局块:配置影响 nginx 全局的指令。一般有运行 nginx 服务器的用户组,nginx 进程 pid 存放路径,日志存放路径,配置文件引入,允许生成 worker process 数等。
  2. events 块:配置影响 nginx 服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。
  3. http 块:可以嵌套多个 server,配置代理、缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type 定义,日志自定义,是否使用 sendfile 传输文件,连接超时时间,单连接请求数等。
  4. server 块:配置虚拟主机的相关参数,一个 http 中可以有多个 server。
  5. location 块:配置请求的路由,以及各种页面的处理情况。

不同模块指令关系:server 继承 main;location 继承 server;upstream 既不会继承指令也不会被继承,它有自己的特殊指令,不需要在其他地方的应用。

# Rewrite

Nginx Rewrite 是利用 nginx 提供的全局变量或自己设置的变量,结合正则表达式和标志位实现 url 重写以及重定向。rewrite 只能放在 server{}, location{}, if{} 中,并且只能对域名后边的除去传递的参数外的字符串起作用,例如 http://seanlook.com/a/we/index.php?id=1&u=str 只对 /a/we/index.php 重写。语法 rewrite regex replacement [flag];

如果相对域名或参数字符串起作用,可以使用全局变量匹配,也可以使用 proxy_pass 反向代理。

表面看 rewrite 和 location 功能有点像,都能实现跳转,主要区别在于 rewrite 是在同一域名内更改获取资源的路径,而 location 是对一类路径做控制访问或反向代理,可以 proxy_pass 到其他机器。很多情况下 rewrite 也会写在 location 里,它们的执行顺序是:

  1. 执行 server 块的 rewrite 指令
  2. 执行 location 匹配
  3. 执行选定的 location 中的 rewrite 指令

如果其中某步 URI 被重写,则重新循环执行 1-3,直到找到真实存在的文件;循环超过 10 次,则返回 500 Internal Server Error 错误。

# Flag 标志位

  • last: 相当于 Apache的[L] 标记,表示完成 rewrite

  • break: 停止执行当前虚拟主机的后续 rewrite 指令集

  • redirect: 返回 302 临时重定向,地址栏会显示跳转后的地址

  • permanent: 返回 301 永久重定向,地址栏会显示跳转后的地址

因为 301 和 302 不能简单的只返回状态码,还必须有重定向的 URL,这就是 return 指令无法返回 301, 302 的原因了。这里 last 和 break 区别有点难以理解:

  1. last 一般写在 server 和 if 中,而 break 一般使用在 location 中
  2. last 不终止重写后的 url 匹配,即新的 url 会再从 server 走一遍匹配流程,而 break 终止重写后的匹配
  3. break 和 last 都能组织继续执行后面的 rewrite 指令

# if 指令与全局变量

if判断指令

语法为 if(condition) {...},对给定的条件 condition 进行判断。如果为真,大括号内的 rewrite 指令将被执行,if 条件(conditon)可以是如下任何内容:

  • 当表达式只是一个变量时,如果值为空或任何以 0 开头的字符串都会当做 false

  • 直接比较变量和内容时,使用 =!=

  • ~ 正则表达式匹配,~* 不区分大小写的匹配,!~ 区分大小写的不匹配

  • -f!-f 用来判断是否存在文件

  • -d!-d 用来判断是否存在目录

  • -e!-e 用来判断是否存在文件或目录

  • -x!-x 用来判断文件是否可执行

栗子:

# 如果UA包含"MSIE",rewrite 请求到 /msid/ 目录下
if ($http_user_agent ~ MSIE) {
    rewrite ^(.*)$ /msie/$1 break;
}

# 如果 cookie 匹配正则,设置变量 $id 等于正则引用部分
if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
    set $id $1;
}

# 如果提交方法为 POST,则返回状态 405(Method not allowed)。return 不能返回 301, 302
if ($request_method = POST) {
    return 405;
}

# 限速,$slow 可以通过 set 指令设置
if ($slow) {
    limit_rate 10k;
}

# 如果请求的文件名不存在,则反向代理到 localhost。这里的 break 也是停止 rewrite 检查
if (!-f $request_filename){
    break;
    proxy_pass  http://127.0.0.1;
}

# 如果 query string 中包含"post=140",永久重定向到 example.com
if ($args ~ post=140){
    rewrite ^ http://example.com/ permanent;
}

# 防盗链
location ~* \.(gif|jpg|png|swf|flv)$ {
    valid_referers none blocked www.jefflei.com www.leizhenfang.com;
    if ($invalid_referer) {
        return 404;
    }
}

全局变量

下面是可以用作if判断的全局变量:

  • $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”。

  • $uri: 不带请求参数的当前URI,$uri不包含主机名,如”/foo/bar.html”。

  • $document_uri: 与$uri相同。

栗子:

# http://localhost:88/test1/test2/test.php

$host = localhost
$server_port = 88
$request_uri = http://localhost:88/test1/test2/test.php
$document_uri = /test1/test2/test.php
$document_root = /var/www/html
$request_filename = /var/www/html/test1/test2/test.php

# 常用正则

  • .: 匹配除换行符以外的任意字符

  • ?: 重复0次或1次

  • +: 重复1次或更多次

  • *: 重复0次或更多次

  • \d: 匹配数字

  • ^: 匹配字符串的开始

  • $: 匹配字符串的介绍

  • {n}: 重复n次

  • {n,}: 重复n次或更多次

  • [c]: 匹配单个字符c

  • [a-z]: 匹配a-z小写字母的任意一个

小括号 () 之间匹配的内容,可以在后面通过 $1 来引用,$2 表示的是前面第二个 () 里的内容。正则里面容易让人困惑的是 \ 转义特殊字符。

# Rewrite 实例

  • WordPress 伪静态

    if (-f $request_filename/index.html) {
        rewrite (.*) $1/index.html break;
    }
    if (-f $request_filename/index.php) {
        rewrite (.*) $1/index.php;
    }
    if (!-f $request_filename) {
        rewrite (.*) /index.php;
    }
    
  • PHPCMS 伪静态

    rewrite ^/caipu-([0-9]+)-([0-9]+)-([0-9]+).html /index.php?m=content&c=index&a=show&catid=$1&id=$2&page=$3 last;
    rewrite ^/content-([0-9]+)-([0-9]+)-([0-9]+).html /index.php?m=content&c=index&a=show&catid=$1&id=$2&page=$3 last;
    rewrite ^/list-([0-9]+)-([0-9]+).html /index.php?m=content&c=index&a=lists&catid=$1&page=$2 last;
    rewrite ^/tag-([^\.]*)-([0-9]+)-([0-9]+).html /index.php?m=content&c=tag&catid=$2&tag=$1&page=$3 last;
    rewrite ^/comment-([0-9]+)-([0-9]+)-([0-9]+).html /index.php?m=comment&c=index&a=init&commentid=content_$1-$2-$3 last;
    rewrite ^/([^\.]*).html /index.php?m=member&c=index&a=$1 last;
    
  • DEDECMS 伪静态

    rewrite "^/index.html$" /index.php last;
    rewrite "^/list-([0-9]+)\.html$" /plus/list.php?tid=$1 last;
    rewrite "^/list-([0-9]+)-([0-9]+)-([0-9]+)\.html$" /plus/list.php?tid=$1&totalresult=$2&PageNo=$3 last;
    rewrite "^/view-([0-9]+)-1\.html$" /plus/view.php?arcID=$1 last;
    rewrite "^/view-([0-9]+)-([0-9]+)\.html$" /plus/view.php?aid=$1&pageno=$2 last;
    rewrite "^/tags.html$" /tags.php last;
    rewrite "^/tag-([0-9]+)-([0-9]+)\.html$" /tags.php?/$1/$2/ last;
    
  • Discuz7 伪静态

    rewrite ^/archiver/((fid|tid)-[\w\-]+\.html)$ /archiver/index.php?$1 last;
    rewrite ^/forum-([0-9]+)-([0-9]+)\.html$ /forumdisplay.php?fid=$1&page=$2 last;
    rewrite ^/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /viewthread.php?tid=$1&extra=page\%3D$3&page=$2 last;
    rewrite ^/space-(username|uid)-(.+)\.html$ /space.php?$1=$2 last;
    rewrite ^/tag-(.+)\.html$ /tag.php?name=$1 last;
    
  • DiscuzX 伪静态

    rewrite ^([^\.]*)/topic-(.+)\.html$ $1/portal.php?mod=topic&topic=$2 last;
    rewrite ^([^\.]*)/article-([0-9]+)-([0-9]+)\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last;
    rewrite ^([^\.]*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last;
    rewrite ^([^\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last;
    rewrite ^([^\.]*)/group-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last;
    rewrite ^([^\.]*)/space-(username|uid)-(.+)\.html$ $1/home.php?mod=space&$2=$3 last;
    rewrite ^([^\.]*)/([a-z]+)-(.+)\.html$ $1/$2.php?rewrite=$3 last;
    if (!-e $request_filename) {
        return 404;
    }
    
  • PHPWind 伪静态

    rewrite ^(.*)-htm-(.*)$ $1.php?$2 last;
    rewrite ^(.*)/simple/([a-z0-9\_]+\.html)$ $1/simple/index.php?$2 last;
    
  • SHOPEX 伪静态

    if (!-e $request_filename) {
        rewrite ^/(.+\.(html|xml|json|htm|php|jsp|asp|shtml))$ /index.php?$1 last;
    }
    
  • Typecho 伪静态

    if (-f $request_filename/index.html) {
        rewrite (.*) $1/index.html break;
    }
    if (-f $request_filename/index.php) {
        rewrite (.*) $1/index.php;
    }
    if (!-f $request_filename) {
        rewrite (.*) /index.php;
    }
    
  • Emlog 伪静态

    if (!-f $request_filename) {
        set $rule_0 1$rule_0;
    }
    
    if (!-d $request_filename) {
        set $rule_0 2$rule_0;
    }
    
    if ($rule_0 = "21") {
        rewrite ^/(post|record|sort|author|page)-([0-9]+)\.html(.*)$ /index.php?$1=$2$3;
        rewrite ^/(post|record|sort|author|page)/([0-9]+)(.*)$ /index.php?$1=$2$3;
        rewrite ^/tag-(.+)\.html$ /index.php?tag=$1;
        rewrite ^/tag/(.+)$ /index.php?tag=$1;
        rewrite ^/t/page/([0-9]+)$ /t/index.php?page=$1;
    }
    

# 基本配置模板

########### 每个指令必须以分号结束 #################

# 配置用户或者组,默认为nobody nobody。
# user administrator administrators;

# 允许生成的进程数,默认为1
# worker_processes 2;

# 指定 nginx 进程运行文件存放地址
# pid /nginx/pid/nginx.pid;

# 制定错误日志路径,级别。这个设置可以放入全局块,http块,server块,级别依次为:debug|info|notice|warn|error|crit|alert|emerg
error_log log/error.log debug;

# 工作模式及连接数上限
events {
    # 设置网路连接序列化,防止惊群现象发生,默认为on
    accept_mutex on;

    # 设置一个进程是否同时接受多个网络连接,默认为off
    multi_accept on;

    # 事件驱动模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport
    # use epoll;

    # 单个work进程允许的最大连接数,默认为512
    worker_connections 1024;
}

# http服务器
http {
    # 文件扩展名与文件类型映射表。设定mime类型(邮件支持类型),类型由mime.types文件定义
    # include /usr/local/etc/nginx/conf/mime.types;
    include mime.types;

    # 默认文件类型,默认为text/plain
    default_type application/octet-stream;

    # 取消服务访问日志
    # access_log off;

    # 自定义日志格式
    log_format myFormat '$remote_addr–$remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for';

    # 设置访问日志路径和格式。"log/"该路径为nginx日志的相对路径,mac下是/usr/local/var/log/。combined为日志格式的默认值
    access_log log/access.log myFormat;
    rewrite_log on;

    # 允许sendfile方式传输文件,默认为off,可以在http块,server块,location块。(sendfile系统调用不需要将数据拷贝或者映射到应用程序地址空间中去)
    sendfile on;

    # 每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限。
    sendfile_max_chunk 100k;

    # 连接超时时间,默认为75s,可以在http,server,location块。
    keepalive_timeout 65;

    # gzip压缩开关
    # gzip on;

    tcp_nodelay on;

    # 设定实际的服务器列表
    upstream mysvr1 {
        server 127.0.0.1:7878;
        server 192.168.10.121:3333 backup; #热备(其它所有的非backup机器down或者忙的时候,请求backup机器))
    }

    upstream mysvr2 {
        # weigth参数表示权值,权值越高被分配到的几率越大
        server 192.168.1.11:80 weight=5;
        server 192.168.1.12:80 weight=1;
        server 192.168.1.13:80 weight=6;
    }
    upstream https-svr {
        # 每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题
        ip_hash;
        server 192.168.1.11:90;
        server 192.168.1.12:90;
    }

    # error_page 404 https://www.baidu.com; #错误页

    # HTTP服务器
    # 静态资源一般放在nginx所在主机
   server {
       listen 80; #监听HTTP端口
       server_name 127.0.0.1; #监听地址
       keepalive_requests 120; #单连接请求上限次数
       set $doc_root_dir "/Users/doing/IdeaProjects/edu-front-2.0"; #设置server里全局变量
       #index index.html;  #定义首页索引文件的名称
       location ~*^.+$ { #请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。
          root $doc_root_dir; #静态资源根目录
          proxy_pass http://mysvr1; #请求转向“mysvr1”定义的服务器列表
          #deny 127.0.0.1; #拒绝的ip
          #allow 172.18.5.54; #允许的ip
       }
   }

    # http
    server {
        listen 80;
        server_name www.helloworld.com; # 监听基于域名的虚拟主机。可有多个,可以使用正则表达式和通配符
        charset utf-8; # 编码格式
        set $static_root_dir "/Users/doing/static";

        location /app1 { #反向代理的路径(和upstream绑定),location后面设置映射的路径
            proxy_pass http://zp_server1;
        }

        location /app2 {
            proxy_pass http://zp_server2;
        }

        location ~ ^/(images|javascript|js|css|flash|media|static)/ {  #静态文件,nginx自己处理
            root $static_root_dir;
            expires 30d; # 静态资源过时间30天
        }

        # 禁止访问 .htxxx 文件
        location ~ /\.ht {
            deny all;
        }

        # 直接简单粗暴的返回状态码及内容文本
        location = /do_not_delete.html {
            return 200 "hello.";
        }

        # 指定某些路径使用https访问(使用正则表达式匹配路径+重写uri路径)
        location ~* /http* { #路径匹配规则:如localhost/http、localhost/httpsss等等
            # rewrite只能对域名后边的除去传递的参数外的字符串起作用,
            # 例如www.c.com/proxy/html/api/msg?method=1&para=2只能对/proxy/html/api/msg重写。
            # rewrite 规则 定向路径 重写类型;
            # rewrite后面的参数是一个简单的正则。$1代表正则中的第一个()。
            # $host是nginx内置全局变量,代表请求的主机名
            # 重写规则permanent表示返回301永久重定向
            rewrite ^/(.*)$ https://$host/$1 permanent;
        }

        # 错误处理页面(可选择性配置)
        # error_page 404 /404.html;
        # error_page 500 502 503 504 /50x.html;

        # 以下是一些反向代理的配置(可选择性配置)
        # proxy_redirect off;
        # proxy_set_header Host $host; # proxy_set_header用于设置发送到后端服务器的request的请求头
        # proxy_set_header X-Real-IP $remote_addr;
        # proxy_set_header X-Forwarded-For $remote_addr; #后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
        # proxy_connect_timeout 90; #nginx跟后端服务器连接超时时间(代理连接超时)
        # proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时)
        # proxy_read_timeout 90; #连接成功后,后端服务器响应时间(代理接收超时)
        # proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小
        # proxy_buffers 4 32k; #proxy_buffers缓冲区,网页平均在32k以下的话,这样设置
        # proxy_busy_buffers_size 64k; #高负荷下缓冲大小(proxy_buffers*2)
        # proxy_temp_file_write_size 64k; #设定缓存文件夹大小,大于这个值,将从upstream服务器传

        # client_max_body_size 10m; #允许客户端请求的最大单文件字节数
        # client_body_buffer_size 128k; #缓冲区代理缓冲用户端请求的最大字节数
    }

    # https
    # - HTTPS的固定端口号是443,不同于HTTP的80端口;
    # - SSL标准需要引入安全证书,所以在 nginx.conf 中你需要指定证书和它对应的 key
    server {
        listen 443;
        server_name  www.hellohttps1.com www.hellohttps2.com;
        set $geek_web_root "/Users/doing/IdeaProjects/backend-geek-web";

        # ssl 证书文件位置(常见证书文件格式为:crt/pem)
        ssl_certificate      /usr/local/etc/nginx/ssl-key/ssl.crt;

        # ssl 证书 key 位置
        ssl_certificate_key  /usr/local/etc/nginx/ssl-key/ssl.key;

        location /passport {
            send_timeout 90;
            proxy_connect_timeout 50;
            proxy_send_timeout 90;
            proxy_read_timeout 90;
            proxy_pass http://https-svr;
        }

        location ~ ^/(res|lib)/ {
            root $geek_web_root;
            expires 7d;
            # add_header用于为后端服务器返回的response添加请求头,这里通过add_header实现CROS跨域请求服务器
            add_header Access-Control-Allow-Origin *;
        }
        # ssl配置参数(选择性配置)
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout 5m;
    }

    # 配置访问控制:每个IP一秒钟只处理一个请求,超出的请求会被delayed
    # 语法:limit_req_zone  $session_variable  zone=name:size  rate=rate
    # (为session会话状态分配一个大小为size的内存存储区,限制了每秒(分、小时)只接受rate个IP的频率)
    limit_req_zone  $binary_remote_addr zone=req_one:10m   rate=1r/s nodelay;
    location /pay {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real_IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # 访问控制:limit_req zone=name [burst=number] [nodelay];
        # burst=5表示超出的请求(被delayed)如果超过5个,那些请求会被终止(默认返回503)
        limit_req zone=req_one burst=5;
        proxy_pass http://mysvr1;
    }

    # 可以把子配置文件放到/usr/local/etc/nginx/servers/路径下,通过include引入
    include /usr/local/etc/nginx/servers/*.conf;
}
上次更新: 2024/4/15 02:28:03