nginx 的用法¶
统计信息:字数 19577 阅读40分钟
简介¶
Nginx(发音/engine x/)是一个轻量级的开源 HTTP 服务器软件,可以用来取代 Apache,或者与 Apache 配合使用。
它与 Apache 的区别在于:它的设计基于事件和异步操作,而 Apache 完全依赖线程。当访问量很大的时候,大量新增的线程 很快会耗尽内存,而基于事件的非阻塞设计能够轻松处理。
Nginx 的安装。
$ sudo apt-get install nginx
重启 nginx。
$ sudo service nginx restart
# 或者
$ nginx -s reload
nginx 有一个主进程和几个 worker 进程。有多少个 CPU,就有多少个 worker 进程。每一个 worker 进程能够处理几千个连接。
$ ps -ef --forest | grep nginx
上面的命令可以查看所有 nginx 进程,一般是 1 个主进程,与 CPU 内核数对应的 worker 进程,1 个 cache manager 进程,1 个 cache loader 进程。
nginx 进程分工如下。
- 主进程:读取配置,绑定端口,创造子进程。
- worker 进程:执行实际任务的进程,包括处理网络请求,读取或写入硬盘,与上游服务器通信。
- cache loader 进程:将硬盘上的缓存读入内存,然后退出。
- cache manager 进程:周期性执行,从缓存清除过时的条目。
worker 进程在创建时,根据配置文件初始化,并且留有多个 socket 用于与主进程通信。每当有新的网络请求,worker 进程就会监听到事件,从而启动。
Web 根目录¶
新建一个目录作为 Web 服务的根目录。
$ sudo mkdir -p /var/www/example.com/public_html
$ sudo chown -R www-data:www-data /var/www/example.com/public_html
$ sudo chmod 755 /var/www
然后,可以在该目录下写入一个首页文件index.html
。
$ sudo nano /var/www/example.com/public_html/index.html
配置文件¶
将默认的配置文件,拷贝成一份指定站点的配置文件。
$ sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/example.com
编辑该文件。
$ sudo nano /etc/nginx/sites-available/example.com
下面是一份简单的配置。
server {
listen 80; ## listen for ipv4; this line is default and implied
#listen [::]:80 default ipv6only=on; ## listen for ipv6
root /var/www/example.com/public_html;
index index.html index.htm;
# Make site accessible from http://localhost/
server_name example.com;
}
最后,激活这个配置文件。
$ sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
然后,重启 nginx。
$ sudo service nginx restart
内置模块¶
nginx 采用模块式设计,通过加载模块扩展功能。目前,nginx 不支持动态加载模块,模块必须和内核一起编译。所有的模块都是中间件,即接收上一个模块的输入,处理后再将输出传给下一个模块。
下面是一些默认编译的模块。
- Access (ngx_http_access_module):限制访问某些 IP 地址。
location / {
deny 192.168.1.1;
allow 192.168.1.0/24;
allow 10.1.1.0/16;
allow 2001:0db8::/32;
deny all;
}
- HTTP Auth (ngx_http_auth_basic_module):启用 HTTP Basic Auth。
location / {
auth_basic "password";
auth_basic_user_file conf/htpasswd;
}
- Subrequest Auth (ngx_http_auth_request_module):客户端认证基于一个子请求的结果。
- Limit connections (ngx_http_limit_conn_module):允许设置单个 IP 地址的同一时间的最大连接数(connection)。
- Limit requests (ngx_http_limit_req_module):允许设置单个 IP 地址的最大请求数。
外部模块¶
外部模块需要在编译时指定。
$ ./configure --add-module=/path/to/module/source
# 或者不安装某些模块
$ ./configure --without-http_dav_module --withouthttp_spdy_module
下面是一些有用的外部模块。
- ngx_pagespeed:Google 的 PageSpeed 项目的一个产品,用于提高网页加载速度。
- nginx-rtmp-module:实时流(stream)操作的模块。
- nginx-push-stream-module:基于流操作的一个推送(push)模块,支持 EventSource 长轮询(Long polling),一个例子就是 WebSocket。
使用方法¶
# 启动
$ /etc/init.d/nginx start
# 停止
$ /etc/init.d/nginx stop
# 重启
$ /etc/init.d/nginx reload
# 验证配置文件
$ nginx -t
配置¶
server 区块¶
基本配置。
server {
listen 80;
server_name tecmintlovesnginx.com www.tecmintlovesnginx.com;
access_log /var/www/logs/tecmintlovesnginx.access.log;
error_log /var/www/logs/tecmintlovesnginx.error.log error;
root /var/www/tecmintlovesnginx.com/public_html;
index index.html index.htm;
}
(1)server_tokens
server_tokens 指定发生错误时,是否显示 nginx 错误信息。
server {
listen 192.168.0.25:80;
Server_tokens off;
server_name tecmintlovesnginx.com www.tecmintlovesnginx.com;
access_log /var/www/logs/tecmintlovesnginx.access.log;
error_log /var/www/logs/tecmintlovesnginx.error.log error;
root /var/www/tecmintlovesnginx.com/public_html;
index index.html index.htm;
}
(2)屏蔽指定的 HTTP 动词
在 server 区块内部,指定可以接受的 HTTP 动词。
if ($request_method !~ ^(GET|HEAD|POST)$) {
return 444;
}
然后,下面的命令就会返回 444 错误。
$ curl -X DELETE http://192.168.0.25/index.html
(3)指定 SSL 版本
server {
listen *:443;
ssl on;
server_name "";
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
ssl_client_certificate /etc/nginx/certs/ca.crt;
ssl_verify_client on;
}
(4)指定 SSL 证书
首先,删除或注释掉下面指定监听 80 端口的两行。
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
改成下面的样子。
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
还可以加上三行附加设置。
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers AES256+EECDH:AES256+EDH:!aNULL;
然后,在原来的server
区块外面,再加一个server
区块。
server {
listen 80;
server_name example.com;
rewrite ^/(.*) https://example.com/$1 permanent;
}
下面是另一个例子。
server {
listen 192.168.0.25:443 ssl;
server_tokens off;
server_name tecmintlovesnginx.com www.tecmintlovesnginx.com;
root /var/www/tecmintlovesnginx.com/public_html;
ssl_certificate /etc/nginx/sites-enabled/certs/tecmintlovesnginx.crt;
ssl_certificate_key /etc/nginx/sites-enabled/certs/tecmintlovesnginx.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
}
最后,重启nginx
。
$ sudo service nginx restart
生成证书的命令。
# openssl genrsa -aes256 -out tecmintlovesnginx.key 1024
# openssl req -new -key tecmintlovesnginx.key -out tecmintlovesnginx.csr
# cp tecmintlovesnginx.key tecmintlovesnginx.key.org
# openssl rsa -in tecmintlovesnginx.key.org -out tecmintlovesnginx.key
# openssl x509 -req -days 365 -in tecmintlovesnginx.csr -signkey tecmintlovesnginx.key -out tecmintlovesnginx.crt
(5)HTTP 导向 HTTPS
server {
return 301 https://$server_name$request_uri;
}
(6)反向代理,转发请求
http {
server {
location /app/ {
proxy_pass http://app/;
proxy_set_header X-ClientCert-DN $ssl_client_s_dn;
}
}
}
上面代码中,用户对/app
路径的访问请求,会被转发到后端服务。这时,nginx 会增加一个 HTTP 信息头X-ClientCert-DN
,它的值就是 nginx 变量$ssl_client_s_dn
,这个值从客户端证书的 Common Name 部分获取。
另一个例子。
location / {
proxy_pass http://localhost:15301/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
User Agent 限制¶
新建一个文件/etc/nginx/blockuseragents.rules
,定义屏蔽 User Agent 的规则。
map $http_user_agent $blockedagent {
default 0;
~*malicious 1;
~*bot 1;
~*backdoor 1;
~*crawler 1;
~*bandit 1;
}
然后,在 server 区块之前包含这个文件。
include /etc/nginx/blockuseragents.rules;
接着,在 server 区块里面启用屏蔽功能。
server {
if($blockedagent) {
return 403;
}
}
重新启动以后,使用被屏蔽的 user agent 访问,就会返回 403 错误。
$ wget --user-agent "I am a bandit haha" http://192.168.0.25/index.html
限制访问¶
全局拦截。
$ vi /etc/nginx/sites-enabled/blockips.conf
写入所要拦截的 IP。
deny 123.123.123.123;
然后,重启 nginx。
sudo /etc/init.d/nginx restart
blockips.conf
里面,如果想要拦掉某个网段,可以写成下面这样。
deny 123.123.123.0/24;
如果只允许某个网段,可以写成下面这样。
allow 1.2.3.4/24;
deny all;
nginx 配置文件里面限制 IP 段。
location / {
allow 192.168.1.1/24;
allow 127.0.0.1;
deny 192.168.1.2;
deny all;
}
启用访问认证。
server {
...
auth_basic "closed website";
auth_basic_user_file conf/htpasswd;
}
全站启用访问认证,某些路径允许自由访问。
server {
...
auth_basic "closed website";
auth_basic_user_file conf/htpasswd;
location /public/ {
auth_basic off;
}
}
认证和 IP 地址之中,只要满足一个条件即可。
location / {
satisfy any;
allow 192.168.1.0/24;
deny all;
auth_basic "closed site";
auth_basic_user_file conf/htpasswd;
}
限制单个 IP 地址的带宽,以及能够建立的连接数。
location /download/ {
limit_conn addr 1;
limit_rate 50k;
}
SSL 支持¶
下面的命令用来生成自签名证书,生成的证书为/etc/nginx/ssl/ssl.crt
。
$ sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/ssl.key -out /etc/nginx/ssl/ssl.crt
将数字证书放在/etc/nginx/certificates/目录。
修改配置文件,打开 HTTPs 端口。
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
...
}
重启 nginx 服务。
修改配置文件,将 HTTP 流量导向 HTTPs。
server {
listen 80;
server_name www.example.com;
rewrite ^(.*) https://$host$1 permanent;
}
增加 ssl session 的缓存时间。
worker_processes auto;
http {
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
server {
listen 443 ssl;
server_name www.example.com;
keepalive_timeout 70;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
...
同一台服务器同时处理 HTTP 和 HTTPs 请求。
server {
listen 80;
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
...
}
同一张证书支持多个域名(比如,同时支持www.example.com和www.example.org)。
ssl_certificate common.crt;
ssl_certificate_key common.key;
server {
listen 443 ssl;
server_name www.example.com;
...
}
server {
listen 443 ssl;
server_name www.example.org;
...
}
如果浏览器支持 TLS Server Name Indication 扩展,就可以一台服务器支持多个证书。
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
...
}
server {
listen 443 ssl;
server_name www.example.org;
ssl_certificate www.example.org.crt;
...
}
检查 nginx 是否支持 SNI 扩展。
$ NGINX -V
...
TLS SNI support enabled
...
均衡负载¶
http {
upstream cluster {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
server 127.0.0.1:3003;
}
server {
listen 80;
server_name www.domain.com;
location / {
proxy_pass http://cluster;
}
}
}
参考链接¶
- nginx Document, Restricting Access
- fcambus, Nginx Resources
- Nishant Modak, Nginx Guide: Essentials
- H5BP, Server Configs Nginx
配置¶
Nginx 的主配置文件是nginx.conf
,通常位于/usr/local/etc/nginx
或者/etc/nginx
。官方网站提供范例配置nginx.conf.default)可以查看。
虚拟主机的默认配置文件是/etc/nginx/sites-available/default
。一般的做法是,在sites-available
目录里面,根据每个站点的名字,新建配置文件,比如/etc/nginx/sites-available/example.com.conf
。
虚拟主机的配置文件通常带有一个或多个server
区块。
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /usr/share/nginx/html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}
修改配置后,可以使用下面的命令重新加载配置。
$ nginx –s reload
除了nginx.conf
,还有sites-available
和sites-enabled
两个目录,前者包括所有可用的网站配置,后者只包括前者的符号链接,指向那些已经激活的网站。
sites-available
目录里面的配置文件,加上 660 权限。
# chmod 660 /etc/nginx/sites-available/example.com.conf
修改它们所属的用户组。
# Debian and Derivatives
$ chgrp www-data /etc/nginx/sites-available/tecmintlovesnginx.com.conf
# CentOS and RHEL
$ chgrp nginx /etc/nginx/sites-available/tecmintlovesnginx.com.conf
另外,需要修改日志目录的用户组,与 nginx 属于同一个用户组。
# mkdir /var/www/logs
# chmod -R 660 /var/www/logs
# chgrp <nginx user> /var/www/logs
然后,建立sites-enabled
目录到site-available
的符号链接。
# ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/example.com.conf
接着,在/var/www/<domain name>/public_html
目录里面,放置一个index.html
。
最后,测试配置,并重启 nginx。
# nginx -t && systemctl start nginx
修改/etc/hosts
,增加域名解析。
192.168.0.25 example.com
sites-enabled
下的文件自动加载。激活新网站的做法是,符号链接sites-enabled
目录的配置文件,然后重启 nginx。
$ cd /etc/nginx/sites-available
# ... create newproject.com ...
$ cd /etc/nginx/sites-enabled
$ ln -s ../sites-available/newproject.com .
$ /etc/init.d/nginx reload
[....] Reloading nginx configuration: nginx.
$
停用某个网站的做法是从 sites-enabled 目录移除符号链接,重启 nginx。
$ cd /etc/nginx/sites-enabled
$ rm newproject.com
$ /etc/init.d/nginx reload
[....] Reloading nginx configuration: nginx.
$
负载均衡¶
Nginx 可以用作负载均衡。
upstream backend {
server backend1.example.com;
server backend2.example.com;
server backend3.example.com;
}
上面代码会将流量分配到三台服务器。具体的分配规则,由 Nginx 自动完成。
Nginx 允许调整不同服务器的权重。
upstream backend {
server backend1.example.com weight=1;
server backend2.example.com weight=2;
server backend3.example.com weight=4;
}
如果有一台服务器有连接失败的可能,可以在配置中指明。
upstream backend {
server backend1.example.com max_fails=3 fail_timeout=15s;
server backend2.example.com weight=2;
server backend3.example.com weight=4;
}
上面代码指定,连续三次失败或 15 秒超时,Nginx 会不再给这台服务器分配流量。
如果某台服务器下线,可以手动指定。
upstream backend {
server backend1.example.com weight=1;
server backend2.example.com weight=2;
server backend3.example.com down;
}
最后,建立一个到backend
的代理。
server {
location / {
proxy_pass http://backend;
}
}
反向代理¶
编辑/etc/nginx/sites-available/default
,或者在该目录下新建一个配置文件/etc/nginx/sites-available/my-site
。
下面是简单的配置。
server {
listen 80;
server_name my.domain.com;
location / {
proxy_pass http://localhost:3000;
}
}
下面是复杂的配置。
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://APP_PRIVATE_IP_ADDRESS:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
上面的字段含义如下。
- server_name:你的域名或 IP 地址
- proxy_pass:私有 IP 地址
可以增加location
区块,为不同的路径代理不同的后端地址。
location /app2 {
proxy_pass http://APP_PRIVATE_IP_ADDRESS:8081;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
重定向¶
Nginx 的重定向命令较为简单。
server {
listen 80;
server_name domain1.com;
return 301 $scheme://domain2.com$request_uri;
}
上面的代码将所有请求,301 重定向到另一个域名。
如果只要重定向某个目录的请求,可以像下面这样写。
# 301 重定向
rewrite ^/images/(.*)$ http://images.example.com/$1 redirect;
# 302 重定向
rewrite ^/images/(.*)$ http://images.example.com/$1 permanent;
TLS 设置¶
启用 HTTP/2¶
打开配置文件(比如/etc/nginx/sites-enabled/sitename
),将下面的一行改掉。
listen 443 ssl;
改成
listen 443 ssl http2;
改完以后,可以使用下面的命令验证。
$ curl --http2 -I https://domain.com/
弃用 TLS 1.0¶
如果要放弃使用 TLS 协议 1.0 版本,就要把下面一行改掉。
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
改成
ssl_protocols TLSv1.1 TLSv1.2;
参考链接¶
- Hayden James, Nginx tuning tips: TLS/SSL HTTPS – Improved TTFB/latency