nginx代理入门

本文通过几个的实例,介绍nginx配置代理的几种方式,以及全局代理的配置。

注:本文中所有实例均可以直接使用,不需要改变任何配置(包括域名,域名解析到127.0.0.1,注意:由于https证书自动续期的需要,仅国内线路的*.local.lyz810.com指向127.0.0.1,国外线路已变更为服务器的IP地址
原始站点(被代理站点)
测试后面所有实例时,请确保该源站配置存在

server {
    listen 80;
    server_name origin.local.lyz810.com;
    default_type text/plain;
    location / {
        return 200 "This is origin.local.lyz810.com$request_uri";
    }

    location =/setcookie/ {
        add_header Set-Cookie "testcookie=123; domain=origin.local.lyz810.com; path=/";
        return 200 "Please find result in response header";
    }

    location =/showcookie/ {
        return 200 $http_cookie;
    }
}

当访问origin.local.lyz810.com的任意页面,都可以看到This is origin.local.lyz810.com后面跟着url

实例一:
通过其他域名进行代理(如目前线上访问google.lyz810.com即可实现代理google.com.hk)
代理配置:

server {
    listen 80;
    server_name proxy.local.lyz810.com;

    location / {
        proxy_pass http://origin.local.lyz810.com/;
    }
}

访问proxy.local.lyz810.com的任意页面,会对应访问origin.local.lyz810.com的页面
如访问proxy.local.lyz810.com/test,返回This is origin.local.lyz810.com/test

实例二:
代理服务器上的url与源站url存在差异,访问proxy.local.lyz810/test/下的所有页面,要求返回origin.local.lyz810.com/hello/对应页面(test改为源站的hello)
代理配置:

server {
    listen 80;
    server_name proxy.local.lyz810.com;

    location /test/ {
        proxy_pass http://origin.local.lyz810.com/hello/;
    }
}

请注意hello后面的/,这个是必须有的,nginx在处理url时,会将location匹配的路径从访问url中去掉后拼接到proxy_pass指令后面
例如访问proxy.local.lyz810.com/test/abc.html,匹配location为/test/,把匹配的部分从url中去掉,还剩abc.html,直接拼接到proxy_pass后面,即http://origin.local.lyz810.com/hello/abc.html
一种典型的错误写法:

    ...
    location /test {
        proxy_pass http://origin.local.lyz810.com/hello/;
    }
    ...

上面的写法语法上没有问题,但是跟需求不匹配。也许你会发现,这个错误的写法在访问proxy.local.lyz810.com/test/abc.html时仍然返回This is origin.local.lyz810.com/hello//abc.html(nginx会对url进行标准化处理,两个/跟一个/访问的是同一个资源)
这是由于hello以/结尾,nginx会将/test后面的内容直接接到proxy_pass最后,造成2个/
但如果访问proxy.local.lyz810.com/test123/abc.html则会返回http://origin.local.lyz810.com/hello/123/abc.html,这个与预期不符。

实例三:
源站要设cookie,由于域名不同,跨域无法设置cookie,通过nginx可以解决这个问题。如访问origin.local.lyz810.com/setcookie/后会在origin.local.lyz810.com域下根目录种一个cookie,现在希望通过代理,访问源站,并在proxy.local.lyz810.com的/test上种上相同的cookie,配置如下:

server {
    listen 80;
    server_name proxy.local.lyz810.com;

    location / {
        proxy_pass http://origin.local.lyz810.com/;
        proxy_cookie_domain origin.local.lyz810.com $host;
        proxy_cookie_path / /test;
    }
}

访问proxy.local.lyz810.com/setcookie/,查看响应头(Set-Cookie:testcookie=123; domain=proxy.local.lyz810.com; path=/test)。
这种使用场景是使用一个域名代理另一个域名的页面,并可以代理认证信息。
我们知道,浏览器是按照域名携带cookie的,所以访问proxy.local.lyz810.com的时候只能带着proxy.local.lyz810.com的cookie访问源站,显然用户不可能自己在proxy.local.lyz810.com上添加cookie,所以我们需要把访问源站登录时,设置的cookie转换为代理域下的cookie,这样访问代理域就和访问源站域完全一样了,相当于镜像站。
通常做完整镜像站时,只需要将cookie的domain修改为代理域即可,path保持一致不需要配置。

实例四:
有时候,页面上的js会对域名做判断,上面所有的方法只能骗过源站的服务器,而不能骗过浏览器的location.hostname,这种情况下,nginx是无法完美解决,如果是开发调试,可以通过配置host来实现。
例如,我的项目地址是demo.lyz810.com,我负责前端开发,需要调用后端接口(后端接口都在/fetch/下)demo.lyz810.com/fetch/api.php?action=getJson(完全可以通过实例三的方法设置另一个域,然后做cookie共享,但我不喜欢开发和线上访问的不一样),那么可以通过下面的方式来实现:
1.设置host 127.0.0.1 demo.lyz810.com
2.nginx配置如下:

server {
    listen 80;
    server_name demo.lyz810.com;
    default_type text/html;
    location /fetch/ {
        proxy_pass http://133.130.97.238/fetch/;
        proxy_set_header Host $host;
    }

    location / {
        return 200 "Please open console to see result<script src='/fetch/api.php?action=getJson'></script>";
    }
}

访问demo.lyz810.com,打开浏览器的控制台,看到发送请求/fetch/api.php返回了信息(这个是线上真实的接口数据),本例中使用了return指令直接返回了一段html代码,正常开发时,此处一般是由root指令指定的静态文件目录,也就是我们的工程目录。
几点说明:
1.proxy_pass需要填写服务器的IP地址,因为本机设置了host,并且nginx在本机部署,所以会受hosts的影响
2.proxy_set_header是nginx访问线上服务器时携带的请求头,因为上面写的是IP,而服务器不能直接通过IP访问(服务器上挂了那么多站点,你用IP访问,它也不知道返回哪个站点),加上Host这个请求头,服务器就知道返回哪个站点下的资源。

实例五:
终极代理大法一,访问本机8888端口,代理任何网站(需要配hosts文件)
代理配置:

server {
    listen 8888;
    server_name _ default;
    resolver 119.29.29.29;
    location / {
        proxy_pass https://$host$request_uri$is_args$args;
    }
}

绑定hosts:127.0.0.1 www.sogou.com,访问http://www.sogou.com:8888/web?query=lyz810&_asf=www.sogou.com可以看到结果,这里用80端口也是完全可以的,hosts文件只要指向本机就可以,如果是局域网内其他机器访问,可以直接配置hosts文件指向nginx服务器的IP,可以通过nginx代理任意站点。
注意:server_name 后面的default表示如果没有匹配到其他的域名,就用当前server,nginx配置文件中同一个端口最多有1个default的server,否则后面设置的default会无效。proxy_pass中域名使用了变量,所以需要配置resolver,即dns服务器地址,这里用的是腾讯云提供的公共dns。此例中代理走https协议,因为目前大部分网站支持https,这里暂且用https,后面有更灵活配置方式。
这种配置的一种应用是共享账号,可以登录某个网站后,将cookie记下来,配置在服务端的proxy_set_header Cookie …中,其他人访问该代理服务器,不需要登录就可以访问公共账号登录的内容。

实例六:
全局代理,无需配置hosts

server {
    listen 80;
    server_name proxy.local.lyz810.com;
    resolver 119.29.29.29;

    location ~ ^/([^/]*)(.*)$ {
        proxy_pass $scheme://$1$2$is_args$args;
    }
}

注意:这种方法只适合于代理任意已知地址,由于网页中很少使用相对地址,所以一般不能直接通过这种代理访问网站,仅限开发调试使用。
这里给出的例子并不完美,转发使用了$scheme根据访问的协议进行同协议转发,但只监听了80端口,所以此处一定为http,可以同时监听443并开启ssl,同时设置证书,已超出本文的内容,不再赘述。
这种配置也有应用场景,目前用于统一代理某些已知接口,不需要每次修改配置文件,只需要写好正确的地址,即可拿到数据。
线上应用在proxy.lyz810.com上,通过此服务器进行跨域管理,所有需要跨域访问的内容交给服务器去访问,返回的结果通过add_header增加跨域头,这样就可以让应用轻松的跨域,具体即实现方法以后介绍。

nginx初学者向导

本向导介绍了以下内容:1.nginx的基本设置,并提供了一些简单的任务与完成方法。2.如何启动、停止nginx,如何重新加载nginx配置文件。3.解释了配置文件的结构。4.如何设置nginx作为静态文件服务器。5.如何设置nginx作为代理服务器。6.如何通过FastCGI协议连接应用。

nginx有一个主进程和一些工作进程。主进程的作用是读取并解析配置文件并维持其他工作进程,工作进程会去处理请求。nginx采用事件驱动模式与系统依赖机制来高效的将请求分配到工作进程上。工作进程数可以在配置文件中定义,可以是固定的某个值或根据CPU核心数自动适配。
nginx和相关模块的工作方式由配置文件决定。默认情况下,配置文件叫做nginx.conf,该文件位于/usr/local/nginx/conf,/etc/nginx,或/usr/local/etc/nginx.
启动停止服务与重新加载配置文件
执行可执行文件即可启动nginx(window下,双击nginx.exe,linux下使用nginx命令)。一旦nginx启动,可以通过调用-s参数控制nginx,语法如下:

nginx -s signal

signal可以是下列一个值:

  • stop:快速关闭
  • quit:优雅关闭
  • reload:重新载入配置文件
  • reopen:重新打开日志文件

例如,停止nginx进程并等待所有工作进程处理完当前的请求可以使用

nginx -s quit

执行该命令的用户需要与启动nginx的用户相同
配置文件中的改动不会立即生效,直到重新加载配置文件或nginx重启。重新加载配置文件命令如下:

nginx -s reload

当nginx主进程接收到重新加载配置文件的信号时,它会检查新的配置文件的语法,并会尝试应用新的配置。如果成功了,nginx主进程会启动新的工作进程,并通知旧的工作进程,让他们关闭。旧的工作进程在收到关闭命令后会停止接受新的连接并会继续处理当前的请求直到所有请求处理完毕,然后旧的工作进程会退出。
可以借助Unix系统的工具如kill命令向nginx进程发送信号,此时信号将会直接通过进程ID发送给进程。nginx的主进程ID默认写在nginx.pid中,该文件位于/usr/local/nginx/logs或/var/run.例如,如果主进程ID为1628,发送QUIT信号让nginx优雅的关闭需要执行:

kill -s QUIT 1628

获取正在运行中的nginx进程的列表,可以用ps命令,例如:

ps -ax | grep nginx

配置文件结构
nginx由定义在配置文件中的指令控制的模块构成。指令分为简单指令和块级指令。简单指令由名称和参数构成,名称与参数之间用空格分开,结尾以英文分号结束。块级指令结构同简单指令,与简单指令不同的是,它是一些被花括号“{}”包裹指令的集合。如果一个块级指令中有其他的指令,则叫做一个上下文(events、http、server及location)。
配置文件中,不在任何上下文中的指令被看做是main上下文,events和https指令在main上下文中,server指令在http上下文中,location在server上下文中。

以“#”开头的行是注释信息
静态内容服务
服务器的一个重要的任务是提供文件(图片或静态html等)。你可以在此实现一个例子,根据请求,文件从不同的本地目录:/data/www(包含html文档)、/data/images(包含图片)获取并提供服务。这需要编辑配置文件,一个http块内包含了一个server块,server块中有两个location块。
首先,创建目录/data/www并放置一个内容任意index.html,创建目录/data/images并放置一些图片。
然后,打开配置文件,默认的配置文件已经包含了几个server块的例子,其中大部分都注释了,现在注释所有的块,并新建一个如下的块:

http {
    server {
    }
}

一般地,配置文件可以包含多个server块,分别定义了监听的端口和主机名称。当nginx决定了使用哪个server块处理请求,它会检测请求头中的URI及参数并匹配到location块上。
在server块中添加一个location块方法如下:

location / {
    root /data/www;
}

location块会将“/”前缀与请求的URI进行比较,对于匹配的请求,URI将会拼接到root指令定义的路径后面,本例中及/data/www,然后在本地文件中找到请求的文件。如果有很多location都能匹配url,则nginx会选择最长的前缀。上面的location定义了最短的前缀,只有一个字符,所以只有其他location都不匹配的时候,这个请求才会被location /匹配。
添加第二个location块:

location /images/ {
    root /data;
}

它将会匹配/images/开头(/也会匹配,但比/images/短)。
整体的配置如下:

server {
    location / {
        root /data/www;
    }

    location /images/ {
        root /data;
    }
}

以上配置好了一个监听在80端口,可以被本机通过http://localhost访问的服务。以/images/开头的请求,会响应一个从/data/images目录对应的文件。例如,响应http://localhost/images/example.png请求,nginx会发送/data/images/example.png文件,如果文件不存在,则返回404错误。不以/images/开头的URI将会定位到/data/www目录。例如,响应http://localhost/some/example.html请求,nginx会发送/data/www/some/example.html文件。
要应用新的配置文件,需要启动nginx或通过nginx的重新载入命令:

nginx -s reload

如果出现问题,可以尝试在access.log和error.log中找到原因,文件位于/usr/local/nginx/logs 或 /var/log/nginx.
设置一个简单的代理服务器
nginx的常用功能之一是用作代理服务器,接受请求并发送到被代理的服务器上然后把服务器的响应返回给客户端。
我们将要配置一个基础的代理服务器,该服务器对于图片文件会从本地读取,其他请求会发送到被代理的服务器上。本例中,两个服务器都在一个nginx实例中定义。
首先,配置被代理的服务器:

server {
    listen 8080;
    root /data/up1;

    location / {
    }
}

这个服务器监听8080端口(80端口已经占用了),将所有请求定位到本地/data/up1目录。建立此目录并放置index.html。注意,root指令放在server上下文中,location中就不再添加root指令。
然后,将上例中的server修改一下:

server {
    location / {
        proxy_pass http://localhost:8080;
    }

    location /images/ {
        root /data;
    }
}

现在修改第二个location块,当前是/images/定位到/data/images下,要将其改为通过文件扩展名进行定位。修改的location如下:

location ~ \.(gif|jpg|png)$ {
    root /data/images;
}

参数是一个正则表达式,匹配了所有以.gif、.jpg和.png结尾的URI。正则表达式应以~开头。匹配正则表达式的请求会被定位到/data/images目录。
当nginx选择location块时,首先会location块前缀的长度,并记住最长的前缀,然后检测正则表达式。如果有一个请求匹配了正则表达式,则选择该location,否则选择最长前缀的匹配。
最终配置文件如下:

server {
    location / {
        proxy_pass http://localhost:8080/;
    }

    location ~ \.(gif|jpg|png)$ {
        root /data/images;
    }

服务器会过滤所有以.gif、.jpg、及.png的请求,把他们定位到/data/images目录(并将路径后面追加上URI),其他的请求会转发到定义的被代理服务器上(localhost:8080)。
修改完配置文件需要重新载入配置文件才会生效。
设置FastCGI代理
nginx可以FastCGI协议的网关代理服务器,支持应用服务如PHP。
最基础的nginx FastCGI协议网关代理服务器配置使用fastcgi_pass指令。通过fastcgi_param指令设置传给FastCGI服务的参数。支持FastCGI的服务监听localhost:9000端口。在PHP中,SCRIPT_FILENAME参数用于定义script name,QUERY_STRING参数用于传递请求参数。完整配置如下:

server {
    location / {
        fastcgi_pass  localhost:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param QUERY_STRING    $query_string;
    }

    location ~ \.(gif|jpg|png)$ {
        root /data/images;
    }
}

以上将会把所有除静态文件之外的请求通过FastCGI协议转发到localhost:9000上。