最後更新: 2022-12-26
目錄
- Modules
- 安裝
- 使用入門
- 設定
- try_files
- if
-
Log Settings
Custom Log Format - MIME Types
- add_header
- Default Host & vhost
- SSL
- Environment Variables
- Set Variables
- header exists
-
location
location 的設定及順序
Mult-path in one location - ulimit
-
nginx url redirect
- non-www to www
- Jump to subdirectory
- Jump another link (By rewrite)
- List of redirect - root 與 alias 的分別
- Secret Header
- Expires
- 有用 Modules
- Doc
介紹
ngnix 是其中一個解決 C10K 問題的 Web Server 來
它用了 event-driven (asynchronous) architecture 來解決此問題.
功能
- HTTP Server
- reverse proxy
- mail proxy(IMAP, POP3, SMTP)
Modules
Core modules:
- Main ( configure, logging, processes )
- Events (epoll)
HTTP modules:
- Core
- Access
- Auth Basic
- Auto Indexing
- Rewrite
- Gzip
- Index
- Limit Requests
- Log
- Proxy
- Upstream
- FastCGI
- uWSGI
Mail modules
- ngx_mail_core_module (not built by default, --with-mail)
Load module
/etc/nginx/nginx.conf
load_module "modules/ngx_http_fancyindex_module.so"; load_module "modules/ngx_http_echo_module.so"; http {}
安裝
Debian 安裝
apt-get install nginx
Centos6 安裝
repo:
[nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=0 enabled=1 priority=1
準備:
mkdir /var/spool/nginx cd /var/spool/nginx mkdir client_body_temp proxy_temp chown www-data. client_body_temp proxy_temp chmod 770 client_body_temp proxy_temp
人手 compile:
https://datahunter.org/build_by_source#nginx
Install from official website
http://nginx.org/en/linux_packages.html
/etc/yum.repos.d/nginx.repo
[nginx] name=nginx repo baseurl=http://nginx.org/packages/OS/OSRELEASE/$basearch/ gpgcheck=0 enabled=1
Replace “OS” with “rhel” or “centos”, depending on the distribution used, and “OSRELEASE” with “6” or “7”, for 6.x or 7.x versions, respectively.
i.e.
baseurl=http://nginx.org/packages/centos/7/$basearch/
使用入門
Usage
nginx [options] <configuration file>
# check version
nginx -v
nginx version: nginx/1.2.1
# Help
nginx -h
# 查看 compile 了什麼入去
nginx -V
... --with-pcre-jit --with-debug --with-http_addition_module --with-http_dav_module ...
# 查看 status
service nginx status
[ ok ] nginx is running.
# 檢查 configure file 有無錯誤
nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
# send signal to a master process (stop, quit, reopen, reload)
nginx -s reload
其他 signal:
- USR2 (12) - upgrade by renaming the old .pid file and running the new binary
- WINCH (28) - engage a graceful shutdown
- QUIT - old worker processes are terminated
設定
設定檔: /etc/nginx/nginx.conf
結構
.... events { .... } http { .... server { .... } server { .... } }
Global Setting:
daemon on; // Default on
master_process on // default on, start multiple processes
# user group
user www-data www-data;
# worker_processes auto; => set one worker per CPU core
# max_clients = worker_processes * worker_connections
# Default value: 1
worker_processes 2;
pid /var/run/nginx.pid;
# LOG
error_log /file/path level; // debug, info, notice, warn, error, and crit
log_not_found on; // log 404, default on
timer_resolution 100ms; // interval between system calls to gettimeofday()
thread_stack_size 1m;
worker_cpu_affinity 01 10; // xx: 2 cpu core
// xxx: 3 cpu core
// e.g.
// 001 010 100: 3 worker processe
worker_priority 0; // 19~-20
working_directory /user/nginx; // location of core files, user 可以對它寫入
#### rlimit ####
# size of core files per worker process (RLIMIT_CORE)
worker_rlimit_core 100m;
# amount of files per worker process (RLIMIT_NOFILE)
# 相當於在 /etc/security/limits.conf 加入
# "nginx soft nofile 4096" 及 "nginx hard nofile 4096"
worker_rlimit_nofile 4096;
events {
# It should be kept in mind that this number includes all connections(connections with proxied servers),
# not only connections with clients.
worker_connections 1024;
# on(Default): worker processes will accept new connections by turn
# off: all worker processes will be notified about new connections
accept_mutex on
# 在 "accept_mutex on" 時 worker 多久"restart accepting new connections"
accept_mutex_delay 500ms
# Default: off. If multi_accept
is disabled, a worker process will accept one new connection at a time
# on: tries to accept() as many connections as possible
# Default: off
multi_accept on;
debug_connection 172.63.155.21; # Writes detailed logs for clients matching this IP
debug_connection 172.63.155.0/24;
# nginx will normally select the most efficient method automatically.
# kqueue — efficient method used on FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0, and macOS.
# epoll — efficient method used on Linux 2.6+.
use epoll;
}
http module(ngx_http_core_module) 的 settings
http { # 不顯示 nginx version 在 error page 及 HTML header, Default ON server_tokens off; # sendfile() is more efficient than the combination of read(2) and write(2) sendfile on; # Enables or disables the TCP_CORK socket option on Linux. (Default off) # tcp_nopush is opposite to tcp_nodelay # The options are enabled only when sendfile is used. # off => attempt to send it's HTTP response headers in one packet on Linux # package to wait until it gets its maximum size (MSS) before sending it to the client # (for the last packet send it immediately) tcp_nopush on; # don't buffer data-sends (disable Nagle algorithm). Good for sending # frequent small bursts of data in real time. tcp_nodelay on; # The server will close connections after this time. # Client 亦會收到 HTTP Header "Keep-Alive: timeout=time
Keep-Alive: timeout=time
Keep-Alive: timeout=time" keepalive_timeout 65; # Sets the maximum size of the types hash tables. types_hash_max_size 2048; include /etc/nginx/conf.d/*.conf; # gzip setting include snippets/gzip.conf; # 用來放 vhost 的設定 include /etc/nginx/sites-enabled/*.conf; }
server_name 的命中次序:
if name matches more than one of the specified variants
- exact name
- longest wildcard name starting with an asterisk, e.g. “*.example.org”
- longest wildcard name ending with an asterisk, e.g. “mail.*”
- first matching regular expression
listen
listen [address][:port] [additional options];
catch-all | wildcard
# default host 不是在 server_name 上設定的 !!
The default server is the first one / set explicitly by default_server
i.e.
listen 80 default_server;
# required to process requests without the “Host” header field
If no server_name is defined in a server block then nginx uses the empty name as the server name.
=> without the “Host” header field
server { listen 80; ... }
OR
server { listen 80; server_name example.org www.example.org ""; ... }
# 效能
if the most frequently requested names of a server are example.org and www.example.org,
it is more efficient to define them explicitly(*.example.org 放到最尾):
server { listen 80; server_name example.org www.example.org *.example.org; ... }
Special Wildcard name ("*")
“.example.org” = “example.org” and “*.example.org”.
server_name use a regular expression
must start with the tilde character:
server_name ~^www\d+\.example\.net$;
snippets/gzip.conf
gzip on;
gzip_disable "msie6";
gzip_comp_level 6; # 1 -> 9
gzip_min_length 4k;
gzip_types text/plain
text/css
text/javascript
application/javascript
application/x-javascript
application/x-font-woff
text/xml
application/xml;
# any = enables compression for all proxied requests.
# off
off(default)
gzip_proxied any;
詳見: nginx_gzip
try_files
Usage:
try_files file ... uri;
try_files file ... =code;
功能:
順序檢查文件是否存在, 返回第一個找到的文件
The path to a "file" is constructed from the file parameter according to the "root" and "alias" directives.
If none of the files were found, an "internal redirect" to the uri specified in the last parameter is made.
它功能相當於
location / { if (!-f $request_filename) { break; } }
e.g.
[2]
location /images/ { try_files $uri /images/default.gif; } location = /images/default.gif { expires 30s; }
[2]
location ~* \.php$ { try_files $uri =404; ... }
防止了 error
... [error] 14278#0: *17186 FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream, client: s.s.s.s, server: datahunter.org, request: "GET /test.php HTTP/1.1", upstream: "fastcgi://127.0.0.1:9056", host: "datahunter.org"
if
if 是由 ngx_http_rewrite_module 提供. nginx 有 if, 但卻不支援 else.
Syntax
if (variable) { ... }
# false if the value of a variable is an empty or “0”
e.g.
if ($slow) { return 403; }
if (condition) { ... }
# comparison of a variable with a string using the “=” and “!=” operators;
regular expression
captures that are made available for later reuse in the $1..$9 variables
- ~, !~ # for case-sensitive matching operators
- ~*, !~* # for case-insensitive matching operators
Check file, directory, link
- -f # check file existence
- -d # check directory existence
- -e # file, directory, link
- -x # executable file
e.g.
if ($allowed_country = no) { return 403; }
break;
Stops processing the current set of ngx_http_rewrite_module directives.
if ($allow_ip) { break; } if ($allowed_country = no) { return 403; }
$allow_ip 是 true 時會跳過第2句
break vs last
?
Log Settings
Default 的 log 位置
- /var/log/nginx/access.log
- /var/log/nginx/error.log
Settings
access_log PATH [FORMAT]; // Default: combined
error_log /path/to/file [Level]; // Level: debug, info, notice, warn, error, and crit
log_not_found on; // 是否 log 404. default on
什麼也不 log
access_log off; error_log off;
Custom Log Format
e.g. cache hit rate
Settings
http { # log log_format cache_status '[$time_local] $remote_addr - $upstream_cache_status ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent"'; access_log /var/log/nginx/access.log cache_status; ... }
Analysing cache efficiency
awk '{print $5}' /var/log/nginx/access.log | sort | uniq -c | sort -r
33 - 24 MISS 185 HIT
e.g. Log request authorization header($http_authorization) & POST body($request_body)
http { # 已要留空格, 否則兩行會連埋 log_format postdata '[$time_local] $remote_addr "$request" $status $bytes_sent ' 'Header: $http_authorization Body: $request_body'; server { location = /post.php { access_log /var/log/nginx/postdata.log postdata; fastcgi_pass php_cgi; } } }
TEST
curl -d '{"key1":"value1", "key2":"value2"}' \
-H "Content-Type: application/json" \
-X POST https://URL/postEndpoint/
escape
Syntax
log_format name [escape=default|json|none] string
escape=default|json|none
default: escaping characters with
\xXX " => \x22
json: all characters not allowed in JSON strings will be escaped(\xXX)
" => \" \ => \\
characters with values less than 32 are escaped as “\n”, “\r”, “\t”, “\b”, “\f”, or “\u00XX”.
MIME Types (mime.types)
Format: mime.types
types { mimetype2 extension2 [extension3…]; […] }
i.e.
types { image/gif gif; image/jpeg jpeg jpg; }
# force all files in a folder to be downloaded instead of being displayed:
location /downloads/ {
# removes all MIME types
types { };
default_type application/octet-stream;
}
# force Content-Type (In case you have no file extension)
location ~ /share { add_header Content-Type application/xml; }
e.g.
http://datahunter.org/404
add_header
Syntax
add_header name value [always];
* http{} server{} location{} 係依次序繼承
當在 location{} 有 add_header 時, 那在 http{} 及 server{} 裡加的 add_header 都會無晒(不會被繼承)
server { # Add header location / { # Add header } }
* Adds the specified field to a response header provided that the response code equals
200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), or 308 (1.13.0)
Opts
- always = added regardless of the response code
Example
add_header content-type "text/html; charset=UTF-8";
Default Host & vhost
Default Host 設定
/etc/nginx/conf.d/default_vhost.conf
server { # for http server_name _; listen 80 default_server; # for https #listen 443 ssl default_server; #ssl_certificate /etc/nginx/ssl/cacert.pem; #ssl_certificate_key /etc/nginx/ssl/privkey.pem; # Default Host 不用 log log_not_found off; # Default Page return 404; # mkdir -p /var/www/default #root /var/www/default; }
vhost 設定
/etc/nginx/sites-enabled/datahunter.org.conf
server { server_name datahunter.org www.datahunter.org; listen 80; }
Notes
SSL
HTTPS
server { listen 443 ssl; server_name datahunter.org;; root html; index index.html index.htm; # The primary certificate comes first(Server), then the "intermediate certificates" ssl_certificate ssl/datahunter.org/cert.pem; ssl_certificate_key ssl/datahunter.org/cert.key; # Enable TLS session tickets (https://tools.ietf.org/html/rfc5077) ssl_session_tickets on; # builtin: a cache built in OpenSSL; used by one worker process only.(specified in sessions) # shared: a cache shared between all worker processes.(specified in bytes, 1m ~ 4000 sessions) # Both cache types can be used simultaneously, # for example: ssl_session_cache builtin:1000 shared:SSL:10m; ssl_session_cache shared:SSL:10m; # SSL session 的 cache 時間, Default: 5m; ssl_session_timeout 1d; # ssl_protocols TLSv1.1 TLSv1.2; # Specified in the format understood by the OpenSSL library ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP; # Server 設定優先 ssl_prefer_server_ciphers on; # Key Exchange # By default, Nginx will use the default DHE (Ephemeral Diffie-Hellman) paramaters provided by openssl. # This uses a weak key that gets lower scores. The best thing to do is build your own. # openssl dhparam 2048 > /etc/nginx/ssl/dhparam.pem ssl_dhparam ssl/dhparam.pem; # Force https on next visit # If you already visited the https version the next time your browser will directly visit the https site and not the http one. # Prevent man-in-the-middle-attacks: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; location / { try_files $uri $uri/ =404; } }
* SSL 的 Key 及 Cert 的 Path 不可以用 Custome Variables
ssl_ciphers
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
Check ssl chain (intermediate certificate)
openssl s_client -connect www.x.y:443 | less
--- Certificate chain 0 s:/OU=Domain Control Validated/OU=EssentialSSL Wildcard/CN=*.x.y i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA 1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority 2 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root 3 s:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root ---
由於 "3" 是 CA, 所以 "s" 及 "i" 都是一樣的.
因為 Device 已內置了 CA, 所以 "3" 是多餘的
intermediate certificate = "1" 及 "2"
OCSP stapling
Environment Variables
Context: main # 所以在 server {...} 不能用 !!
因為
nginx removes all environment variables inherited from its parent process except the TZ variable.
env variable[=value];
- allows preserving some of the inherited variables
- changing variables values
- creating new environment variables
Set Variables
set
set $variable value;
Sets a value for the specified variable.
The value can contain text, variables, and their combination.
* 此 var 不可以用在 access_log, error_log, root, server_name, ssl_certificate
Example
set $message "Hello Buddy";
Proxy Setting
302_problem:
nginx
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header
redefining or appending fields to the request header passed to the proxied server.
These directives are inherited from the previous level
if and only if there are no proxy_set_header directives defined on the current level.
These directives are inherited from the previous level if and only if there are no proxy_set_header directives defined on the current level.
If the value of a header field is an empty string then this field will not be passed to a proxied server:
proxy_set_header Accept-Encoding "";
.htaccess
SetEnvIf X-Forwarded-Proto "https" HTTPS=on
主要是修正了以下問題:
<? echo $_SERVER["REMOTE_ADDR"]; echo $_SERVER["HTTPS"]; ?>
header exists
if ($http_x_custom_header) { .... } map $http_x_header $is_ok { default "0"; Value1 "1"; Value2 "1"; Value3 "1"; }; if ($is_ok) { return 405; }
location
- location 的設定及順序
- Nested location
- Mult-path in one location
A location can either be defined by a prefix string, or by a regular expression.
# 命中全部
location = FULL_PATH # 精確匹配, 只有完全匹配上才能生效
# First matching expression stops
location ^~ regex # non-regular expression match
location ~ regex # case-sensitive regular expression
location ~* regex # case-insensitive matching
# 命中部份
location PATH # prefix matching (沒有用 regex)
解說
Step To find location matching a given request
- exact locations ( location = /xxx { } )
- regexp locations ( location ~, location ~* ) - first match
- prefix locations ( location /xxx { } ) - longest matching prefix is selected
e.g.
Config:
# mkdir /home/vhosts/test/public_html/images -p root /home/vhosts/test/public_html; #### location test location / { return 1; } location = / { return 2; } location ~* \.(gif|jpg|jpeg)$ { return 3; } location ~ / { return 4; } location ~ /images/ { return 5; } location /img/ { return 6; }
Request:
"/" # 1, 2 or 4 ? 2, 原因: 精準("=")
"/index.html" # 1 or 4 ? 4, 原因: regex
"/images/test.jpg" # 3, 4 or 5 ? 3, 原因: regex first match
"/img/test.png" # 1, 4 or 6 ? 4, 原因: regex
結論: 全部用 ~ 會一至許多.
Notes
if a "/" request happens frequently, defining "location = /" will speed up the processing of these requests
location /test { location /test/1 { return 1;} location /test/2 { return 2;} return 3; }
* You should use /test/1 & /test/2 in your inner location block
as the inner URLs are not relative to the outer URLs.
* When a parent location is defined by a regex,
any nested locations must also be defined by regexes
(However, you may define nested regex locations when the parent location is a prefix location)
測試
- curl -I http://192.168.88.20/test
- curl -I http://192.168.88.20/test/1
- curl -I http://192.168.88.20/test/2
location ~ ^/(first/location|second/location)/ { ... }
應用
# Protect admin panel location ~ ^/(wp-admin|wp-login.php) { auth_basic "Restricted"; auth_basic_user_file htpasswd; }
Example
1) Mult-path in one location & Nested location
location ~ ^/(a|b) { location ~ ^/a { ... } location ~ ^/b { ... } }
2) API no cache
location /api { # Proxy Settings include backend/b1.conf; } location / { location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { # Enable Cache proxy_cache MyCache; add_header X-Cache-Status $upstream_cache_status; proxy_cache_valid 200 1d; # No Logs log_not_found off; access_log off; # Proxy Settings include backend/b1.conf; } # Proxy Settings include backend/b1.conf; }
regex captured groups
# use $1 for the first, \d+ and $2 for the second, and so on.
location ~/photos/resize/(\d+)/(\d+) { ... }
# Named captures are a feature of PCRE and they have different syntax available in different versions.
location ~/photos/resize/(?<width>(\d+))/(?<height>(\d+)) { # so here you can use the $width and the $height variables }
# test
location ~ ^/r/([0-9a-zA-Z]+) { echo $1; }
ulimit
Check
su - nginx -s /bin/bash
-, -l, --login # make the shell a login shell
ulimit -n
設定
/etc/security/limits.conf
nginx hard nofile 65535 nginx soft nofile 65535
OS limit
sysctl fs.file-max
202785
Solution
use the command ulimit in nginx start script
ulimit -n 65536
nginx url redirect
non-www to www
[方案 1]
server { listen 80; server_name example.com; return 301 http://www.example.com$request_uri; } server { listen 80; server_name www.example.com; [...] }
[方案 2]
server { listen 80; server_name example.com www.example.com; if ($host = example.com) { return 301 $scheme://www.example.com$request_uri; } # Your other Nginx configuration directives for the domain go here }
Jump to subdirectory
location = /content/unique-page-name { return 301 /new-name/unique-page-name; }
Jump another link (By rewrite)
假設想實現以下 redirect
http://example.com/issue1 --> http://example.com/shop/issues/custom_issue_name1 http://example.com/issue2 --> http://example.com/shop/issues/custom_issue_name2 http://example.com/issue3 --> http://example.com/shop/issues/custom_issue_name3
[方法1]
location /issue { rewrite ^/issue(.*) http://$server_name/shop/issues/custom_issue_name$1 permanent; }
[方法2]
location /issue1 { rewrite ^/.* http://$server_name/shop/issues/custom_issue_name1 permanent; } location /issue2 { rewrite ^.* http://$server_name/shop/issues/custom_issue_name2 permanent; } ......
List of redirect
On Apache
.htaccess
Redirect 301 /a/b http://1/2 Redirect 301 /c/d http://3/4 ...
On Nginx
有關 Setting
map_hash_bucket_size size; # Default: 32|64|128 map_hash_max_size size; # Default: 2048
Error Page
自定 Error Page
#### Cust error page # error_page 500 502 503 504 /50x.html; error_page 404 /404.html location = /404.html { root /usr/share/nginx/html/error; } location = /50x.html { root /usr/share/nginx/html/error; } #### location / { # test 404 return 404; error_page 404 @sorry; }
root 與 alias 的分別
在這以下 Setting 下
location ~ /munin { autoindex off; root /var/cache/munin/www; }
去 http://x.x.x/munin 時, 那 request 會去了 "/var/cache/munin/www/munin"
所以, 如果想 /munin 係直接拿 /www 內的東西,
那就要用 alias
location /munin { autoindex off; alias /var/cache/munin/www; }
map file
以下兩個結果係一樣的
- location /robots.txt { alias /home/www/static/any-filename.txt; }
- location /robots.txt { root /home/www/static/; }
alias with regex
* An alias within a regex location block requires a value which specifies the full path to the target file.
e.g.
location ~ ^/myapp/([a-zA-Z0-9_-]+)/(.*)$ { alias /var/lib/myapp/$1/static/$2; }
Expires
i.e.
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { expires 1w; log_not_found off; ... }
Response Headers 會多了
Expires: Wed, 15 May 2024 02:26:17 GMT
有用 Modules
jemalloc: http://datahunter.org/jemalloc
tengine: http://datahunter.org/tengine
pagespeed: http://datahunter.org/pagespeed
Doc
http://wiki.nginx.org/Configuration