nginx入门教程 ,转

1.Nginx 状态码配置和错误文件

server {
    # 配置访问 /test.js 时报 403 错
    location /test.js {
        return 403;
    }

    # 配置访问 /404 时报 404 错
    location /404 {
        return 404;
    }

    # 配置访问 /500 时报 500 错
    location /500 {
        return 500;
    }

    # 把指定状态码指向这个文件 uri
    error_page 500 502 503 504 /status.html;
    error_page 404 /status.html;
}

如:

server {
    listen 80;
    server_name test.me;

    root /Users/xiaowu/work/test.me;

    # 用if匹配任何以 403 开头的,会匹配到 /4034444
    if ($request_uri ~* ^/403) {
        return 403;
    }

    # 用location匹配 /500/ /500,但不匹配 /500/1
    location ~* "^/500/?$" {
        return 500;
    }

    # 用if匹配以 /501/ 开头的,匹配 /501/1,/501/1/2 但不匹配 /501
    if ($request_uri ~* ^/501/) {
        return 501;
    }

    # 用location匹配 /502/ /502 /502/1 /502/1/2
    location ~* "^/502(/.*)?$" {
        return 502;
    }

    # 用location只匹配 /503
    location = /503 {
        return 503;
    }
}

error_page配置小提示

注意 error_page 配置时加 = 和不加 = 的区别,加了 = 表示响应为指定的 http status code ,默认为 200,不加 = 为原错误的状态码~

# 这样可以访问错误页面时 http status 为 404 ,并且页面内容是 404.html 的内容
error_page 404 /404.html
error_page 404 500 /404.html;

# 这样配置访问错误页面时 http status 为 200 ,但页面内容是 404.html 的内容
error_page 404 500 = /404.html;

# 这样配置访问错误页面时 http status 为 404 ,但页面内容是 404.html 的内容
error_page 404 500 =404 /404.html;

# 也可以把404请求直接301到某个域上
error_page 404 =301 https://xuexb.com/404;

这样就可以根据自己需求配置错误页为指定的状态码,因为非 200 的状态码可能会被浏览器拦截。

2.主域301重定向

你的网站可能有多个域名访问,比如:www.xuexb.comxuexb.com 等,设置主域意思是不管用户输入哪个域名,都会 301 重定向到主域上,设置主域可以对 SEO 更友好,比如:

以xuexb.com为主域

www.xuexb.com => xuexb.com
www.xuexb.com/search/xxoo => xuexb.com/search/xxoo
www.xuexb.com/a/b/c/404.html => xuexb.com/a/b/c/404.html

配置文件核心:

server {
    # 设置多个域名
    server_name www.xuexb.com xuexb.com;

    # 判断host是不是xuexb.com,如果不是则直接301重定向,permanent表示301
    if ( $host != 'xuexb.com' ){
        rewrite ^/(.*)$ http://xuexb.com/$1 permanent;
    }

    # 其他规则
}

3.Node.js 反向代理

服务端如果使用nodejs运行服务,由于端口不能同时多个服务占用,而服务器中可能又是多个网站,那么可以使用 Nginx 做反向代理,比如有这些网站域名和端口:

域名端口
www.xxoo.com8001
www.xo.com8002
www.xo.cn8003

当然一个服务器上的网站可能还有更多,可以通过配置 Nginx 转发来代理这些端口分发,如:

server {
    server_name www.xxoo.com;
    listen 80;

    # 设置这个网站的根目录
    root /wwwroot/www.xxoo.com/;

    # 由于下面配置了文件不存在则代码到 Node.js 中,那么直接访问目录(不带默认主页)的话会有问题,这里做下判断
    # 如果访问目录下有 index.html 文件,则直接重写到该文件
    # break 表示重写且停止,但 url 不变,而 permanent 表示301重定向,url 会更新
    if ( -f $request_filename/index.html ){
        rewrite (.*) $1/index.html break;
    }

    # 如果请求的文件不存在,则代理到 Node.js
    if ( !-f $request_filename ){
        rewrite (.*) /index.js;
    }

    # 代理node服务 8001
    location = /index.js {
        # 设置一些代理的header信息,这些信息将被透传到 Node.js 服务的header信息里
        proxy_set_header Connection "";
        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;
        proxy_set_header X-NginX-Proxy true;

        # 代理服务
        proxy_pass http://127.0.0.1:8001$request_uri;

        # 忽略其他重写
        proxy_redirect off;
    }
}

配置之后,比如你网站根目录里有 index.html 文件,访问 url 如:

访问链接解析过程备注
www.xxoo.com/index.htmlNginx由于文件存在,直接使用 Nginx 输出
www.xxoo.comNginx由于判断该目录下有 index.html 文件,则自动重写到该文件,但 url 不变
www.xxoo.com/xx.htmlNginx -> Node.js:8001由于文件不存在,使用 Nginx 代理到 Node.js 的 8001 端口
www.xxoo.com/xxoo/Nginx -> Node.js:8001首先判断该目录是否存在

如果存在再判断是否有 index.html 文件

一旦不成立,直接代理到 Node.js

4.配置图片防盗链

防盗链是指当图片不是自己网站打开时返回 403 或者指定图片,是通过请求的来路判断是否是自己的站点来设置响应。

语法

valid_referers none | blocked | server_names | string

  • none:表示没有来路
  • blocked:表示有来路
  • server_names:来路里包含当前域名
  • string:(忽略端口)
    • 如果是字符串:一个域名验证的规则,* 表示通配符
    • 如果是以 ~ 开头:正则表达式,排除https://或http://开头的字符串

以上参数可以叠加一起使用。

例子

server {

    # 配置所有图片
    location ~* \.(gif|jpg|png|bmp)$ {
        # 验证可以是没有来路、或者有来路时来路匹配xuexb.com、或者匹配当前域名
        valid_referers none blocked *.xuexb.com server_names;

        # 如果验证不通过则返回403
        if ($invalid_referer) {
            return 403;
        }
    }
}

5.配置 HTTPS

首先配置支持 HTTPS 必须让 Nginx 开启 http_ssl_module 模块,点击查看nginx编译安装参数 ,可以使用nginx -V查看是否开启TLS SNI support enabled

购买/生成 SSL 证书,可以使用免费的证书,比如:Let's Encrypt,免费好用的 HTTPS 证书

# 配置 HTTPS

# 配置个http的站点,用来做重定向,当然如果你不需要把 HTTP->HTTPS 可以把这个配置删了
server {
    listen       80;

    # 配置域名
    server_name www.xxoo.com xxoo.com;

    # 添加 STS, 并让所有子域支持, 开启需慎重
    add_header strict-transport-security 'max-age=31536000; includeSubDomains; preload';

    # 配置让这些 HTTP 的访问全部 301 重定向到 HTTPS 的
    rewrite ^(.*) https://www.xxoo.com$1 permanent;
}

# 配置 HTTPS
server {
    # 配置域名
    server_name www.xxoo.com xxoo.com;

    # https默认端口
    listen 443;

    # 添加STS, 并让所有子域支持, 开启需慎重
    add_header strict-transport-security 'max-age=31536000; includeSubDomains; preload';

    # https配置
    ssl on;
    ssl_certificate /xxoo/www.xxoo.com.crt;
    ssl_certificate_key /xxoo/www.xxoo.com.key;

    # 其他按正常配置处理即可...
}

注意,这里证书的格式是 .crt

配置后的访问规则

输入链接最终访问链接
http://www.xxoo.comhttps://www.xxoo.com
http://www.xxoo.com/404/500https://www.xxoo.com/404/500
http://xxoo.comhttps://www.xxoo.com
https://www.xxoo.com-(原链接不变)
https://xxoo.com/500https://www.xxoo.com/500

https://github.com/Neilpang/acme.sh

6.配置泛域名转发

有的时候,我们需要配置一些自定义的子域名,如:

  • xuexb.user.demo.com
  • a01.user.demo.com

这时候就需要域名的 DNS 解析一个泛域名 *.user.demo.com 到服务器,Nginx 可以配置如下:

子域名转发到子目录

server {
    listen       80;
    server_name ~^([\w-]+)\.user\.demo\.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;
        proxy_set_header        X-NginX-Proxy true;
        proxy_pass              http://127.0.0.1:8080/$1$request_uri;
    }
}

以上配置表示:

  • xuexb.user.demo.com/path?a=1 -> 127.0.0.1:8080/xuexb/path?a=1
  • a01.user.demo.com/path?a=1 -> 127.0.0.1:8080/a01/path?a=1

这样后端就可以根据子目录解析不同的规则,甚至 Nginx 可以再进行链接重写。

子域名配置不同的目录

server {
    listen       80;
    server_name ~^([\w-]+)\.user\.demo\.com$;

    root /home/user/wwwroot/user/$1;
}

以上配置可以把不同的子域名分发到不同的目录中,做到路径分离的功能,如:

  • xuexb.user.demo.com -> /home/user/wwwroot/user/xuexb;
  • a01.user.demo.com -> /home/user/wwwroot/user/a01;

7.学习使用 echo 模块

Nginx echo模块是在nginx程序上扩展了 echo 输出字符的功能,对于调试很方便,项目地址: https://github.com/openresty/echo-nginx-module

安装

目前支持 nginx-1.11.2,高版本可能安装失败,我装 1.11.13 失败了,更多支持nginx的版本见: https://github.com/openresty/echo-nginx-module#compatibility

release 下载最新的安装包,并解压.

在配置 Nginx 时用: ./configure --add-module=/你的解压路径,并编译安装,如果是重新编译安装可参考: 重新编译安装

使用

只是研究了一些常用的

echo - 输出字符

  • 语法: echo [options] <string>...

  • 默认值: no
# 简单的hello,world!
server {
    location = /api/hello {
        echo "hello,world!";
    }
}

默认echo会自动换行,不需要换行可以: echo -n xxx;;

echo_before_body,echo_after_body - 页面前、后插入内容

  • 语法: echo_before_body [options] [argument]...

  • 默认值: no
server {
    # 简单的输出
    location = /api/hello {
        echo_before_body echo_before_body;
        echo "hello,world!";
        echo_after_body echo_after_body;
    }

    # 常见于代理页面前、后插内容
    location = /api/proxy_before_after {
        echo_before_body echo_before_body;
        echo_before_body echo_before_body;
        proxy_pass http://127.0.0.1;
        echo_after_body echo_after_body;
        echo_after_body echo_after_body;
    }
}

echo_sleep - 请求等待

  • 语法: echo_sleep <seconds>
  • 默认值: no
server {
    # 简单的输出
    location = /api/sleep {
        echo 1;

        # 等待1秒
        echo_sleep 1;

        echo 2;
    }
}

echo_location_async,echo_location - 请求指定路径

  • 语法: echo_location_async <location> [<url_args>]

  • 默认值: no
location /main {
    # 异步调用/sub
    echo_location_async /sub;

    echo world;
}

location /main2 {
    # 同步调用/sub
    echo_location_async /sub;

    echo world;
}

location /sub {
    echo hello;
}
异步跟同步的区别是:
  1. 异步会并行的去请求
  2. 同步等待当前请求结束才会往下执行

下面这个整个时间为2s,因为新路径最大是2s:

 location /main {
     echo_location_async /sub1;
     echo_location_async /sub2;
     echo "took $echo_timer_elapsed sec for total.";
 }
 location /sub1 {
     echo_sleep 2;
 }
 location /sub2 {
     echo_sleep 1;
 }

下面这个整个时间为3s,因为需要等待/sub1完成才进入/sub2:

 location /main {
     echo_location /sub1;
     echo_location /sub2;
     echo "took $echo_timer_elapsed sec for total.";
 }
 location /sub1 {
     echo_sleep 2;
 }
 location /sub2 {
     echo_sleep 1;
 }

可以通过第二个参数传querystring: echo_location_async /sub 'foo=Foo&bar=Bar';

echo_foreach_split - 分隔循环

  • 语法: echo_foreach_split <delimiter> <string>
location /split {
    echo_foreach_split ',' $arg_list;
        echo "item: $echo_it";
    echo_end;
}

上面配置当访问: /split?list=cat,dog,mouse 时会输出:

item: cat
item: dog
item: mouse

$arg_list是对应$args.list

那么配置全echo_location_async可以做成nginx-combo服务了,如:

location = /api/combo {
    echo_foreach_split ',' $query_string;
        echo "/* combo: $echo_it */";
        echo_location_async $echo_it;
        echo;
    echo_end;
}

访问: /api/combo?/a.js,/b.js,需要注意的是文件的路径必须以/开始,因为匹配的location,当然真的 nginx-combo 服务请看: nginx-http-concat

8.使用 nginx-http-concat

ginx-http-concat 是一个 Nginx 扩展模块,用来合并 HTTP 请求。

下载

访问 https://github.com/alibaba/nginx-http-concat/releases 官网下载最新的源包,如:

# 下载
wget https://github.com/alibaba/nginx-http-concat/archive/1.2.2.tar.gz

# 解压并记录解压后的目录
tar xzf 1.2.2.tar.gz

安装

使用 编译安装 ,在配置 configure 时添加参数:

# 配置
./configure 其他编译参数 --add-module=/刚才解压的目录

# 安装
make
[sudo] make install

注意: 如果是重新编译安装时不要运行 make install,可参数: 重新编译安装

如我的配置:

./configure 
    --...
    --add-module=/home/work/src/nginx-http-concat-1.2.2

如果有多个 --add-module 分别对应写上即可

配置

使用 location 匹配到你想要匹配的路径,对其设置参数:

server {
    location /static/css/ {
        concat on;
        concat_types text/css;
        concat_max_files 20;
    }
        
    location /static/js/ {
        concat on;
        concat_types application/javascript;
        concat_max_files 30;
    }
}
重启服务,如: nginx -s reload

更新配置项点击: https://github.com/alibaba/nginx-http-concat#module-directives

使用

现在就可以通过url中的??来合并了,比如: /static/css/??a.css,path/b.css

9.日志切割 - Shell

由于 Nginx 的日志(包括 access 和 error)默认都是存放在一个文件夹内,当时间长了,日志文件体积越来越大,不易管理,可以自己写个脚本来处理,其实就是定时定点去把日志移动下位置。

#!/bin/sh

# 日志源目录
sourceDir='/var/log/nginx/xuexb.com/last'

# 日志备份目录
backDir='/var/log/nginx/xuexb.com/back'

echo "split-logs start: $(date +%Y-%m-%d %H:%M:%S)"

ls $sourceDir | while read filename
do
    mkdir -p "$backDir/$(date +%Y%m%d)/"
    mv "$sourceDir/$filename" "$backDir/$(date +%Y%m%d)/"
    echo "$sourceDir/$filename => $backDir/$(date +%Y%m%d)/$filename"
done

# 刷新nginx
kill -USR1 `cat /var/run/nginx.pid`

echo "split-logs end: $(date +%Y-%m-%d %H:%M:%S)"
echo "----------------"

有几个注意的:

  1. 日志源目录 - 该目录存放日志文件,如 access.log,error.log,当然如果你的站点比较多,可以用域名来命名,比如: xuexb.com.access.log,xuexb.com.error.log 。
  2. 日志备份目录 - 会以Ymd命名文件夹名存放在该目录。
  3. 刷新 Nginx - 由于是直接移动日志文件,移动后需要刷新下 Nginx 使其再生成日志文件,当然也可以 nginx -s reload ,但 reload 要比 kill 的慢,而 kill -USR1 是无损的。
  4. 定时任务 - 需要使用 root 帐户下使用 crontab -e 插入一条定时任务,定时 23:55 开始分割,后面是把错误和信息输出到指定文件,方便调试,如:
55 23 * * * sh /你的目录/split-log >> /var/log/nginx/crontab.log 2>&1

10.配置浏览器缓存

使用 expires 参数。

不缓存

server {
    expires -1;
}

输出Response Headers:

Cache-Control:no-cache

当文件没有变更时会返回 304 ,有变更时会是 200 ,如果强制命中 200 可以再添加: if_modified_since off; 忽略 Request Headers 里的 If-Modified-Since 字段。

缓存

server {
    expires 1d;
}

1d为1天,单位如下:

ms  milliseconds
s   seconds
m   minutes
h   hours
d   days
w   weeks
M   months,30 days
y   years,365 days

如果希望最大缓存可以:

server {
    expires max;
}

输出Response Headers:

Cache-Control:max-age=315360000

根据链接设置缓存时间

server {
    # 设置为1月
    set $expires_time           1M;

    # 针对后台不缓存
    if ($request_uri ~* ^/admin(\/.*)?$) {
        set $expires_time       -1;
    }

    # 针对静态文件缓存最大
    if ($request_uri ~* ^/static(\/.*)?$) {
        set $expires_time       max;
    }

    # 设置吧
    expires $expires_time;
}

  

11.配置默认主页、目录浏览

设置默认主页

直接可以使用目录形式打开的页面称为默认主页,一般常见的有: index.htmlindex.htmindex.php 这些,要吧通过配置来完成,如:

server {
    root /网站根目录;

    # 设置默认主页,支持多个,按优先级来,空格分格
    index index.html index.htm index.php;
}

设置目录浏览

当一个目录内没有默认主页的文件时,直接访问目录会报 403 Forbidden 错误,而启用目录浏览功能后可以直接列出当前目录下的文件、文件夹,如:

server {
    root /网站根目录;

    # 优先使用默认主页
    index index.html index.htm;

    # 当默认主页不存在时直接列出目录内文件树
    autoindex on;
}

但多 autoindex 相关可官方文档: http://nginx.org/en/docs/http/ngx_http_autoindex_module.html

注意,线上运行环境最好别开启该配置,因为这将直接暴露你的文件~

12.add_header 指令技巧

官方的介绍:

Adds the specified field to a response header provided that the response code equals 200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), or 308 (1.13.0). The value can contain variables.

There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level.

If the always parameter is specified (1.7.5), the header field will be added regardless of the response code.

意思也就是说话在响应状态码成功时,add_header 指令才生效,并且当前《作用域》下没有 add_header 指令时,会向上层继承。

在使用过程中难免会遇到上级指令被覆盖的情况,如:

server {
    add_header x-name nginx;

    location / {
        root /path;
    }

    location /static/ {
        add_header x-name2 nginx2;
    }
}

当匹配到 / 时,由于 location / 中没有 add_header 指令,所以会继承 server 中的 x-name ,而当匹配到 /static/ 时,由于内容已经有 add_header 指令,则上层的 x-name 不会被继承,导致只会输出 x-name2

使用 include 语法

因为项目中的应用往往配置会有很多,我们可以把具体的功能拆分成独立的配置文件,使用 include 引用进来,如:

  • inc/no-cache.conf - 无缓存
  • inc/cache-max.conf - 缓存最大
  • inc/sts.conf - STS
  • inc/xss.conf - XSS 安全过滤
  • inc/php.conf - PHP FastCGI
  • ...

这样就可以按需引用了,如:

location / {
    include inc/sts.conf;
    include inc/security.conf;
    include vhost/xiaoshuo.io/no-cache.conf;
}

# png,jpg 转 webp
location ~* \.(jpg|png|meibanfawojiuxiangchangdianyirangquanzhongdadian)$ {
    include inc/sts.conf;
    include inc/security.conf;
    include vhost/xiaoshuo.io/cache-max.conf;

    if ($cookie_webp = '1') {
        rewrite ^/(.*)$ /$1.webp last;
    }
}
location ~* \.webp$ {
    include inc/sts.conf;
    include inc/security.conf;
    include vhost/xiaoshuo.io/cache-max.conf;
    try_files $uri $uri/ @webp;
}
location @webp {
    content_by_lua_file 'lua/webp.lua';
}

# http-concat + max cache
location /style/js/ {
    include inc/sts.conf;
    include inc/security.conf;
    include vhost/xiaoshuo.io/cache-max.conf;
    concat on;
    concat_types application/javascript;
    concat_max_files 30;
    default_type application/javascript;
}
location /style/css/ {
    include inc/sts.conf;
    include inc/security.conf;
    include vhost/xiaoshuo.io/cache-max.conf;
    concat on;
    concat_types text/css;
    concat_max_files 30;
    default_type text/css;
}

# 时效性高的接口
location ~* ^/((so\/(.+))||ajax)\.php$ {
    include inc/sts.conf;
    include inc/security.conf;
    include vhost/xiaoshuo.io/php.conf;
    add_header cache-control 'private, max-age=0, no-cache';
}

# 其实所有的 php
location ~* \.php$ {
    set $skip_cache 0;
    if ($request_method = POST) {
        set $skip_cache 1;
    }
    if ($arg_nocache = "1") {
        set $skip_cache 1;
    }

    fastcgi_cache_key $scheme$request_method$host$request_uri;
    fastcgi_cache_use_stale
        error
        timeout
        invalid_header
        updating
        http_500
        http_503;
    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;
    fastcgi_cache_valid 200 5m;
    fastcgi_cache_valid any 1m;
    fastcgi_cache m_xiaoshuo_php_cache;

    add_header x-php-cache-status $upstream_cache_status;
    include inc/sts.conf;
    include inc/security.conf;
    include vhost/xiaoshuo.io/cache-small.conf;
    include vhost/xiaoshuo.io/php.conf;
}

12.proxy_pass url 反向代理的坑

说到 Nginx 就不得不说 Nginx 的反向代理是多么的好用,一个指令 proxy_pass 搞定反向代理,对于接口代理、负载均衡很是实用,但 proxy_pass 指令后面的参数很有讲究。

网上有很多什么绝对路径、相对路径的说法,其实在实际的应用中就分为两种情况:

url 只是 host

这里指不包含 $uri ,如:

  • http://host - √
  • https://host - √
  • http://host:port - √
  • https://host:port - √
  • http://host/ - x
  • http://host:port/ - x

这时候 location 匹配的完整路径将直接透传给 url ,如:

// 访问:   /                               后端:   /
// 访问:   /api/xx                         后端:   /api/xx
// 访问:   /api/xx?aa                      后端:   /api/xx?aa
location / {
    proxy_pass http://node:8080;
}

// 访问:   /api/                           后端:   /api/
// 访问:   /api/xx                         后端:   /api/xx
// 访问:   /api/xx?aa                      后端:   /api/xx?aa
// 访问:   /api-xx?aa                      后端:
location /api/ {
    proxy_pass http://node:8080;
}

// 访问:   /api/                           后端:   /api/
// 访问:   /api/xx                         后端:   /api/xx
// 访问:   /api/xx?aa                      后端:   /api/xx?aa
// 访问:   /api-xx?aa                      后端:   /api-xx?aa
location /api {
    proxy_pass http://node:8080;
}

url 包含路径

注意,这里的路径哪怕只是一个 / 也是存在的,如:

  • http://host - x
  • https//host/ - √
  • http://host:port - x
  • https://host:port/ - √
  • http://host/api - √
  • http://host/api/ - √

proxy_pass urlurl 包含路径时,匹配时会根据 location 的匹配后的链接透传给 url ,注意匹配后就是这样:

location 规则访问的原始链接匹配之后的路径
location //
location //aa
location //a/b/c?da/b/c?d
location /a//a/
location /a//a/b/c?db/c?d

明白匹配之后的路径后,在 proxy_pass url 包含路径时,将会把匹配之后的路径透传给 url ,如:

// 访问:   /                               后端:   /
// 访问:   /api/xx                         后端:   /api/xx
// 访问:   /api/xx?aa                      后端:   /api/xx?aa
location / {
    proxy_pass http://node:8080/;
}

// 访问:   /api/                           后端:   /
// 访问:   /api/xx                         后端:   /xx
// 访问:   /api/xx?aa                      后端:   /xx?aa
// 访问:   /api-xx?aa                      未匹配
location /api/ {
    proxy_pass http://node:8080/;
}

// 访问:   /api                            后端:   /
// 访问:   /api/                           后端:   //
// 访问:   /api/xx                         后端:   //xx
// 访问:   /api/xx?aa                      后端:   //xx?aa
// 访问:   /api-xx?aa                      后端:   /-xx?aa
location /api {
    proxy_pass http://node:8080/;
}

// 访问:   /api/                           后端:   /v1
// 访问:   /api/xx                         后端:   /v1xx
// 访问:   /api/xx?aa                      后端:   /v1xx
// 访问:   /api-xx?aa                      未匹配
location /api/ {
    proxy_pass http://node:8080/v1;
}

// 访问:   /api/                           后端:   /v1/
// 访问:   /api/xx                         后端:   /v1/xx
// 访问:   /api/xx?aa                      后端:   /v1/xx
// 访问:   /api-xx?aa                      未匹配
location /api/ {
    proxy_pass http://node:8080/v1/;
}

由以上规则可以看出,当 proxy_pass url 中包含路径时,结尾的 / 最好同 location 匹配规则一致。

当 proxy_pass 遇到正则

location 以正则形式匹配时,proxy_pass 就不能以 / 结束了,也就是不能包含路径了,比如错误的:

location ~* ^/api/ {
    proxy_pass http://host/;
}

location / {
    if ($uri ~* ^/api/) {
        proxy_pass http://host/;
    }
}

解决办法就是把链接中的路径去掉。

重写代理链接 - url rewrite

当原始链接(浏览器访问的链接)和代理服务器链接规则不一致时,可以使用 Nginx URL Rewrite 功能去动态的重写,如:

location ~* ^/api/ {
    rewrite ^/api/(.*) /?path=$1 break;
    proxy_pass http://node:8080;
}

以上请求会把匹配 /api/ 的链接重写为 /?path= 的链接透传给 node:8080 服务,有意思的是当使用 rewrite 指令并且生效后,proxy_pass url 链接中的路径会被忽略,如:

// 访问:   /                               后端:   /node/
// 访问:   /api                            后端:   /node/api
// 访问:   /api/                           后端:   /?path=
// 访问:   /api/a/b/c                      后端:   /?path=a/b/c
location / {
    rewrite ^/api/(.*) /?path=$1 break;
    proxy_pass http://node:8080/node/;
}

  

转自:https://xuexb.github.io/learn-nginx/