blackLearning.github.io icon indicating copy to clipboard operation
blackLearning.github.io copied to clipboard

Nginx笔记

Open Fadingvision opened this issue 6 years ago • 0 comments

NGINX笔记

Nginx配置文件结构

Core Contexts:
  Global/Main Context
    Events Context
    HTTP Context
      Upstream Context
      Server Context
        Location Context
    Mail Context

全局变量:

  • $host: 请信息中的Host,如果请求中没有Host行,则等于设置的服务器名
  • $request_method: 客户端请求类型,如GET、POST
  • $remote_addr: 客户端的IP地址
  • $args: 请求中的参数
  • $content_length: 请求头中的Content-length字段
  • $http_user_agen: 客户端agent信息
  • $http_cookie: 客户端cookie信息
  • $remote_port: 客户端的端口
  • $server_protocol: 请求使用的协议,如HTTP/1.0、·HTTP/1.1`
  • $server_addr服务器地址
  • $server_name: 服务器名称
  • $server_port: 服务器的端口号

main: nginx的全局配置,对全局生效。

常用指令:

  • include: 用于引入其他的配置文件
  • error_log file [level]: 用于指定日志打印的文件位置和日志级别
error_log /var/log/nginx/error.log warn;
  • pid file: 用于指定存储主进程的进程id的文件;
# shell命令也可以用于查看nginx进程 ps -ax | grep nginx
pid /var/run/nginx.pid;
  • worker_processes number | auto: 用于指定子进程的数量,auto默认为CPU核心的数量;

events: 配置影响nginx服务器或与用户的网络连接。

常用指令:

  • worker_connections 8000: 指定一个子进程能够打开的最大的并发连接数量。

服务的最大连接数 = worker_connections * worker_processes;

http: 可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。

常用指令:

  • server_tokens off; 表明是否展示response header中的Server字段中的nginx版本号信息。

  • log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; 标明日志文件的记录格式和信息(main为该格式的名称)。

  • access_log /var/log/nginx/access.log main; 表明记录访问本服务的日志位置和采用的格式名称。

  • keepalive_timeout 20s; 允许每条连接保持闲置的时间,意味着:一个http产生的tcp连接在传送完最后一个响应后,还需要hold住keepalive_timeout秒后,才开始关闭这个连接。一般来说更长的时间更好,特别是对于需要进行加密传输的ssl连接来说,但是更长的时间也意味者连接会保持很长的时间,长时间的tcp连接容易导致系统资源无效占用,如果此时有新的大量连接进来,那么就可能导致连接数过大导致nginx崩溃。

  • sendfile on/off; 指定是否使用sendfile系统调用来传输文件,sendfile系统调用在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了数据在内核缓冲区和用户缓冲区之间的拷贝,操作效率很高,被称之为零拷贝。

  • map string $variable {...};

map 的主要作用是创建自定义变量,通过使用 nginx 的内置变量,去匹配某些特定规则,如果匹配成功则设置某个值给自定义变量。 而这个自定义变量又可以作于他用。

  # Add X-XSS-Protection for HTML documents.
  # h5bp/security/x-xss-protection.conf
  map $sent_http_content_type $x_xss_protection {
    #         (1)    (2)
    text/html "1; mode=block";
  }

  # Add X-Frame-Options for HTML documents.
  # h5bp/security/x-frame-options.conf
  map $sent_http_content_type $x_frame_options {
    text/html DENY;
  }

  # Add Content-Security-Policy for HTML documents.
  # h5bp/security/content-security-policy.conf
  map $sent_http_content_type "script-src 'self'; object-src 'self'" {
    text/html "script-src 'self'; object-src 'self'";
  }

  # Add Referrer-Policy for HTML documents.
  # h5bp/security/referrer-policy.conf.conf
  map $sent_http_content_type $referrer_policy {
    text/html "no-referrer-when-downgrade";
  }

  # Add X-UA-Compatible for HTML documents.
  # h5bp/internet_explorer/x-ua-compatible.conf
  map $sent_http_content_type $x_ua_compatible {
    text/html "IE=edge";
  }

  # Add Access-Control-Allow-Origin.
  # h5bp/cross-origin/requests.conf
  map $sent_http_content_type $cors {
    # Images
    image/bmp     "*";
    image/gif     "*";
    image/jpeg    "*";
    image/png     "*";
    image/svg+xml "*";
    image/webp    "*";
    image/x-icon  "*";

    # Web fonts
    font/collection               "*";
    application/vnd.ms-fontobject "*";
    font/eot                      "*";
    font/opentype                 "*";
    font/otf                      "*";
    application/x-font-ttf        "*";
    font/ttf                      "*";
    application/font-woff         "*";
    application/x-font-woff       "*";
    font/woff                     "*";
    application/font-woff2        "*";
    font/woff2                    "*";
  }

上面的设置了一些自定义的变量,这些变量会根据发送的content-type来设置对应的变量值。

server:配置虚拟主机的相关参数,一个http中可以有多个server, NGINX可以通过listen和server_name来决定使用哪个server来响应你的请求。

常用指令:

# http
server {
  listen [::]:80 default_server deferred;
  listen 80 default_server deferred;

  server_name _;

  # return 301 https://$host$request_uri;
  # 而且返回了一个nginx特有的,非http标准的返回码444,它可以用来关闭连接。
  return 444;
}
  • root /var/www/example.com/public; 设置静态文件请求的根路径

  • listen: 设置虚拟主机的IP地址和端口

    • IP地址端口连写
    • 单独的IP地址,端口默认80
    • 单独的端口, 默认为0.0.0.0
    • none, 默认为0.0.0.0:80
  • server_name: 设置虚拟主机的名称

常见的几种server_name的写法:

server {
    listen [::]:80;
    listen 80;
    server_name example.com;
    ...
}
server {
    listen 80;
    server_name ~^(www|host1).*\.example\.com$;
    ...
}
server {
    listen 80;
    server_name ~^(subdomain|set|www|host1).*\.example\.com$;
    ...
}
server {
    listen 80;
    server_name  ~^(?<user>.+)\.example\.net$;
    ...
}

nginx将会按照下面的原则顺序匹配server_name属性:

  • 首先尝试找到请求头部中的Host字段的值与server_name完全匹配的server block;
  • 匹配以首通配符开头的server_name
  • 匹配以尾通配符结尾的server_name
  • 第一个匹配到的用正则表达式描述的server_name(在名字前用~符号表示)
  • 如果上述都没有匹配到, nginx选择default_server标识 或者第一个server block作为请求匹配block.

关于选择 www 或非 www URL 作为域名

在一个 HTTP 网址中,在初始 http:// 或 https:// 后的第一个子字符串称为域。它是文档所在的服务器的名称。

一个服务器不一定是一个独立的物理机:几台服务器可以驻留在同一台物理机器上,或者一台服务器可以通过几台机器进行处理,协作处理并响应或负载均衡它们之间的请求。关键点在于语义上一个域名代表一个单独的服务器。

server {
  listen [::]:80;
  listen 80;

  server_name www.example.com;
  # 如果您选择使用非 www 网址为规范类型,你的所有 www 网址都应该被重定向到对应的非 www 网址上。
  return 301 $scheme://example.com$request_uri;
}

server {
  # listen [::]:80 accept_filter=httpready; # for FreeBSD
  # listen 80 accept_filter=httpready; # for FreeBSD
  listen [::]:80;
  listen 80;

  # The host name to respond to
  server_name example.com;

  # Path for static files
  root /var/www/example.com/public;

  # Custom error pages
  include h5bp/errors/custom_errors.conf;

  # Include the basic h5bp config set
  include h5bp/basic.conf;
}

location:配置请求的路由,以及各种页面的处理情况。nginx将会用url来匹配对应的location块,从而选择怎样去处理你的请求。

location optional_modifier location_match {
	...directives
}

optional_modifier:

  • =: 完全匹配
  • ~大小写敏感的正则匹配
  • ~*: 大小写不敏感的正则匹配
  • ^~: 非正则匹配
  • none: 用request url来进行普通的前缀匹配

常用指令:

  • index
index index.$geo.html index.0.html /index.html;
autoindex on | off;
  • try_files
root /var/www/main;
try_files $uri $uri.html $uri/ /fallback/index.html;
  • rewrite
rewrite ^/rewriteme/(.*)$ /$1 last;

一个以/rewriteme/hello的请求会被重定向为/hello的请求,此时location会被重新搜索

  • error_page
error_page 404             /404.html;
error_page 500 502 503 504 /50x.html;

upstream:配置上游的服务(例如nodejs, php, java服务)具体地址,负载均衡配置不可或缺的部分。

http {
    upstream myapp1 {
        server srv1.example.com weight=2;
        server srv2.example.com weight=1;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://myapp1;
        }
    }
}

upstream指令定义了一个被代理的服务器的列表,NGINX将会使用负载均衡来决定将请求发送到被代理的服务器上, 权重越大的服务就会被分配越多的连接。

Nginx如何处理一个请求

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

server {
    listen      192.168.1.1:80;
    server_name example.net www.example.net;
    ...
}

server {
    listen      192.168.1.2:80;
    server_name example.com www.example.com;
    ...
}

nginx首先测试请求的IP地址和端口是否匹配某个server配置块中的listen指令配置。接着nginx继续测试请求的Host头是否匹配这个server块中的某个server_name的值。如果主机名没有找到,nginx将把这个请求交给默认虚拟主机处理。例如,一个从192.168.1.1:80端口收到的访问www.example.com的请求将被监听192.168.1.1:80端口的默认虚拟主机处理,本例中就是第一个服务器,因为这个端口上没有定义名为www.example.com的虚拟主机。

默认服务器是监听端口的属性,所以不同的监听端口可以设置不同的默认服务器:

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

server {
    listen      192.168.1.1:80 default_server;
    server_name example.net www.example.net;
    ...
}

server {
    listen      192.168.1.2:80 default_server;
    server_name example.com www.example.com;
    ...
}

HTTPS/HTTP2

此部分的功能由ngx_http_ssl_module模块提供。

server {
  # listen [::]:443 ssl http2 accept_filter=dataready;  # for FreeBSD
  # listen 443 ssl http2 accept_filter=dataready;  # for FreeBSD
  listen [::]:443 ssl http2;
  listen 443 ssl http2;

  # The host name to respond to
  server_name example.com;

  include h5bp/ssl/ssl_engine.conf;
  include h5bp/ssl/certificate_files.conf;
  include h5bp/ssl/policy_intermediate.conf;
}
  • ssl_engine
server {
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 24h;

  keepalive_timeout 300s;

  ssl_session_tickets off;
  ssl_prefer_server_ciphers on;
}

ssl_session_cache 用于通过缓存SSL的session参数10分钟来减少昂贵的ssl握手,通过开启ssl_session缓存,我们告诉客户端去重用已经存在的session key来进行ssl连接。

1M 的缓存可以保持4000个session, 所以可以开启40000个缓存。

keepalive_timeout 增加连接的活跃时间来减少不必要的重复握手。 ssl_prefer_server_ciphers 指定在使用SSLv3和TLS协议时,服务器密码应优先于客户端密码

  • certificate_files
server {
  ssl_certificate /etc/nginx/certs/default.crt;
  ssl_certificate_key /etc/nginx/certs/default.key;
}

ssl_certificate 指定当前虚拟主机所使用的证书文件,会被发送到每个与该服务的客户端,

ssl_certificate_key是私匙,应该被存在一个读写权限控制的文件之中,同时可以被nginx master进程读取。

关于HTTPS/2

公钥是所有人都能获取到的钥匙,私钥则是服务器私自保存的钥匙。非对称加密算法中公钥加密的内容只能用私钥解密,私钥加密的内容则只有公钥才能解密。

客户端利用ssl握手得到的公钥用非对称加密算法加密出一个对称密钥给服务端,服务端用她的私钥读取对称密钥,然后就用这个对称密钥来做对称加密双方的消息。

为了防止公钥在传送过程中被调包,所以引入数字证书。TLS 的身份认证是通过证书信任链完成的,浏览器从站点证书开始递归校验父证书,直至出现信任的根证书(根证书列表一般内置于操作系统,浏览器自己维护)。站点证书是在 TLS 握手阶段,由服务端发送的。

HTTPS通信流程:

  1. 客户端发送Client Hello报文开始SSL通信,报文中包含SSL版本、可用算法列表、密钥长度等。
  2. 服务器支持SSL通信时,会以Server Hello报文作为应答,报文中同样包括SSL版本以及加密算法配置,也就是协商加解密算法。
  3. 服务端将自己的证书下发给客户端,让客户端验证自己的身份,发送Certificate报文,也就是将上述配置的ssl_certificate发送给客户端。
  4. 客户端发送Client Key Exchange报文,使用3中的证书公钥加密Pre-master secret随机密码串,后续就以这个密码来做对称加密进行通信。
  5. 服务器使用私钥解密成功后返回一个响应提示SSL通信环境已经搭建好了。然后就是常规的http c/s通信。
  • policy_intermediate
server {
  ssl_protocols TLSv1.2;
  ssl_ciphers EECDH+CHACHA20:EECDH+AES;
}

ssl_protocols 和 ssl_ciphers 用于限制TLS(传输层安全协议)指定版本和指定加密算法的链接。

HTTP/1.x 客户端需要使用多个连接才能实现并发和缩短延迟;HTTP/1.x 不会压缩请求和响应标头,从而导致不必要的网络流量;HTTP/1.x 不支持有效的资源优先级,致使底层 TCP 连接的利用率低下; 因此与 HTTP/1.1 相比,HTTP/2 的主要变化在于性能提升。HTTP 方法、状态代码、URI 和标头字段等核心概念一如往常。因此升级到HTTP2并不需要网站管理者做额外的处理。

  • 多路复用,减少tcp的连接
  • 压缩标头,
  • 服务端推送

安全

add_header 用于在http状态码非20x 或者 30x的时候向http response header添加字段,如果用了always参数,则会无视状态码。

$variables 为map定义的变量,根据content-type值变化。

http {
  # Content-Security-Policy减少跨站脚本和内容注入的攻击风险,只允许本站的脚本和其他资源运行加载
  add_header Content-Security-Policy "script-src 'self'; object-src 'self'" always;

  # No Referrer When Downgrade:仅当发生协议降级(如 HTTPS 页面引入 HTTP 资源,从 HTTPS 页面跳到 HTTP 等)时不发送 Referrer 信息。这个规则是现在大部分浏览器默认所采用的;
  add_header Referrer-Policy $referrer_policy always;

  # 它告诉浏览器所有的子域只能通过HTTPS访问当前资源,而不是HTTP
  add_header Strict-Transport-Security "max-age=16070400; includeSubDomains" always;

  # X-Content-Type-Options 响应首部相当于一个提示标志,被服务器用来提示客户端一定要遵循在 Content-Type 首部中对  MIME 类型 的设定,而不能对其进行修改。这就禁用了客户端的 MIME 类型嗅探行为
  add_header X-Content-Type-Options nosniff always;

  #  X-Frame-Options HTTP 响应头是用来给浏览器指示允许一个页面可否在 <frame>, <iframe> 或者 <object> 中展现的标记。DENY 表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。该header可用于防止点击劫持
  add_header X-Frame-Options $x_frame_options always;

  #  X-XSS-Protection 响应头是Internet Explorer,Chrome和Safari的一个功能,当检测到跨站脚本攻击 (XSS)时,浏览器将停止加载页面。
  # 1;mode=block: 启用XSS过滤。 如果检测到攻击,浏览器将不会清除页面,而是阻止页面加载。
  add_header X-XSS-Protection $x_xss_protection always;
}

http {
  # 表明是否展示response header中的Server字段中的`nginx`版本号信息。
  server_tokens off;
}

server {
  # 防止敏感文件信息的暴露
  location ~* (?:#.*#|\.(?:bak|conf|dist|fla|in[ci]|log|orig|psd|sh|sql|sw[op])|~)$ {
    deny all;
  }
}

跨域CORS

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。

其他CORSheader的含义:

Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-PINGOTHER, Content-Type Access-Control-Max-Age: 86400 Access-Control-Allow-Credentials: true

首部字段 Access-Control-Allow-Methods 表明服务器允许客户端使用 POST, GET 和 OPTIONS 方法发起请求。该字段与 HTTP/1.1 Allow: response header 类似,但仅限于在需要访问控制的场景中使用。

首部字段 Access-Control-Allow-Headers 表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type。与 Access-Control-Allow-Methods 一样,Access-Control-Allow-Headers 的值为逗号分割的列表。

最后,首部字段 Access-Control-Max-Age 表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效。

Access-Control-Allow-Credentials表示是否允许客户端发送cookie等身份认证信息,表示如果服务端的响应中缺失 Access-Control-Allow-Credentials: true信息,则浏览器不会将响应内容返回给请求的发起者。

http {
  # 所有的图片和字体文件允许跨域访问
  map $sent_http_content_type $cors {
    # Images
    image/bmp     "*";
    image/gif     "*";
    image/jpeg    "*";
    image/png     "*";
    image/svg+xml "*";
    image/webp    "*";
    image/x-icon  "*";

    # Web fonts
    font/collection               "*";
    application/vnd.ms-fontobject "*";
    font/eot                      "*";
    font/opentype                 "*";
    font/otf                      "*";
    application/x-font-ttf        "*";
    font/ttf                      "*";
    application/font-woff         "*";
    application/x-font-woff       "*";
    font/woff                     "*";
    application/font-woff2        "*";
    font/woff2                    "*";
  }

  add_header Access-Control-Allow-Origin $cors;
}

#
# Wide-open CORS config for nginx
#
location / {
  if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    #
    # Custom headers and headers various browsers *should* be OK with but aren't
    #
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
    #
    # Tell client that this pre-flight info is valid for 20 days
    #
    add_header 'Access-Control-Max-Age' 1728000;
    add_header 'Content-Type' 'text/plain; charset=utf-8';
    add_header 'Content-Length' 0;
    return 204;
  }
  if ($request_method = 'POST') {
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
  }
  if ($request_method = 'GET') {
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
  }
}

代理

server {
  listen       80;
  server_name  fe.server.com;
  location /remoteapp {
    proxy_set_header   Host             $host:$server_port;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_pass http://remoteAPIServer/;
  }

  location /api/v1/ {
    proxy_pass https://remoteAPIServer/api/v1/;
    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;
    proxy_redirect http:// https://;
  }
}

proxy_pass将会把你的请求发送到对应的被代理的服务器上。 proxy_set_header: 设置发送到服务器上的额外header. proxy_redirect: 代理重定向,将服务器上的重定向修改为https之后返回给客户端。

websocket 代理

http {
  map $http_upgrade $connection_upgrade {
      default upgrade;
      '' close;
  }
  # real websocket server
  upstream websocket {
      server 192.168.100.10:8010;
  }
  server {
      listen 8020;
      location / {
          proxy_pass http://websocket;
          proxy_http_version 1.1;
          # enable NGINX to properly handle the WebSocket protocol.
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection $connection_upgrade;
      }
  }
}

压缩

# 开启gzip压缩
gzip on;

# 压缩级别 (1-9).
# 5 is a perfect compromise between size and CPU usage, offering about
# 75% reduction for most ASCII files (almost identical to level 9).
# Default: 1
gzip_comp_level 5;

# Don't compress anything that's already small and unlikely to shrink much
# if at all (the default is 20 bytes, which is bad as that usually leads to
# larger files after gzipping).
# 最小的可压缩数据,默认是20,通常会压缩出更大的数据,所以限制为256
# Default: 20
gzip_min_length 256;

# Compress data even for clients that are connecting to us via proxies,
# identified by the "Via" header (required for CloudFront).
# Default: off
gzip_proxied any;

# Tell proxies to cache both the gzipped and regular version of a resource
# whenever the client's Accept-Encoding capabilities header varies;
# Avoids the issue where a non-gzip capable client (which is extremely rare
# today) would display gibberish if their proxy gave them the gzipped version.
# Default: off
gzip_vary on;

# 压缩的数据类型。
# text/html is always compressed by gzip module.
# Default: text/html
gzip_types
  application/atom+xml
  application/javascript
  application/json
  application/ld+json
  application/manifest+json
  application/rss+xml
  application/geo+json
  application/vnd.ms-fontobject
  application/x-web-app-manifest+json
  application/xhtml+xml
  application/xml
  application/rdf+xml
  font/otf
  application/wasm
  image/bmp
  image/svg+xml
  text/cache-manifest
  text/css
  text/javascript
  text/plain
  text/markdown
  text/vcard
  text/calendar
  text/vnd.rim.location.xloc
  text/vtt
  text/x-component
  text/x-cross-domain-policy;

缓存

map $sent_http_content_type $expires {
  default                               1M;

  # CSS
  text/css                              1y;

  # Data interchange
  application/atom+xml                  1h;
  application/rdf+xml                   1h;
  application/rss+xml                   1h;

  application/json                      0;
  application/ld+json                   0;
  application/schema+json               0;
  application/geo+json                  0;
  application/xml                       0;
  text/calendar                         0;
  text/xml                              0;

  # Favicon (cannot be renamed!) and cursor images
  image/vnd.microsoft.icon              1w;
  image/x-icon                          1w;

  # HTML
  text/html                             0;

  # JavaScript
  application/javascript                1y;
  application/x-javascript              1y;
  text/javascript                       1y;

  # Manifest files
  application/manifest+json             1w;
  application/x-web-app-manifest+json   0;
  text/cache-manifest                   0;


  # Markdown
  text/markdown                         0;

  # Media files
  audio/ogg                             1M;
  image/bmp                             1M;
  image/gif                             1M;
  image/jpeg                            1M;
  image/png                             1M;
  image/svg+xml                         1M;
  image/webp                            1M;
  video/mp4                             1M;
  video/ogg                             1M;
  video/webm                            1M;

  # WebAssembly
  application/wasm                      1y;

  # Web fonts
  font/collection                       1M;
  application/vnd.ms-fontobject         1M;
  font/eot                              1M;
  font/opentype                         1M;
  font/otf                              1M;
  application/x-font-ttf                1M;
  font/ttf                              1M;
  application/font-woff                 1M;
  application/x-font-woff               1M;
  font/woff                             1M;
  application/font-woff2                1M;
  font/woff2                            1M;

  # Other
  text/x-cross-domain-policy            1w;
}

expires $expires;

expires 用于开启或禁止ExpiresCache-Control响应头,且只能在http状态码为200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), or 308 (1.13.0)时生效,参数可以为正数或者负数。

header中Expires的值是由指定的时间和当前时间相加得到。 Cache-Control的值取决于指定的时间,如果时间为负,则cache-control被设置为no-cache,如果为0或者正数,则设置为max-age为该时间。

常用的其它时间单位

ms # 毫秒 s # 秒 m # 分钟 h # 小时 d # 天 w # 星期 M # 月(30d) y # 年(365d)

日志

常用的日志用access_log, error_log.

可以定义不同的日志格式为不同的日志类型使用:

# Default main log format from nginx repository:
log_format main
                '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for"';

# Extended main log format:
log_format main-level-0
                '$remote_addr - $remote_user [$time_local] '
                '"$request_method $scheme://$host$request_uri '
                '$server_protocol" $status $body_bytes_sent '
                '"$http_referer" "$http_user_agent" '
                '$request_time';

# Debug log formats:
log_format debug-level-0
                '$remote_addr - $remote_user [$time_local] '
                '"$request_method $scheme://$host$request_uri '
                '$server_protocol" $status $body_bytes_sent '
                '$request_id $pid $msec $request_time '
                '$upstream_connect_time $upstream_header_time '
                '$upstream_response_time "$request_filename" '
                '$request_completion';

log_format debug-level-1
                '$remote_addr - $remote_user [$time_local] '
                '"$request_method $scheme://$host$request_uri '
                '$server_protocol" $status $body_bytes_sent '
                '$request_id $pid $msec $request_time '
                '$upstream_connect_time $upstream_header_time '
                '$upstream_response_time "$request_filename" $request_length '
                '$request_completion $connection $connection_requests';

log_format debug-level-2
                '$remote_addr - $remote_user [$time_local] '
                '"$request_method $scheme://$host$request_uri '
                '$server_protocol" $status $body_bytes_sent '
                '$request_id $pid $msec $request_time '
                '$upstream_connect_time $upstream_header_time '
                '$upstream_response_time "$request_filename" $request_length '
                '$request_completion $connection $connection_requests '
                '$server_addr $server_port $remote_addr $remote_port';

最佳实践

  • 用include来组织你的配置文件
  • 分别监听80和443端口
  • 阻止未定义server names的请求连接
# Place it at the beginning of the configuration file to prevent mistakes.
server {

  # Add default_server to your listen directive in the server that you want to act as the default.
  listen 10.240.20.2:443 default_server ssl;

  # We catch invalid domain names, requests without the "Host" header and all others (also due to the above setting).
  server_name _ "" default_server;

  ...

  return 444;

  # We can also serve:
  # location / {

    # static file (error page):
    # root /etc/nginx/error-pages/404;
    # or redirect:
    # return 301 https://badssl.com;

    # return 444;

  # }

}
  • 通过map来定义大量的重定向
map $http_user_agent $device_redirect {

  default "desktop";

  ~(?i)ip(hone|od) "mobile";
  ~(?i)android.*(mobile|mini) "mobile";
  ~Mobile.+Firefox "mobile";
  ~^HTC "mobile";
  ~Fennec "mobile";
  ~IEMobile "mobile";
  ~BB10 "mobile";
  ~SymbianOS.*AppleWebKit "mobile";
  ~Opera\sMobi "mobile";

}
# 将来自移动端的请求重定向到移动站点
if ($device_redirect = "mobile") {
  return 301 https://m.domain.com$request_uri;
}
  • 尽量在server全局内使用root指令而不是为每个location block分别使用root

  • 通过设置ssl_session_cache来减少多次ssl握手带来的额外时间和资源消耗

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 24h;
ssl_session_tickets off;
ssl_buffer_size 1400;
  • 用无特权的用户来运行nginx,但需要保证该用户拥有nginx运行所需要的权限,例如读写日志文件。

但是主进程必须由root来启动,因为一般来说只有root用户才能监听小于1024的端口号(例如常用的80或者443端口)

# Edit nginx.conf:
user www-data;

# Set owner and group for root (app, default) directory:
chown -R www-data:www-data /var/www/domain.com
  • 保护敏感资源的权限不能被外部的请求随意读取
if ($request_uri ~ "/\.git") {
  return 403;
}

# or
location ~ /\.git {
  deny all;
}

# or
location ~* ^.*(\.(?:git|svn|htaccess))$ {
  return 403;
}

# or all . directories/files excepted .well-known
location ~ /\.(?!well-known\/) {
  deny all;
}
  • 隐藏上游服务器的敏感header
proxy_hide_header X-Powered-By;
proxy_hide_header X-AspNetMvc-Version;
proxy_hide_header X-AspNet-Version;
proxy_hide_header X-Drupal-Cache;
  • 只允许TSL(1.2+)协议的ssl握手连接。因为ssl协议以及TSL1.0都存在安全漏洞。TSL1.0以及TS1.1协议都会被在2020年从浏览器中移除。
ssl_protocols TLSv1.2;

# For TLS 1.3
ssl_protocols TLSv1.2 TLSv1.3;

工具

reference

https://github.com/trimstray/nginx-quick-reference#online-tools

https://github.com/h5bp/server-configs-nginx

https://github.com/fcambus/nginx-resources

understanding-the-nginx-configuration-file-structure-and-configuration-contexts

What is HTTP/2 – The Ultimate Guide

Fadingvision avatar Mar 17 '19 13:03 Fadingvision