常用的nginx配置

前言

虽然说作为一个前端攻城师一般情况下是不会担心nginx的相关配置的,如果有运维同学的话,一般是找他们帮忙搞定这些事情的。但是小一点的公司的话可能就根本没有运维了,这时候我们就需要“自己动手,丰衣足食”了!

nginx简介

Nginx(发音同engine x)是一款由俄罗斯程序员Igor Sysoev所开发轻量级的网页服务器、反向代理服务器以及电子邮件(IMAP/POP3)代理服务器。由于其各种优势各种好(自行了解),所以很热很火🔥,基本上大大小小的公司都在使用了。

以前的前端可能不会涉及到这方面,但是由于node.js火爆之后,前端以及向所谓的“全栈”方向转移了,然后基于node.js的各种工程、工具也是越来越多。这时候前端需要的越来越多了,而nginx似乎成为了“刚需”(对自己而言),虽然不需要深入的了解nginx什么原理啊什么机制啊,但是最基础的还是需要的,最起码最起码会做一些基本的配置,满足自己的日常需要。

基础配置

首先,最基础的莫过于配置一个server,然后指向自己项目的根目录,以便能通过域名或者ip访问。下边看一个最基础版本的开发环境配置:

# nginx.conf
http {
	include       mime.types;
	default_type  application/octet-stream;

	log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
					  '$status $body_bytes_sent "$http_referer" '
					  '"$http_user_agent" "$http_x_forwarded_for"';

	access_log  logs/access.log  main;

	sendfile        on;
	#tcp_nopush     on;

	keepalive_timeout  65;
		
	gzip  on;
	autoindex on; #文件列表
	autoindex_exact_size off;
	autoindex_localtime on;
		
	# 开启ssi支持,默认是off
	ssi on;
	# 默认值是off,开启后在处理SSI文件出错时不输出错误提示:”[an error occurred while processing the directive] ”
	ssi_silent_errors on;
	# 默认是text/html,如果需要shtml支持,则需要设置:ssi_types text/shtml
	ssi_types text/shtml;

	expires -1;
	if_modified_since off;
	add_header Last-Modified "";
	add_header Etag "";

	add_header Access-Control-Allow-Origin *;# 允许任何域
	add_header Access-Control-Allow-Headers X-Requested-With;
	add_header Access-Control-Allow-Methods GET,POST,OPTIONS;

	server {
		listen       80;
		server_name  localhost;

		#access_log  logs/host.access.log  main;

		location / {
			root   /git/;
			index  index.html index.htm;
		}

		#error_page  404              /404.html;

		# redirect server error pages to the static page /50x.html
		#
		error_page   500 502 503 504  /50x.html;
		location = /50x.html {
			root   html;
		}
	}

	server {
		listen 80;
		server_name fed.com;
		location / {
			root   /git/fed/;
		}
	}
}

上边其实就是nginx作为基本的文件服务的基本配置,意思就是启动了两个server,一个是localhost一个fed.com,记得需要在本机hosts中加入127.0.0.1 fed.com;然后在浏览器中访问localhost的话,显示的内容就是本机的/git/目录下的内容,而访问fed.com的话则会显示本机/git/fed/下的内容。

nginx是支持include语法的,所以在一般场景下,我们都会针对于不同的server_name创建不同的.conf文件,一般情况下是在nginx配置目录下新建一个子文件夹conf.d,然后只需要在http配置中的最后加上include /usr/local/etc/nginx/conf.d/*.conf;就OK了,也就是:

http {
	include /path/to/nginx/conf.d/*.conf;
}

然后上边的fed.com就可以单独拿出来了,可以把它命名为fed.com.conf,然后再这个文件中的内容就是上边配置信息:

server {
	listen 80;
	server_name fed.com;
	location / {
		root   /git/fed/;
	}
}

有好多时候开发环境是需要反向代理的,这个通过nginx是很容易就做到的,如下:

server {
	listen 80;
	server_name dev.example.com dev2.example.com; #配置多个name
	
	location / {
		proxy_pass http://127.0.0.1:3000;
		proxy_redirect     off;
		proxy_set_header   Host             $host;
		# 下边俩个配置的介绍
		# http://gong1208.iteye.com/blog/1559835
		# http://bbs.linuxtone.org/thread-9050-1-1.html
		proxy_set_header   X-Real-IP        $remote_addr;
		proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
		client_max_body_size       10m;
	}
}

在浏览器中访问dev.example.com或者dev2.example.com可以看到其实是代理到了本机监听3000端口的服务上去了。

负载均衡

如果网站流量相对比较大的时候,一台server已经满足不了需求了,一般情况下我们可能需要加上负载均衡了,nginx中同样也提供了方法:

upstream back-server {
	# PS: 地址瞎编的
	server 123.56.67.110:8000 weight 2;
	server 123.56.67.111:8000 weight=1 max_fails=2 fail_timeout=30s ;
	server 123.56.67.112:8090 backup;
}
server {
	listen 80;
	location / {
		proxy_pass http://back-server;
		proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
	}
}

简单说下上边配置的意思,nginx默认支持的调度算法是轮询(自带的还有一个ip_hash调度算法,按照访问ip的hash结果分配):

每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。Weight 指定轮询权值,Weight值越大,分配到的访问机率越高,主要用于后端每个服务器性能不均的情况下。

除去weight的值,其他字段意思分别是:

  • max_fails: 允许请求失败的次数,默认为1;超过后返回proxy_next_upstream模块定义的错误。

  • fail_timeout: 有两层含义,一是在指定时间内最多可以有max_fails次失败;还有就是在max_fails次失败后,在该指定时间内不分配请求到这台server。

  • backup: 预留机器,当其他机器都不正常的时候就会访问到这台server了。

  • proxy_next_upstream: 该指令属于http_proxy模块,指定后端返回什么样的异常响应时,使用其他server

场景记

这里再简要介绍下一些场景下的配置。

支持HTML5的pushstate

这种场景很简单,就是单页面的场景,但是又希望使用的不是hash来改变浏览器地址,而是通过HTML5的pushstate来改变地址;如果此时我们后端没有server只是静态html文件的话,一旦刷新就会404了。

在nginx中,我们通过rewrite指令来达到我们的目的:

server {
	server_name myserver.com;
	location / {
		root /git/myproject/;
		rewrite ^/(.*)$ /index.html break;
	}
}

如果说我们不希望所有的地址都rewrite到index.html,例如在项目中还有js、css等目录的时候,此时需要的就是返回对应的js、css等文件了。那么只需要稍加修改就可以了:

server {
	server_name myserver.com;
	location / {
		root /git/myproject/;
		rewrite ^/(?!js|css).*$ /index.html break;
	}
}

rewrite的语法就是:

rewrite regex replacement [flag];

可以看到后边紧跟的分别是:一个正则、符合正则的替换为的字符串值、最后是一个可省略的flag标记。

flag可以有如下值:

  • last: 相当于Apache的[L]标记,表示完成rewrite,会停止rewrite检测,但是跟break有本质的不同,last的语句不一定是最终结果

  • break: 本条规则匹配完成后,终止匹配,不再匹配后面的规则,会停止rewrite检测,也就是说当含有break flag的rewrite语句被执行时,该语句就是rewrite的最终结果

  • redirect: 返回302临时重定向,浏览器地址会显示为跳转后的URL

  • permanent: 返回301永久重定向,浏览器地址会显示为跳转后的URL

这里比较难理解的是lastrewrite的区别了,这里只需要简单记住:last一般写在server和if中,而break则一般写在location中就可以了。

某个path下的访问的内容需要重新定义

这里的重新定义一般包含两种情况:

  1. 访问到其他目录(文件服务)

  2. 访问其他server(一般反向代理服务)

访问到其他目录

假设访问static路径下的内容的时候,其实是希望从另一目录得到的,那么此时就需要重新配置了:

server {
	listen 80;
	server_name fed.com;
	location / {
		root   /git/fed/;
	}

	location /static/ {
		root   /git/static/;
		break;
	}
}

上边的配置的结果就是访问fed.com/static/*的内容的时候其实返回的是目录/git/static/static/*的内容。

注意上边的结果是/git/static/static/*,而不是/git/static/*,这是很值得注意的一点。其实这也就是涉及到了rootalias指令的区别了,如果改成下边的:

server {
	listen 80;
	server_name fed.com;
	location / {
		root   /git/fed/;
	}

	location /static/ {
		alias   /git/static/;
		break;
	}
}

那么访问fed.com/static/*下的内容的话就是返回的/git/static/*的内容了。

访问其他server

例如访问/api/下的就统一发送到后端的接口服务上去的话,可以这样配置:

server {
	listen 80;
	server_name fed.com;
	location / {
		root   /git/fed/;
	}

	location /api {
		proxy_pass http://server.com;
		proxy_set_header Host $host;
	}
}

这样访问/api/*的话就会代理到http://server.com服务上了。

ajax跨域请求直接得到本地fake数据

server {
	listen 80;
	server_name fed.com;

	add_header Access-Control-Allow-Origin *;# 允许任何域
	add_header Access-Control-Allow-Headers X-Requested-With;
	add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
	
	location / {
		root   /git/fed/;
		if ($request_method = OPTIONS) {
			return 204;
		}
	}
}

注意上边除了增加了跨站资源共享的响应头之外,还判断了如果是OPTIONS请求的话直接返回204了。如果没有的话,浏览器遇到跨域的时候首先会发一个options请求来检测支不支持跨域以及允许哪些请求方法跨域,这时由于直接使用nginx,没有启用node之类的服务,所以请求过来后nginx发现没有他自己能返回的东西,最后这个options请求就会响应为405了。

当然网上还有一种方案,就是对所有的405错误进行处理:

server {
	listen 80;
	server_name fed.com;

	add_header Access-Control-Allow-Origin *;# 允许任何域
	add_header Access-Control-Allow-Headers X-Requested-With;
	add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
	
	location / {
		root   /git/fed/;
		error_page 405 =200 $uri;
	}
}

参考:

  1. http://nginx.org/en/docs/
  2. https://zh.wikipedia.org/wiki/Nginx
  3. http://tengine.taobao.org/book/index.html
  4. http://iqbon.iteye.com/blog/1882319
  5. http://segmentfault.com/a/1190000002873747
  6. http://gong1208.iteye.com/blog/1559835
  7. http://bbs.linuxtone.org/thread-9050-1-1.html
  8. http://segmentfault.com/a/1190000002873747
  9. http://saiyaren.iteye.com/blog/1914865
  10. http://readystate4.com/2012/05/17/nginx-and-apache-rewrite-to-support-html5-pushstate/
  11. «http://segmentfault.com/a/1190000002797606»
  12. http://blog.cafeneko.info/2010/10/nginx_rewrite_note/

发布于: 2015年 09月 06日