将 nginx 配置为一个 web 服务器

编译自:
nginx-web-server

目录

  • 建立虚拟服务器
  • 配置 location
  • 使用变量
  • 返回指定的状态码
  • URI 重写
  • 对 HTTP Response 进行重写
  • 处理错误

这里有一个视频,有兴趣可以看看:nginx 安装与调优,其中讲述了以下主题:

  • nginx 怎样使你的应用能更快响应、具有更好的伸缩性、更快、更安全?
  • 如何安装 nginx
  • 为 nginx 调整操作系统参数

从高层次来说,配置 nginx 为一个 web 服务器,主要是定义:

  • 对哪些 URL 进行处理
  • 如何处理对于这些 URL 的 HTTP 请求

从较低层次来说,主要是定义一组 虚拟服务器 ,让这些 虚拟服务器 去处理对于 特定域名IP地址 的请求。

更多关于如何配置 nginx 的信息,可参考 Creating NGINX Plus Configuration Files

每个 虚拟服务器 都定义了 location 区块,location 区块定义了对于指定的一组 URI 是如何进行处理的。

在 location 中可作如下定义:

  • 转发请求到后端服务器(也就是将此 nginx 服务器作为代理),或者返回一个文件;
  • 对 URI 进行重写,将请求重定向至其它的 location 或者其它虚拟服务器;
  • 返回指定的 error code,并且可以为每个 error code 配置一个指定的 error page。

建立虚拟服务器


在 nginx 配置文件中,必须包含至少一个 server 指令,server 指令定义了一个虚拟服务器。当一个请求到来时,nginx 首先要决定选择哪一个虚拟服务器处理该请求。

server 指令在 http context 中进行定义,例如:

http {
    server {
        # Server configuration
    }
}

http context 中,可添加多个 server 指令以定义多个虚拟服务器。

server 区块中,一般会包含一个 listen 指令。listen 指令是用于定义该虚拟服务器监听于哪个 IP地址端口(或者定义一个 Unix socket 文件及路径)。IP地址可为 IPv4 或 IPv6 地址;IPv6 地址应放入方括号中。

以下是一个例子,该服务器监听于 127.0.0.1 和 8080 端口:

server {
    listen 127.0.0.1:8080;
    # The rest of server configuration
}

如果不指定端口地址,将使用标准的端口地址,即 80 端口。如果不指定 IP 地址,将监听于所有 IP 地址之上。如果 server 区块中没有包含 listen 指令,根据超级用户的权限,将使用 标准的 80/tcp 端口或者 默认的 8000/tcp 端口。

如果同时有多个 虚拟主机 匹配了请求的IP地址和端口,nginx 将测试请求的 Host 首部字段,将它与 server 区块中的 server_name 指令的配置进行对比。server_name 指令的值有三种:准确的主机名通配主机名正则表达式主机名。通配主机名在其起始、或末尾有一个 * 符号,或者起始和末尾都有一个 * 符号,* 可匹配任意长度的字符串;nginx 的正则表达式兼容 Perl 的正则语法,在正则表达式之前需有~前缀。

以下是关于 准确的主机名 的例子:

server {
    listen      80;
    server_name example.org www.example.org;
    ...
}

如果有多个 server_name 指定的主机名与请求首部的 Host 字段匹配,nginx 按如下的优先次序选择,当找到第一个能匹配的 server name 就终止查找:

  1. 准确的主机名
  2. 以 “*” 起始的最长的通配主机名,比如 *.example.org
  3. 以 “*” 结尾的最长的通配主机名,比如 mail.*
  4. 第一个匹配的正则表达式(按照配置文件中的顺序)

如果没有找到与 Host 字段匹配的 server name,nginx 将把请求路由到该端口地址的 默认的虚拟主机。 如果没有特别指定,默认的虚拟主机 就是 nginx.conf 文件中的第一个定义的 虚拟主机。也可以使用 listen 指令的 default_server 参数显式地指定一个虚拟主机为 默认的虚拟主机

server {
    listen      80 default_server;
    ...
}

配置 location


nginx 可基于请求的 URI,将访问流量转发给不同的后端服务器,或者将不同的文件提供给客户端。这个功能由 location 指令定义,location 指令在 server 区块中定义。

举个例子,你可以定义三个 location 区块,将一些请求转发给一个后端服务器,将另一些请求转发给另一个不同的后端服务器,余下的请求从本地的文件系统中提供客户端所请求的文件。

当一个请求交给了某个虚拟主机之后,nginx 将请求的 URI 与该虚拟主机内所有的 location 指令的定义进行对比,然后将请求映射到匹配的 location 之中。

在每个 location 区块中,一般可以放置更多的 location 指令(有少数例外),用于进一步对请求进行分组处理。

Note: 在本文中,location 这个词只表示一个单独的 location 上下文。

location 指令可定义两种参数:

  • 前缀字符串(路径名)
  • 正则表达式

如果一个请求的 URI 以某个 前缀字符串 为起始,则与该 location 匹配。

下面举一个 前缀字符串 的例子,在下面的例子中,该 location 的参数为 /some/path/ ,它能匹配类似于 URI 为 /some/path/document.html 的请求:

location /some/path/ {
    ...
}

正则表达式必须带有前缀 ~~*(忽略大小写)。在下面的例子中,该 location 可匹配包含 .html 或者 .htm 的请求:

location ~ \.html? {
    ...
}

为了找到某个 URI 的最佳匹配,nginx 首先比较 URI 和 前缀字符串,然后比较 URI 和 正则表达式。

正则表达式拥有更高的优先级,除非正则表达式使用了 ^~ 修饰符。

在所有匹配的 前缀字符串 中,nginx 选择其中最长及最完整的前缀字符串所对应的 location。

下面是准确的查找 loction 的过程:

  1. 将 URI 与所有的 前缀字符串 进行比较
  2. 修饰符 = 表示 URI 与 前缀字符串 必须精确匹配。如果能够精确匹配,则结束查找
  3. 如果在匹配的最长的 前缀字符串 前添加了 ^~ 修饰符,则不会再检查正则表达式
  4. 保存匹配的最长的 前缀字符串
  5. 测试 URI 和 正则表达式的匹配情况
  6. 一旦找到匹配的正则表达式,则终止查找,并使用对应的 location
  7. 如果没有匹配的正则表达式,使用保存匹配的最长的 前缀字符串

如果对于 / 的请求很频繁,可为 location / 添加 = 修饰符,这样可以加速处理过程,因为一次匹配查找即可结束:

location = / {
    ...
}

在 location 区块中,可匹配将请求转发给后端服务器。在下面的例子中,对于匹配第二个 location 的请求,将被转发给 www.example.com

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

    location / {
        proxy_pass http://www.example.com;
    }
}

root 指令指定了提供静态文件的本地路径。如果请求的 URI 匹配第一个 location,将 URI 追加到 /data 之后即可得到文件的访问路径:/data/URI。比如请求的 URI 为 /images/example.png,nginx 将 /data/images/example.png 文件返回给客户端。

proxy_pass 指令用于将请求转发给后端服务器,参数值即为后端服务器的访问路径 URL。从后端服务器取回响应之后,nginx 再转发给客户端。在上面的例子中,所有不以 /images/ 起始的 URI,对应的请求将被转发给后端服务器。

使用变量


nginx.conf 中可使用变量。变量的值在运行时进行计算,可作为指令的参数使用。引用变量,使用 $ 符号,比如 $remote_addr。变量的定义信息依赖于 nginx 当前的状态,比如当前被处理的请求的属性等。

有许多变量是预定义的,比如 core HTTP 定义的变量;另外你也可以自定义变量,使用 setmap,或 geo 指令进行自定义。

大多数变量的值是在运行时计算出来的,其中包含特定请求的信息。比如 $remote_addr 包含客户端的 IP地址 信息,而 $uri 包含当前的 URI 值。

返回指定的状态码


在有些情况下,比如当一个页面已经被临时或永久地移动到其他位置,访问这种 web 站点的 URI 时需要立即返回一个错误码或者重定向码。最容易的方法是使用 return 指令:

location /wrong/url {
    return 404;
}

return 指令的第一个参数是响应状态码。第二参数是可选的,可以是一个重定向的 URL(for codes 301, 302, 303, and 307),或者是放入响应 body 的一段文本。比如:

location /permanently/moved/url {
    return 301 http://www.example.com/moved/here;
}

return 指令可放入 location 和 server 上下文中。

URI 重写


在处理请求的过程中,使用 rewrite 指令可对 URI 进行多次修改。

rewrite 指令的语法为:

rewrite regex replacement [flag]

regex 是用于匹配 URI 的正则表达式,replacement 参数用于替换匹配的 URI。flag 参数是可选的,可用于终止进一步的 rewrite 指令操作,或者发送重定向(301 或 302)给客户端,例如:

location /users/ {
    rewrite ^/users/(.*)$ /show?user=$1 break;
}

server 上下文和 location 上下文中,都可包含多个 rewrite 指令。nginx 按照 rewrite 指令出现的顺序依次进行处理。当一个 server 上下文被选择用于处理请求时,在 server 上下文中的 rewirte 指令被执行一次。

当 nginx 对一组 rewrite 指令进行处理之后,它根据新的 URI 选择匹配的 location 上下文。如果在选择的 location 中也包含 rewrite 指令,它们将被依次执行。如果 URI 匹配其中任意一个,当所有定义的 rewrite 指令被处理之后,开始对新的 URI 进行查找。

如下例子中,rewrite 指令和 return 指令联合使用:

server {
    ...
    rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last;
    rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra  last;
    return  403;
    ...
}

上例中,对两组 URI 进行了区分。

  • 类似于 /download/some/media/file 的 URI 请求被替换为 /download/some/mp3/file.mp3。因为该 rewrite 指令添加了 last flag,它将跳过后面的指令(第二个 rewrite 指令和 return 指令)。然后 nginx 开始处理新的 URI。

  • 类似于 /download/some/audio/file 的 URI 请求被替换为 /download/some/mp3/file.ra。因为该 rewrite 指令添加了 last flag,它将跳过后面的指令(return 指令)。

  • 如果 URI 不匹配 location 中的任何一个 rewrite 指令,nginx 返回 403 状态码给客户端。

rewrite 指令有两个参数可以终止 rewrite 指令集的处理:

  • last - 停止当前 server 或 location 上下文中的 rewrite 指令集的执行,nginx 对修改后的 URI 进行匹配查找。如果新匹配的 location 中包含 rewrite 指令,URI 有可能被再次修改。

  • break - 如同 break 指令,终止当前上下文中的 rewrite 指令集的执行,nginx 对修改后的 URI 进行匹配查找,在新匹配的 location 中如果包含 rewrite 指令,它们不会被执行。

对 HTTP Response 进行重写


有时你需要对 HTTP 响应报文的内容进行重写或修改。你可以使用 sub_filter 指令来定义重写操作。该指令支持变量和链式替换,可作复杂的修改:

举个例子,你可以对指向一个服务器的绝对链接进行修改:

location / {
    sub_filter      /blog/ /blog-staging/;
    sub_filter_once off;
}

另一个例子,修改 http://https:// ,并且将 localhost 地址修改为请求首部中的 host 字段的值。sub_filter_once 指令告诉 nginx 是否对所有匹配的字符串重复地执行替换操作,还是只在第一次遇到匹配的字符串时,执行一次替换操作(只替换一次)。sub_filter_once 默认为 on,表示只在第一次遇到匹配的字符串时,替换一次:

location / {
    sub_filter     'href="http://127.0.0.1:8080/'    'href="https://$host/';
    sub_filter     'img src="http://127.0.0.1:8080/' 'img src="https://$host/';
    sub_filter_once on;
}

注意,如果响应报文的一部分已经被一个 sub_filter 指令所修改,当另一个 sub_filter 也匹配该部分时,不会再进行修改。

处理错误


使用 error_page 指令,你可以配置 nginx :

  • 返回一个 error code 以及一个自定制的页面;
  • 在响应报文中,替换一个不同的 error code
  • 发送重定向指令给浏览器,指向不同的 URI

在下面的例子中,error_page 指令指定了 /404.html 页面,这个页面在返回 404 code 时会一并返回给客户端:

error_page 404 /404.html;

要注意的是,这个指令不是立即返回该错误给客户端(这是 return 指令做的事),这只是定义了如果处理该错误。该 error code 可来自于一个后端服务器,或者在 nginx 做一些处理时出现(例如,当 nginx 不能找到请求的文件时,将返回 404 错误)。

下面的例子中,当 nginx 找不到请求的页面时,它将 code 404 替换为了 code 301,并将返回一个重定向给客户端:http:/example.com/new/path.html

这个配置是很有用的,当客户端尝试以一个老旧的 URI 请求页面时,可返回重定向指令,令其访问在新路径下的文件。

code 301 告诉浏览器,该页面已经被永久地转移到了其他地方,它需要将返回的新的地址替换原来的老的地址:

location /old/path.html {
    error_page 404 =301 http:/example.com/new/path.html;
}

下面的例子中,当请求 /images/ 路径下的文件发生文件未找到的错误时,通过内部重定向转发到后端服务器。因为在 error_page 指令中,在 = 后面没有定义替换的状态码,所以返回给客户端的状态码由后端服务器指定(不一定是 404):

server {
    ...
    location /images/ {
        # Set the root directory to search for the file
        root /data/www;

        # Disable logging of errors related to file existence
        open_file_cache_errors off;

        # Make an internal redirect if the file is not found
        error_page 404 = /fetch$uri;
    }

    location /fetch/ {
        proxy_pass http://backend/;
    }
}

error_page 指令告诉 nginx ,当发生 file not found 错误时,做一个内部的重定向。$uri 变量的值为当前请求的 URI,在重定向中被转发给了后端服务器。

例如,如果 /images/some/file 未找到,它被替换为 /fetch/images/some/file,对这个新 URI,nginx 会查找与其匹配的 location,这里就是 location /fetch/,然后就被转发给后端服务器了。
(可参考 反向代理

当发送 file not found 错误时,open_file_cache_errors 指令在这里用于防止记录错误日志。因为请求已经被转发给后端,所以不应该记录错误日志。


版权信息
*本文编译自 nginx.com

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容