Nginx配置雜記
Nginx是一個高性能的HTTP和反向代理服務器,也是一個IMAP/POP3/SMTP代理服務器,相較于Apache,具有占有內存少、穩定性高等優勢。Nginx安裝非常簡單、配置文件簡潔,但是配置的類目卻不少,本文主要記錄Nginx的安裝以及相關的配置(以下操作在CentOS6.7 64bit環境下)。
Nginx安裝
作為一個喜歡折騰的人,Nginx首選當然是采用源碼包安裝,不過也可以選擇yum、rpm來安裝。Mac下,用brew install nginx來快速安裝。
為了能從源碼包編譯Nginx,除了編譯器之外,還需要提供OpenSSL(啟用SSL)以及Perl(使用rewrite)、zlib壓縮庫等等。由于Nginx的模塊化特性,還可以安裝第三方模塊為Nginx提供額外的功能,如ngx_lua、pageSpeed。
第三方模塊可以在 github 或者 https://www.nginx.com/resources/wiki/modules/ 查找。
接下來,通過安裝Nginx相關依賴庫、Nginx以及ngx_lua來介紹如何安裝Nginx以及第三方模塊。
SOURCE_DIR=/source/
PCRE_DIR=pcre-8.37
PCRE_TAR=pcre-8.37.tar.gz
PCRE_URL=http://sourceforge.net/projects/pcre/files/pcre/8.37/pcre-8.37.tar.gz
ZLIB_DIR=zlib-1.2.8
ZLIB_TAR=zlib-1.2.8.tar.gz
ZLIB_URL=http://sourceforge.net/projects/libpng/files/zlib/1.2.8/zlib-1.2.8.tar.gz
OPENSSL_DIR=openssl-1.0.2h
OPENSSL_TAR=openssl-1.0.2h.tar.gz
OPENSSL_URL=https://www.openssl.org/source/openssl-1.0.2h.tar.gz
OPENSSL_INSTALL=/usr/local/openssl
LUAJIT_DIR=LuaJIT-2.0.4
LUAJIT_TAR=LuaJIT-2.0.4.tar.gz
LUAJIT_URL=http://luajit.org/download/LuaJIT-2.0.4.tar.gz
NGINX_DIR=nginx-1.11.1
NGINX_TAR=nginx-1.11.1.tar.gz
NGINX_URL=http://nginx.org/download/nginx-1.11.1.tar.gz
NGINX_INSTALL_DIR=/usr/local/nginx/
NGX_DEVEL_KIT=https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz
NGX_DEVEL_KIT_TAR=v0.3.0.tar.gz
NGX_DEVEL_KIT_DIR=ngx_devel_kit-0.3.0
NGX_LUA=https://github.com/openresty/lua-nginx-module/archive/v0.10.5.tar.gz
NGX_LUA_TAR=v0.10.5.tar.gz
NGX_LUA_DIR=lua-nginx-module-0.10.5
export LUAJIT_LIB=/usr/local/lib
export LUAJIT_INC=/usr/local/include/luajit-2.0
mkdir -p $SOURCE_DIR
# 更新yum源以及安裝gcc、gcc-c++
yum update -y && yum install -y gcc gcc-c++
# 安裝pcre
cd $SOURCE_DIR
wget $PCRE_URL
tar -zxvf $PCRE_TAR
cd $SOURCE_DIR$PCRE_DIR
./configure && make && make install
# 安裝zlib
cd $SOURCE_DIR
wget $ZLIB_URL
tar -zxvf $ZLIB_TAR
cd $SOURCE_DIR$ZLIB_DIR
./configure
make && make install
# 安裝perl
yum install -y perl
# 安裝openssl
cd $SOURCE_DIR
wget $OPENSSL_URL
tar -zxvf $OPENSSL_TAR
cd $SOURCE_DIR$OPENSSL_DIR
./config --prefix=$OPENSSL_INSTALL
make && make install
# 安裝luajit
cd $SOURCE_DIR
wget $LUAJIT_URL
tar -zxvf $LUAJIT_TAR
cd $SOURCE_DIR$LUAJIT_DIR
make
make install
cd $SOURCE_DIR
wget $NGX_DEVEL_KIT
tar -zxvf $NGX_DEVEL_KIT_TAR
wget $NGX_LUA
tar -zxvf $NGX_LUA_TAR
# 安裝nginx
cd $SOURCE_DIR
wget $NGINX_URL
tar -zxvf $NGINX_TAR
cd $NGINX_DIR
groupadd www
useradd www -g www
./configure --user=www --group=www --prefix=$NGINX_INSTALL_DIR --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-openssl=$SOURCE_DIR$OPENSSL_DIR --add-module=$SOURCE_DIR$NGX_DEVEL_KIT_DIR --add-module=$SOURCE_DIR$NGX_LUA_DIR
make && make install
# 解決nginx啟動問題
ln -s /lib64/libpcre.so.0.0.1 /lib64/libpcre.so.1
ln -s /usr/local/lib/libluajit-5.1.so.2 -s /lib64/libluajit-5.1.so.2
Nginx配置起步
nginx.conf是主配置文件,由若干個部分組成,每個大括號({})表示一個部分。每一行指令都由分號結束(;),標志著一行的結束。
以下是一份簡單的配置:
user nobody nobody;
worker_processes 2;
events {
use epoll;
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
access_log /dev/null;
error_log /dev/null;
sendfile on;
keepalive_timeout 65;
gzip on;
gzip_min_length 1024;
gzip_buffers 4 8k;
gzip_http_version 1.0;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
}
}
從配置可以看出,Nginx監聽了80端口、域名為localhost、根路徑為html文件夾(上面安裝路徑為 /usr/local/nginx,所以絕對路徑為/usr/local/nginx/html)、默認index文件為index.html、index.htm。
Nginx測試配置文件(包含include文件),可通過以下命令來完成,但是只檢查語法錯誤。
nginx -t -c /path/to/nginx.conf
include指令
在Nginx的配置文件中,include可以出現在任何地方,以便增強配置文件的可讀性,使得部分配置文件可以重新使用。
使用include包含的文件,必須確保包含的文件自身有正確的Nginx語法,即配置指令和塊,然后指定這些文件的路徑(沒有給全路徑的情況下,Nginx會基于它的主配置文件路徑進行搜索)。如:
include mime.types; # 即:/usr/local/nginx/conf/mime.types
如果路徑中出現通配符,表示可配置多個文件。這種多用于包含多個虛擬主機的情況,只需要在nginx.conf中http塊結束前添加如下一行配置,就可以將多個虛擬主機的配置分離出來,比較好維護。
include server/*.conf;
nginx將會配置server目錄下符合.conf結束的配置文件。
server部分
由關鍵字server開始的部分被稱作虛擬服務器部分,包含在http部分中,用于響應Http請求。一個虛擬服務器由listen和server_name指令組合定義。
listen指令定義了一個IP地址/端口組合或者是UNIX套接字路徑。listen還有其他的一些可選參數比如default_server、ssl、http2等等。
listen address[:port];
listen port;
listen unix:path;
server_name指令默認值為”",意味著沒有server_name指令時,對于沒有設置Host頭的請求將會匹配該server。比如說,對于IP地址訪問的請求,可以直接丟棄,如下:
server {
listen 80;
return 444; # Nginx對于Http非標準代碼會立即關閉一個鏈接
}
location指令
location指令可以用在server部分,提供來自客戶端的URI或者內部重定向訪問,也可以被嵌套使用。
location定義:
location [modifier] uri {...}
修飾符 | 含義 |
---|---|
= | 使用精確匹配并且終止搜索 |
^~ | 表示uri以某個常規字符串開頭,理解為匹配url路徑即可。它并非正則表達式匹配,目的是優于正則表達式匹配。這里匹配的是解碼uri,例如uri中的“%20”將會匹配location的“ ”(空格)。 |
~ | 區分大小寫的正則表達式匹配 |
~* | 不區分大小寫的正則表達式匹配 |
多個 location 配置的情況下匹配順序為(當有匹配成功時候,停止匹配,按當前匹配規則處理請求):
- 匹配=
- 匹配^~
- 按文件中正則出現的順序匹配
除了上面的location定義之外,還可以命名location:
location @name {...}
命名location僅對內部訪問重定向,在進入一個location之前它會保留被請求的URI部分。命名重定向只能夠在server級別定義。
location指令除了以下兩點之外可以被嵌套:
- 具有“=”前綴
- 命名location
如下:
location / {
location ^~ /css {
location ~* /css/.*\.css$ {
}
}
}
valid_referers指令
valid_referers指令是用來校驗Http請求頭Referer是否有效。
valid_referers none | blocked | server_names | string ...;
nginx會通過查看Referer字段和valid_referers后面的Referer列表進行匹配,如果匹配到了就將invalid_referer字段值設為0,否則設為1。
- none:表示沒有Referer;
- blocked:表示有Referer但是被防火墻或者是代理給去除了;
- server_names:表示Referer頭為server_names列表中的一個;
- string|regular expression:用于匹配Referer。
通過我們通過校驗Referer頭來處理圖片防盜鏈,如下:
location ~* \.(gif|jpg|png)$ {
valid_referers none blocked server_names;
if ($invalid_referer) {
return 403;
}
}
try_files指令
try_files指令可以用在server部分,不過最常見的還是用在location部分,它會按照給定的參數順序進行嘗試,第一個被匹配到的將會被使用。
它的語法如下:
try_files file ... uri;
try_files file ... =code;
try_files指令可能會通過添加‘/’來檢查目錄的存在,如“$uri/”。如果沒有找到文件,會進行一個內部重定向到最后一個參數,最后一個參數可以是命名location或者Http狀態碼。
它經常被用于從一個變量去匹配一個可能的文件,然后將處理傳遞到一個命名location,如下:
location / {
try_files $uri $uri/ @mongrel;
}
location @mongrel {
proxy_pass http://server;
}
if是邪惡的
當在location塊中使用if指令,在某些情況下它并不按照預期運行,一般來說避免使用if指令。
if指令的語法如下:
if (condition) { ... }
在location塊中if指令能夠保證安全的指令應該只有以下兩個:
return ...;
rewrite ... break;
在某些情況下,需要測試一個變量,那么還是需要用到if指令,如下:
if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /msie/$1 break;
}
if ($invalid_referer) {
return 403;
}
以下用一些例子說明if可能產生一些意料不到的結果:
# 第二個header才會出現在響應頭中
location /only-one-if {
set $true 1;
if ($true) {
add_header X-First 1;
}
if ($true) {
add_header X-Second 2;
}
return 204;
}
# try_files失效
location /if-try-files {
try_files /file @fallback;
set $true 1;
if ($true) {
# nothing
}
}
nginx內置預定義變量
為了使得更加容易配置Nginx,Nginx提供了許多預定義的變量,當然也可以通過使用set來設置變量。你可以在if中使用預定義變量,也可以將它們傳遞給代理服務器。
變量名稱 | 值 |
---|---|
$args_name | 在請求中的name參數 |
$args | 所有請求參數 |
$query_string | $args的別名 |
$content_length | 請求頭Content-Length的值 |
$content_type | 請求頭Content-Type的值 |
$host | 如果當前有Host,則為請求頭Host的值;如果沒有這個頭,那么該值等于匹配該請求的server_name的值 |
$remote_addr | 客戶端的IP地址 |
$request | 完整的請求,從客戶端收到,包括Http請求方法、URI、Http協議、頭、請求體 |
$request_uri | 完整請求的URI,從客戶端來的請求,包括參數 |
$scheme | 當前請求的協議 |
$uri | 當前請求的標準化URI |
反向代理
反向代理是一個Web服務器,它接受客戶端的連接請求,然后將請求轉發給上游服務器,并將從服務器得到的結果返回給連接的客戶端。
比如說,我們用Node寫了一個服務,掛載在服務器的3000端口上,用戶并不能直接該服務,可以采用Nginx來轉發,訪問http://xxx.com,Nginx反向代理,從http://localhost:3000獲取內容。配置如下:
server {
listen 80;
server_name xxx.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
}
代理到上游服務器的配置中,最重要的是proxy_pass指令。
以下是代理模塊中的一些常用指令:
指令 | 說明 |
---|---|
proxy_connect_timeout | Nginx從接受請求至連接到上游服務器的最長等待時間 |
proxy_cookie_domain | 替代從上游服務器來的Set-Cookie頭的domain屬性 |
proxy_cookie_path | 替代從上游服務器來的Set-Cookie頭的path屬性 |
proxy_set_header | 重寫發送到上游服務器頭的內容,也可以通過將某個頭部的值設置為空字符串,而不發送某個頭部的方法實現 |
upstream模塊
upstream指令啟用一個新的配置區段,在該區段定義一組上游服務器。這些服務器可能被設置不同的權重,也可能出于對服務器進行維護,標記為down。
以下是一個簡單的upstream示例:
upstream nodejs {
ip_hash;
server 127.0.0.1:3000;
server 127.0.0.1:3001 down;
keepalive 32;
}
server指令可選參數 :
- weight:設置一個服務器的訪問權重,數值越高,收到的請求也越多;
- fail_timeout:在這個指定的時間內服務器必須提供響應,如果在這個時間內沒有收到響應,那么服務器將會被標記為down狀態;
- max_fails:設置在fail_timeout時間之內嘗試對一個服務器連接的最大次數,如果超過這個次數,那么服務器將會被標記為down;
- down:標記一個服務器不再接受任何請求;
- backup:一旦其他服務器宕機,那么有該標記的機器將會接收請求。
keepalive指令 :
Nginx服務器將會為每一個worker進行保持同上游服務器的連接。
負載均衡 :
upstream模塊能夠使用3種負載均衡算法:輪詢、IP哈希、最少連接數。
- 輪詢:默認情況下使用輪詢算法,不需要配置指令來激活它,它是基于在隊列中誰是下一個的原理確保訪問均勻地分布到每個上游服務器;
- IP哈希:通過ip_hash指令來激活,Nginx通過IPv4地址的前3個字節或者整個IPv6地址作為哈希鍵來實現,同一個IP地址總是能被映射到同一個上游服務器;
- 最少連接數:通過least_conn指令來激活,該算法通過選擇一個活躍數最少的上游服務器進行連接。如果上游服務器處理能力不同,可以通過給server配置weight權重來說明,該算法將考慮到不同服務器的加權最少連接數。
常見使用場景
使用SSL對流量進行加密
Nginx經常被用于終結SSL連接,比如說多說采用第三方登錄的圖片并不支持https,可以通過改造圖片URL(指向https某個目錄下),然后Nginx將https反向代理到http上游服務器獲取圖片來解決。
以下以解決多說頭像不支持http問題為例,將https://wenjs.me/proxy/xxx.png轉發到http://xxx.png
Server配置如下:
server {
listen 443 ssl http2 default;
server_name wenjs.me;
ssl on;
ssl_certificate xxx.crt;
ssl_certificate_key xxx.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 60m;
ssl_session_tickets on;
ssl_stapling on;
ssl_stapling_verify on;
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_prefer_server_ciphers on;
# other location
resolver 8.8.8.8;
# 轉發到http源地址
location ~ ^/proxy/(\w+\.)(bdimg\.com|cdncache\.org|douban\.com|gravatar\.com|qlogo\.cn|sinaimg\.cn|xnimg\.cn)(\/.*)$ {
valid_referers blocked server_names;
if ($invalid_referer) {
return 403;
}
proxy_connect_timeout 10s;
proxy_read_timeout 10s;
proxy_pass http://$1$2$3;
expires max;
}
# 返回默認圖片
location ~ ^/proxy/(.*)$ {
valid_referers blocked server_names;
if ($invalid_referer) {
return 403;
}
rewrite https://static.duoshuo.com/images/noavatar_default.png permanent;
}
}
下載多說的embed.js,修改頭像的URL,頁面引用該新的embed.js即可。
avatarUrl: function(e) {
if (e.avatar_url) {
e.avatar_url = e.avatar_url.replace(/^http\:\/\//, "http://wenjs.me/proxy/");
}
return e.avatar_url || nt.data.default_avatar_url
}
跨域問題
在工作中,有時候會遇到一些接口不支持跨域,這時候可以簡單的添加add_headers來支持cors跨域。配置如下:
server {
listen 80;
server_name api.xxx.com;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET,POST,HEAD';
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
}
正向代理
正向代理是一個位于客戶端和原始服務器之間的代理服務器,為了從原始服務器取得內容,客戶端向代理服務器發送一個請求并指定目標(原始服務器),然后代理服務器向原始服務器轉交請求并將獲得的內容返回給客戶端。客戶端必須要進行一些特別的設置才能使用正向代理。
比如說,一個用戶訪問不了某網站A,但是他能訪問一個代理服務器,這個代理服務器能訪問A,于是用戶可以先連上代理服務器,告訴它需要訪問的內容,代理服務器去取回來返回給用戶。
透明代理
透明代理的意思是客戶端根本不需要知道有代理服務器的存在,它改編你的請求頭(報文),并會傳送真實IP。以前在學校的時候,校園網就采用了透明代理的方式。
總結
以上內容主要介紹了Nginx的安裝、配置指令以及Proxy模塊相關的知識,希望能對Nginx有個初步的認識,在前端開發中,能夠利用Nginx來快速搭建一些調試環境。
來自:http://www.androidchina.net/5134.html