ngin Finding Everything About Nginx Here

nginx获取访问域名和真实ip

发表于 2015-07-19 阅读数 3953

nginx是如何处理访问域名的呢,如果加入代理后如何处理访问ip的呢,后端服务器如何拿真实的ip呢?本文将一一弄清这一切问题。我们将从访问的那一刻开始到后端服务器的处理为结束点,记录这一过程中跟域名和ip有关的每个关键点。让您轻松解决碰到的有关访问域名和真实ip的问题。

1.访问时发生了什么?

a、浏览器输入 http://www.example.com/index.html时,浏览器向服务器发送了什么请求呢?
GET /index.html HTTP/1.1
Host: www.example.com
看到没,没有发送 GET http://www.example.com/index.html,而是把域名放到了HOST这个请求头部里。这里的HOST是非常重要的,呆会我会解释。

b、curl 工具访问 curl http://www.example.com/index.html时,它向服务器发送的数据跟浏览器一样。

c、通过telnet访问,这个是原始的数据构造。
你可以输入:
GET /index.html HTTP/1.1
HOST: www.example.com

也可以这样:
GET http://www.example.com/index.html HTTP/1.1
Host: www.other.com

到这里明白为什么HOST是如此重要了吧,我们接下来分析nginx是如何处理的。

2. nginx的设计
nginx有个很重要的结构体 ngx_http_request_t 体现请求这个业务模型。

a、处理请求行
static void ngx_http_process_request_line(ngx_event_t *rev) 
{
        ...
       if (r->host_start && r->host_end) {  # GET http://{host}/url ,如果request line有host,则处理如下

            host.len = r->host_end - r->host_start;

            host.data = r->host_start;               

            if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {

                return;

            }

            r->headers_in.server = host;  # 很重要的记录,r->headers_in.server在后面很多地方用到,这个体现的host,可以是ip也可以是domain

        }

}

b、处理请求头部
这里有关系的头部信息只有 Host

static ngx_int_t ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,     ngx_uint_t offset)

{

    host = h->value;

    if (r->headers_in.server.len) {  # 如果已经有了,则忽略这个头部信息,所以 GET http://host/url 的host优先级最高

        return NGX_OK;

    }
    
    if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {

        return NGX_ERROR;

    }

    r->headers_in.server = host;

}


c. 配置文件里的server_name。nginx还记录了这个信息,cscf->server_name

3. nginx的变量
先了解以上几个信息后,我们看下有哪些变量跟这些有关

a、$host, $server_name

static ngx_int_tngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v,   uintptr_t data)

{

    if (r->headers_in.server.len) {

        v->len = r->headers_in.server.len;

        v->data = r->headers_in.server.data;

    } else {

        v->len = cscf->server_name.len;

        v->data = cscf->server_name.data;

    }

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    return NGX_OK;

}

$host的值为 {GET http://host} > Host > cscf->server_name。所以host体现的是被访问到的主机,不仅可以是ip,也可以是域名。

$server_name的值为cscf->server_name

b、$server_addr, $remote_addr

这两个分别是服务器ip地址和客户端地址

所以到这里您已经可以轻松拿到服务器的server_name, addr和客户端的addr

4、真实ip的概念
加入代理之后,情况变的有点复杂,后端服务器经常需要拿到客户端的真实ip,而不是这个代理服务器的ip。我们将解决这一问题。
在php我们获取客户端的ip,经常这样处理拿$_SERVER['REMOTE_ADDR']这个变量。
如果加入代理后,不做任何处理,这个值拿到的是nginx的ip,为什么呢?
看下配置fastcgi_params
fastcgi_param  REMOTE_ADDR        $remote_addr;  # 这个我们之前说的是客户端ip地址,现在代理服务器是nginx的客户端了。
解决办法很简单,让代理服务器告诉nginx客户端的ip即可。
如果是用nginx作代理,可以加proxy_set_header xxx $remote_addr;
然后程序就可以拿这个头部的信息了。

本文旨在讲清原理,了解其过程后,解决就相当轻松了。