[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"content-how_nginx_processes_a_request":3,"$fxyu49nb244a8":21},{"id":4,"type":5,"slug":6,"title":7,"date":8,"category":9,"tags":10,"body_markdown":12,"permalink":13,"excerpt_src":14,"media_type":14,"media_title":14,"media_author":14,"media_url":14,"rating":14,"layout":14,"pv":15,"admin_only":15,"created_at":16,"updated_at":16,"deleted_at":14,"status":17,"html":18,"excerpt":19,"cover":20},170,"article","how_nginx_processes_a_request","【译】nginx是如何处理请求的","2017-05-05 12:55","web",[11],"nginx","\nNginx首先需要确定由哪个`server`来处理请求。我们看一个简单的配置文件，在`*:80`端口上包含了三个虚拟主机（`server`）：\n\n```\nserver {\n    listen      80;\n    server_name example.org www.example.org;\n    ...\n}\n\nserver {\n    listen      80;\n    server_name example.net www.example.net;\n    ...\n}\n\nserver {\n    listen      80;\n    server_name example.com www.example.com;\n    ...\n}\n```\n\n在这个配置下，Nginx只通过请求的`Host`头来决定路由到哪个`server`。如果`Host`头的值跟所有的`server`的`server_name`都不匹配，或者请求中没有包含这个阔大，Nginx会使用这个端口上的默认`server`。在这个配置文件中，默认`server`是指第一个，这正是Nginx标准的默认行为。默认`server`也可以通过配置显示指定，只需要在`listen`指令值中加上`default_server`参数即可。\n\n\u003C!-- more -->\n\n```\nserver {\n    listen      80 default_server;\n    server_name example.net www.example.net;\n    ...\n}\n```\n\n> `default_server`参数在 0.8.21 版本之后可用。在更早的版本中，应该使用`default`参数。\n\n值得注意的是，默认`server`是`listen`指令的参数，而不是`server_name`的。下文会详细介绍。\n\n## 如何阻止没有指明 Host 的请求\n\n如果要禁止没有包含`Host`头的请求，可以用下面的配置让一个`server`丢弃这样的请求：\n\n```\nserver {\n    listen      80;\n    server_name \"\";\n    return      444;\n}\n```\n\n如果请求没有带`Host`头，将与`server_name`为空字符串的`server`匹配，返回非标准的私有状态码444时，Nginx会关闭连接。\n\n> 从 0.8.48 版本开始，空字符串是`server_name`的默认值，因此`server_name \"\"`可以省略。在更早的版本中，默认值是机器的主机名。\n\n## 基于 IP 和基于主机名的虚拟主机\n\n我们来看一个更复杂的例子，这个例子中有一些监听在不同地址上的`server`：\n\n```\nserver {\n    listen      192.168.1.1:80;\n    server_name example.org www.example.org;\n    ...\n}\n\nserver {\n    listen      192.168.1.1:80;\n    server_name example.net www.example.net;\n    ...\n}\n\nserver {\n    listen      192.168.1.2:80;\n    server_name example.com www.example.com;\n    ...\n}\n```\n\n在这个配置中，Nginx 首先将请求的 IP 地址和端口与`server`进行匹配，得到一些匹配的`server`。然后根据请求的`Host`头与这些`server`的`server_name`进行匹配。如果`server_name`无法匹配，则请求将由默认`server`处理。例如，在`192.168.1.1:80`上收到一个`www.example.com`的请求，这个请求将由`192.168.1.1:80`上的默认`server`进行处理，也就是由第一个`server`进行处理，因为无法在这个（IP 地址和）端口上匹配到`www.example.com`。\n\n前面已经说过，默认`server`是`listen`指令的一个参数，那么监听不同的（IP 地址和）端口就可以指定不同的默认`server`：\n\n```\nserver {\n    listen      192.168.1.1:80;\n    server_name example.org www.example.org;\n    ...\n}\n\nserver {\n    listen      192.168.1.1:80 default_server;\n    server_name example.net www.example.net;\n    ...\n}\n\nserver {\n    listen      192.168.1.2:80 default_server;\n    server_name example.com www.example.com;\n    ...\n}\n```\n\n## 一个简单的 PHP 站点配置\n\n现在我们通过一个简单的 PHP 站点来看一下 Nginx 是如何处理`location`的：\n\n```\nserver {\n    listen      80;\n    server_name example.org www.example.org;\n    root        \u002Fdata\u002Fwww;\n\n    location \u002F {\n        index   index.html index.php;\n    }\n\n    location ~* \\.(gif|jpg|png)$ {\n        expires 30d;\n    }\n\n    location ~ \\.php$ {\n        fastcgi_pass  localhost:9000;\n        fastcgi_param SCRIPT_FILENAME\n                      $document_root$fastcgi_script_name;\n        include       fastcgi_params;\n    }\n}\n```\n\nNginx 首先会通过遍历字符串的方式查找指向最具体的`location`前缀，这与配置顺便无关。在上面的楝文件中，唯一的前缀是`\u002F`，它可以匹配任意请求，将被最后使用。然后 Nginx 会按照配置书写的顺序进行正则表达式匹配。一旦匹配到第一条规则，则会停止后续查找，Nginx 将使用这个`location`。如果正则表达式匹配全部失败了，则 Nginx 会使用前面找到的前缀指向最具体的`location`。\n\n值得注意的是，所有类型的`location`都只匹配请求的`URI`部分，不包括任何参数。这是因为请求的参数可能有很多种形式，例如：\n\n```\n\u002Findex.php?user=john&page=1\n\u002Findex.php?page=1&user=john\n```\n\n此外，用户可以在参数中随便添加任何东西：\n\n```\n\u002Findex.php?page=1&something+else&user=john\n```\n\n现在我们来看看，按上面的配置文件，一个请求将如何被处理：\n\n- 请求`\u002Flogo.gif`首先被前缀`location` `\u002F`匹配到，然后被正则表达式`\\.(gif|jpg|png)$`匹配到。这样的话，它将由后者进行处理。因为指定了`root \u002Fdata\u002Fwww`，因此这个请求将被映射到`\u002Fdata\u002Fwww\u002Flogo.gif`，这个文件将被发送到客户端。\n- 请求`\u002Findex.php`也被前缀`location ` `\u002F`匹配到，然后被正则表达式`\\.(php)$`匹配到。这样的话，它将由后者进行处理。请求被转交到监听在`localhost:9000`的 FastCGI 服务进行处理。`fastcgi_param`指令会将 FastCGI 的参数`SCRIPT_FILENAME`设置为`\u002Fdata\u002Fwww\u002Findex.php`，然后 FastCGI 服务会执行这个文件。`$document_root`变量的值等于`root`指令的值，变量`$fastcgi_script_name`的值等于请求的 URI ，也就是`\u002Findex.php`。\n- 请求`\u002Fabout.html`只被前缀`location` `\u002F`匹配到，这样它将被在这个`location`中进行处理。因为指定了`root \u002Fdata\u002Fwww`，因此这个请求将被映射到`\u002Fdata\u002Fwww\u002Fabout.html`，这个文件将被发送到客户端。\n- 请求`\u002F`的处理更复杂一些。它只被前缀`location` `\u002F`匹配到，这样它将被在这个`location`中进行处理。接下来`index`指令将根据`root \u002Fdata\u002Fwww`指令的路径探测`index`参数中指定的文件是否存在。如果`\u002Fdata\u002Fwww\u002Findex.html`不存在，而`\u002Fdata\u002Fwww\u002Findex.php`存在，则该指令将内部跳转到`\u002Findex.php`，然后 Nginx 会像对待新请求一样重新匹配这个请求。如前文所述，这个请求将被 FastCGI 服务进行处理。\n\n编写：Igor Sysoev 编辑：Brian Mercer\n\n原文：\u003Chttp:\u002F\u002Fnginx.org\u002Fen\u002Fdocs\u002Fhttp\u002Frequest_processing.html>\n","\u002Farticle\u002Fhow_nginx_processes_a_request.html",null,0,"2026-08-28 04:37:17","published","\u003Cp>Nginx首先需要确定由哪个\u003Ccode>server\u003C\u002Fcode>来处理请求。我们看一个简单的配置文件，在\u003Ccode>*:80\u003C\u002Fcode>端口上包含了三个虚拟主机（\u003Ccode>server\u003C\u002Fcode>）：\u003C\u002Fp>\n\u003Cpre class=\"shiki\" style=\"background-color:#121212;color:#dbd7caee\" tabindex=\"0\">\u003Ccode>server {\n    listen      80;\n    server_name example.org www.example.org;\n    ...\n}\n\nserver {\n    listen      80;\n    server_name example.net www.example.net;\n    ...\n}\n\nserver {\n    listen      80;\n    server_name example.com www.example.com;\n    ...\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>在这个配置下，Nginx只通过请求的\u003Ccode>Host\u003C\u002Fcode>头来决定路由到哪个\u003Ccode>server\u003C\u002Fcode>。如果\u003Ccode>Host\u003C\u002Fcode>头的值跟所有的\u003Ccode>server\u003C\u002Fcode>的\u003Ccode>server_name\u003C\u002Fcode>都不匹配，或者请求中没有包含这个阔大，Nginx会使用这个端口上的默认\u003Ccode>server\u003C\u002Fcode>。在这个配置文件中，默认\u003Ccode>server\u003C\u002Fcode>是指第一个，这正是Nginx标准的默认行为。默认\u003Ccode>server\u003C\u002Fcode>也可以通过配置显示指定，只需要在\u003Ccode>listen\u003C\u002Fcode>指令值中加上\u003Ccode>default_server\u003C\u002Fcode>参数即可。\u003C\u002Fp>\n\u003Cpre class=\"shiki\" style=\"background-color:#121212;color:#dbd7caee\" tabindex=\"0\">\u003Ccode>server {\n    listen      80 default_server;\n    server_name example.net www.example.net;\n    ...\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cblockquote>\n\u003Cp>\u003Ccode>default_server\u003C\u002Fcode>参数在 0.8.21 版本之后可用。在更早的版本中，应该使用\u003Ccode>default\u003C\u002Fcode>参数。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>值得注意的是，默认\u003Ccode>server\u003C\u002Fcode>是\u003Ccode>listen\u003C\u002Fcode>指令的参数，而不是\u003Ccode>server_name\u003C\u002Fcode>的。下文会详细介绍。\u003C\u002Fp>\n\u003Ch2>如何阻止没有指明 Host 的请求\u003C\u002Fh2>\n\u003Cp>如果要禁止没有包含\u003Ccode>Host\u003C\u002Fcode>头的请求，可以用下面的配置让一个\u003Ccode>server\u003C\u002Fcode>丢弃这样的请求：\u003C\u002Fp>\n\u003Cpre class=\"shiki\" style=\"background-color:#121212;color:#dbd7caee\" tabindex=\"0\">\u003Ccode>server {\n    listen      80;\n    server_name &quot;&quot;;\n    return      444;\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>如果请求没有带\u003Ccode>Host\u003C\u002Fcode>头，将与\u003Ccode>server_name\u003C\u002Fcode>为空字符串的\u003Ccode>server\u003C\u002Fcode>匹配，返回非标准的私有状态码444时，Nginx会关闭连接。\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>从 0.8.48 版本开始，空字符串是\u003Ccode>server_name\u003C\u002Fcode>的默认值，因此\u003Ccode>server_name &quot;&quot;\u003C\u002Fcode>可以省略。在更早的版本中，默认值是机器的主机名。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2>基于 IP 和基于主机名的虚拟主机\u003C\u002Fh2>\n\u003Cp>我们来看一个更复杂的例子，这个例子中有一些监听在不同地址上的\u003Ccode>server\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Cpre class=\"shiki\" style=\"background-color:#121212;color:#dbd7caee\" tabindex=\"0\">\u003Ccode>server {\n    listen      192.168.1.1:80;\n    server_name example.org www.example.org;\n    ...\n}\n\nserver {\n    listen      192.168.1.1:80;\n    server_name example.net www.example.net;\n    ...\n}\n\nserver {\n    listen      192.168.1.2:80;\n    server_name example.com www.example.com;\n    ...\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>在这个配置中，Nginx 首先将请求的 IP 地址和端口与\u003Ccode>server\u003C\u002Fcode>进行匹配，得到一些匹配的\u003Ccode>server\u003C\u002Fcode>。然后根据请求的\u003Ccode>Host\u003C\u002Fcode>头与这些\u003Ccode>server\u003C\u002Fcode>的\u003Ccode>server_name\u003C\u002Fcode>进行匹配。如果\u003Ccode>server_name\u003C\u002Fcode>无法匹配，则请求将由默认\u003Ccode>server\u003C\u002Fcode>处理。例如，在\u003Ccode>192.168.1.1:80\u003C\u002Fcode>上收到一个\u003Ccode>www.example.com\u003C\u002Fcode>的请求，这个请求将由\u003Ccode>192.168.1.1:80\u003C\u002Fcode>上的默认\u003Ccode>server\u003C\u002Fcode>进行处理，也就是由第一个\u003Ccode>server\u003C\u002Fcode>进行处理，因为无法在这个（IP 地址和）端口上匹配到\u003Ccode>www.example.com\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Cp>前面已经说过，默认\u003Ccode>server\u003C\u002Fcode>是\u003Ccode>listen\u003C\u002Fcode>指令的一个参数，那么监听不同的（IP 地址和）端口就可以指定不同的默认\u003Ccode>server\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Cpre class=\"shiki\" style=\"background-color:#121212;color:#dbd7caee\" tabindex=\"0\">\u003Ccode>server {\n    listen      192.168.1.1:80;\n    server_name example.org www.example.org;\n    ...\n}\n\nserver {\n    listen      192.168.1.1:80 default_server;\n    server_name example.net www.example.net;\n    ...\n}\n\nserver {\n    listen      192.168.1.2:80 default_server;\n    server_name example.com www.example.com;\n    ...\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>一个简单的 PHP 站点配置\u003C\u002Fh2>\n\u003Cp>现在我们通过一个简单的 PHP 站点来看一下 Nginx 是如何处理\u003Ccode>location\u003C\u002Fcode>的：\u003C\u002Fp>\n\u003Cpre class=\"shiki\" style=\"background-color:#121212;color:#dbd7caee\" tabindex=\"0\">\u003Ccode>server {\n    listen      80;\n    server_name example.org www.example.org;\n    root        \u002Fdata\u002Fwww;\n\n    location \u002F {\n        index   index.html index.php;\n    }\n\n    location ~* \\.(gif|jpg|png)$ {\n        expires 30d;\n    }\n\n    location ~ \\.php$ {\n        fastcgi_pass  localhost:9000;\n        fastcgi_param SCRIPT_FILENAME\n                      $document_root$fastcgi_script_name;\n        include       fastcgi_params;\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Nginx 首先会通过遍历字符串的方式查找指向最具体的\u003Ccode>location\u003C\u002Fcode>前缀，这与配置顺便无关。在上面的楝文件中，唯一的前缀是\u003Ccode>\u002F\u003C\u002Fcode>，它可以匹配任意请求，将被最后使用。然后 Nginx 会按照配置书写的顺序进行正则表达式匹配。一旦匹配到第一条规则，则会停止后续查找，Nginx 将使用这个\u003Ccode>location\u003C\u002Fcode>。如果正则表达式匹配全部失败了，则 Nginx 会使用前面找到的前缀指向最具体的\u003Ccode>location\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Cp>值得注意的是，所有类型的\u003Ccode>location\u003C\u002Fcode>都只匹配请求的\u003Ccode>URI\u003C\u002Fcode>部分，不包括任何参数。这是因为请求的参数可能有很多种形式，例如：\u003C\u002Fp>\n\u003Cpre class=\"shiki\" style=\"background-color:#121212;color:#dbd7caee\" tabindex=\"0\">\u003Ccode>\u002Findex.php?user=john&amp;page=1\n\u002Findex.php?page=1&amp;user=john\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>此外，用户可以在参数中随便添加任何东西：\u003C\u002Fp>\n\u003Cpre class=\"shiki\" style=\"background-color:#121212;color:#dbd7caee\" tabindex=\"0\">\u003Ccode>\u002Findex.php?page=1&amp;something+else&amp;user=john\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>现在我们来看看，按上面的配置文件，一个请求将如何被处理：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>请求\u003Ccode>\u002Flogo.gif\u003C\u002Fcode>首先被前缀\u003Ccode>location\u003C\u002Fcode> \u003Ccode>\u002F\u003C\u002Fcode>匹配到，然后被正则表达式\u003Ccode>\\.(gif|jpg|png)$\u003C\u002Fcode>匹配到。这样的话，它将由后者进行处理。因为指定了\u003Ccode>root \u002Fdata\u002Fwww\u003C\u002Fcode>，因此这个请求将被映射到\u003Ccode>\u002Fdata\u002Fwww\u002Flogo.gif\u003C\u002Fcode>，这个文件将被发送到客户端。\u003C\u002Fli>\n\u003Cli>请求\u003Ccode>\u002Findex.php\u003C\u002Fcode>也被前缀\u003Ccode>location \u003C\u002Fcode> \u003Ccode>\u002F\u003C\u002Fcode>匹配到，然后被正则表达式\u003Ccode>\\.(php)$\u003C\u002Fcode>匹配到。这样的话，它将由后者进行处理。请求被转交到监听在\u003Ccode>localhost:9000\u003C\u002Fcode>的 FastCGI 服务进行处理。\u003Ccode>fastcgi_param\u003C\u002Fcode>指令会将 FastCGI 的参数\u003Ccode>SCRIPT_FILENAME\u003C\u002Fcode>设置为\u003Ccode>\u002Fdata\u002Fwww\u002Findex.php\u003C\u002Fcode>，然后 FastCGI 服务会执行这个文件。\u003Ccode>$document_root\u003C\u002Fcode>变量的值等于\u003Ccode>root\u003C\u002Fcode>指令的值，变量\u003Ccode>$fastcgi_script_name\u003C\u002Fcode>的值等于请求的 URI ，也就是\u003Ccode>\u002Findex.php\u003C\u002Fcode>。\u003C\u002Fli>\n\u003Cli>请求\u003Ccode>\u002Fabout.html\u003C\u002Fcode>只被前缀\u003Ccode>location\u003C\u002Fcode> \u003Ccode>\u002F\u003C\u002Fcode>匹配到，这样它将被在这个\u003Ccode>location\u003C\u002Fcode>中进行处理。因为指定了\u003Ccode>root \u002Fdata\u002Fwww\u003C\u002Fcode>，因此这个请求将被映射到\u003Ccode>\u002Fdata\u002Fwww\u002Fabout.html\u003C\u002Fcode>，这个文件将被发送到客户端。\u003C\u002Fli>\n\u003Cli>请求\u003Ccode>\u002F\u003C\u002Fcode>的处理更复杂一些。它只被前缀\u003Ccode>location\u003C\u002Fcode> \u003Ccode>\u002F\u003C\u002Fcode>匹配到，这样它将被在这个\u003Ccode>location\u003C\u002Fcode>中进行处理。接下来\u003Ccode>index\u003C\u002Fcode>指令将根据\u003Ccode>root \u002Fdata\u002Fwww\u003C\u002Fcode>指令的路径探测\u003Ccode>index\u003C\u002Fcode>参数中指定的文件是否存在。如果\u003Ccode>\u002Fdata\u002Fwww\u002Findex.html\u003C\u002Fcode>不存在，而\u003Ccode>\u002Fdata\u002Fwww\u002Findex.php\u003C\u002Fcode>存在，则该指令将内部跳转到\u003Ccode>\u002Findex.php\u003C\u002Fcode>，然后 Nginx 会像对待新请求一样重新匹配这个请求。如前文所述，这个请求将被 FastCGI 服务进行处理。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>编写：Igor Sysoev 编辑：Brian Mercer\u003C\u002Fp>\n\u003Cp>原文：\u003Ca href=\"http:\u002F\u002Fnginx.org\u002Fen\u002Fdocs\u002Fhttp\u002Frequest_processing.html\">http:\u002F\u002Fnginx.org\u002Fen\u002Fdocs\u002Fhttp\u002Frequest_processing.html\u003C\u002Fa>\u003C\u002Fp>\n","\u003Cp>Nginx首先需要确定由哪个\u003Ccode>server\u003C\u002Fcode>来处理请求。我们看一个简单的配置文件，在\u003Ccode>*:80\u003C\u002Fcode>端口上包含了三个虚拟主机（\u003Ccode>server\u003C\u002Fcode>）：\u003C\u002Fp>\n\u003Cpre class=\"shiki\" style=\"background-color:#121212;color:#dbd7caee\" tabindex=\"0\">\u003Ccode>server {\n    listen      80;\n    server_name example.org www.example.org;\n    ...\n}\n\nserver {\n    listen      80;\n    server_name example.net www.example.net;\n    ...\n}\n\nserver {\n    listen      80;\n    server_name example.com www.example.com;\n    ...\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>在这个配置下，Nginx只通过请求的\u003Ccode>Host\u003C\u002Fcode>头来决定路由到哪个\u003Ccode>server\u003C\u002Fcode>。如果\u003Ccode>Host\u003C\u002Fcode>头的值跟所有的\u003Ccode>server\u003C\u002Fcode>的\u003Ccode>server_name\u003C\u002Fcode>都不匹配，或者请求中没有包含这个阔大，Nginx会使用这个端口上的默认\u003Ccode>server\u003C\u002Fcode>。在这个配置文件中，默认\u003Ccode>server\u003C\u002Fcode>是指第一个，这正是Nginx标准的默认行为。默认\u003Ccode>server\u003C\u002Fcode>也可以通过配置显示指定，只需要在\u003Ccode>listen\u003C\u002Fcode>指令值中加上\u003Ccode>default_server\u003C\u002Fcode>参数即可。\u003C\u002Fp>\n","",{"total":22,"totalRoots":22,"comments":23,"pv":15},1,[24],{"id":25,"content":26,"user_id":27,"nick":28,"link":20,"date":29,"rid":15,"vote_up":15,"vote_down":15,"visible":30},154,"\u003Cp>多谢楼主翻译，文章非常明白。\u003C\u002Fp>",73,"大眉侠","2017-05-12 05:10:13",true]