nginx

来自百合仙子's Wiki
跳转到导航 跳转到搜索

配置

内置变量

http://nginx.org/en/docs/varindex.html

$host
主机名(无端口号)
$http_<header> :HTTP 头的值。名称使用全小写。
$scheme
$https
如果正在使用 HTTPS 访问,为on,否则为空。
$request_uri
请求的 URL 路径
$uri
正规化之后的请求的 URL 路径,可以被后续操作修改。

虚拟主机

见官方的VirtualHostExample

server_name_in_redirect指示是否根据server_name跳转到其第一个值,默认值为off[1]

使用listen 80 default_server形式指定默认虚拟主机。

日志

记录 Cloudflare 这种反代 IP 的:

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

加上请求处理时间:

log_format cflog '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent $request_time "$http_referer" "$http_user_agent" $realip_remote_addr';

HTTPS

要使用 HTTPS,在server段中这样配置[2]。其中监听 80 端口是可选的。

listen       80;
listen       443 ssl;
server_name  chat.vim-cn.com chat.edisonnotes.com;
ssl_certificate /etc/stunnel/stunnel.pem;
ssl_certificate_key /etc/stunnel/stunnel.pem;

关于证书相关信息,参见 openssl。 nginx 参数$https在使用 HTTPS 时为on,否则为空。但是位于 Cloudflare 这种反向代理后边时,需要依据其它 HTTP 头来生成。因为fastcgi_param不能在if块内使用,所以需要使用 map 模块,如下

http {
  map $http_x_forwarded_proto $cf_https {
    http  off;
    https on;
  }
  ...

  location ... {
    ...
    fastcgi_param   HTTPS           $cf_https;
  }
}

另见Nginx的访问控制/地址转向/HTTPS设置 [Lainme's Blog]

加密算法相关配置

使用 openssl 生成 DH 参数:

openssl dhparam -out dhparams.pem 2048

然后配置 nginx:[3]

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_dhparam dhparams.pem;
#ssl_ciphers ALL:!3DES:!MD5:!RC4:!aNULL:!eNULL:!MEDIUM:!LOW:!EXP;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";

重定向到 HTTPS

server {
    listen       [::]:80;
    return 301 https://$host$request_uri;
}

参见

PHP + FastCGI

PHP 公用配置:

index	index.php index.html;
location ~ (.+\.php\d?)($|/) {
        fastcgi_pass	unix:/run/php-fpm/php-fpm.sock;
        fastcgi_index	index.php;
        set	$script	$request_filename;
        if ($request_filename ~ ^(.+\.php\d?)(/.*)$){
                set $script	$1;
                set $pathinfo	$2;
        }
        fastcgi_param	PATH_INFO	$pathinfo if_not_empty;
        fastcgi_param	SCRIPT_FILENAME	$script;
        include		fastcgi_params;
}

参见 MediaWiki#Nginx 配置

PHP FastCGI 方案一

安装 php-cgi 后,可使用以下命令启动PHP:

php-cgi -b 9000

PHP FastCGI 方案二

安装 php-fpm 后修改配置文件并启动该服务即可。

PATH_INFO

通过在 location 中使用正则表达式可达成此设置

location ~ ^(.*\.py)(/.*)?$ {
    root           /home/lily;
    include        fastcgi_params;
    fastcgi_pass   unix:/tmp/sock;
    fastcgi_index  index.py;
    fastcgi_param  SCRIPT_FILENAME  $document_root$1;
    fastcgi_param  PATH_INFO        $2;
    fastcgi_param  SCRIPT_NAME      $1;
}

owncloud 的有效配置

此配置经测试在 owncloud 5.0.2 上有效。参照了官方的(有问题的)配置[4]

server {
    server_name     owncloud.localhost;
    access_log      /srv/http/localhost/logs/owncloud.access.log;
    error_log       /srv/http/localhost/logs/owncloud.error.log;
    root            /usr/share/webapps/owncloud;
    index           index.php;

    # deny direct access
    location ~ ^/(data|config|\.ht|db_structure\.xml|README) {
            deny all;
    }

    # default try order
    location / {
            try_files $uri $uri/ @webdav;
    }

    # owncloud WebDAV
    location @webdav {
            fastcgi_split_path_info ^(.+\.php\d?)(/.*)$;
            fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
    }

    # enable php
    location ~ \.php$ {
            fastcgi_split_path_info ^(.+\.php\d?)(/.*)$;
            fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
    }
}

Jappix 的有效配置

Jappix 是一个 PHP 写的 XMPP 网页客户端。

map $http_x_forwarded_proto $cf_https {
        http  off;
        https on;
}

server {
        server_name chat.example.com;
        root /var/www/jappix;
        error_log /var/log/nginx/chat.example.com_error_log;
        access_log /var/log/nginx/chat.example.com_access_log combined;
        index index.html index.htm index.php index.php4 index.php5;
        include cloudflare;

        location ~ (.+\.php\d?)($|/) {
                fastcgi_pass	unix:/var/run/php5-fpm.sock;
                fastcgi_index	index.php;
                set	$script	$request_filename;
                if ($request_filename ~ ^(.+\.php\d?)(/.*)$){
                        set $script	$1;
                        set $pathinfo	$2;
                }
                include		fastcgi_params;
                fastcgi_param	HTTPS		$cf_https;
                fastcgi_param	PATH_INFO	$pathinfo if_not_empty;
                fastcgi_param	SCRIPT_FILENAME	$script;
        }
}

server {
        server_name bosh.example.com;
        error_log /var/log/nginx/bosh.example.com_error_log;
        access_log /var/log/nginx/bosh.example.com_access_log combined;
        include cloudflare;
        location / {
                proxy_pass http://localhost:5280;
                proxy_pass_header	Server;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

CGI

有如下方案

使用 nginx-fcgi

使用 fcgiwrap 程序[5]

fcgiwrap文件[6]

fastcgi_pass	unix:/run/fcgiwrap.sock;
fastcgi_param	QUERY_STRING       $query_string;
fastcgi_param	REQUEST_METHOD     $request_method;
fastcgi_param	CONTENT_TYPE       $content_type;
fastcgi_param	CONTENT_LENGTH     $content_length;
fastcgi_param	SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param	REQUEST_URI        $request_uri;
fastcgi_param	DOCUMENT_URI       $document_uri;
fastcgi_param	DOCUMENT_ROOT      $document_root;
fastcgi_param	SERVER_PROTOCOL    $server_protocol;
fastcgi_param	GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param	SERVER_SOFTWARE    nginx;
fastcgi_param	REMOTE_ADDR        $remote_addr;
fastcgi_param	REMOTE_PORT        $remote_port;
fastcgi_param	SERVER_ADDR        $server_addr;
fastcgi_param	SERVER_PORT        $server_port;
fastcgi_param	SERVER_NAME        $server_name;
fastcgi_param	REMOTE_USER        $remote_user;

配置:

location /cgi-bin {
        root /srv/http;
        include fcgiwrap;
}

alias 指示

给本地地址取别名,类似于ApacheAlias指示。注意,有时结尾的/很重要,因为 nginx 会删除结尾的一个字符(如此处第三例)。

location /test {
	alias  /home/lilydjwg/tmpfs/;
	index  index.php;
	autoindex on;
}

也可以使用正则表达式,但要求使用捕获组

location ~ ^/test/(.*\.php)($|/) {
	alias          /home/lilydjwg/tmpfs/$1;
	fastcgi_pass   127.0.0.1:9000;
	fastcgi_index  index.php;
	fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
	include	       /etc/nginx/fastcgi_params;
}
location /path/ {
    if ($request_uri ~ ^(.*/)index\.html$) {
            rewrite ^(.*/)index\.html$ $1 permanent;
    }

    alias   /some/path/;
    autoindex on;
    index  index.html;
}

location 指示

参见官方文档

搜索顺序

  1. =开头者。如匹配则停止
  2. 字面地址,匹配最精确的优先。如果以^~开头则停止
  3. 正则表达式,按其在配置中出现的顺序
  4. If #3 yielded a match, that result is used. Otherwise, the match from #2 is used.

类型

=
精确匹配
(无)
字面字符串,开头匹配
^~
同上,但如匹配立即停止
~
正则匹配

代理

Module ngx_http_proxy_module

WebSocket

添加以下配置就可以了:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;

Authentication with reverse proxy

Here's a sample config setting with basic authentication enabled:[7]

location /couchdb/ {
    auth_basic "Restricted";
    auth_basic_user_file htpasswd;
    rewrite /couchdb/(.*) /$1 break;

    proxy_pass http://localhost:5984;
    # or should it be this in this case? --- proxy_redirect /couchdb/ /;
    proxy_pass_header Server;                                             # 将来自上游的 Server 头发送给客户端
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Scheme $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

DNS

注意proxy_pass会缓存 DNS 解析结果。要迫使 nginx 每次将重新解析,可将 URL 赋值到变量来绕过[8]

set $backend_upstream "http://dynamic.example.com:80";
proxy_pass $backend_upstream;

realip

[9]

set_real_ip_from 192.168.1.0/24;
real_ip_header X-Forwarded-For;

URL 重写

https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite

域名重定向:

# ...
server_name img.vim-cn.com elimage.edisonnotes.com;

location / {
  if ($host != 'img.vim-cn.com' ) {
    rewrite ^/(.*)$ $scheme://img.vim-cn.com/$1 permanent;
  }
  # ...
}

去掉结尾的index.html,只能使用if,否则会死循环:

location /path/ {
    if ($request_uri ~ ^(.*/)index\.html$) {
            rewrite ^(.*/)index\.html$ $1 permanent;
    }

    alias   /some/path/;
    autoindex on;
    index  index.html;
}

代理中的 URL 重写

proxy_redirect off时,应当proxy_set_header Host $http_host。否则不应该设置。

proxy_redirect第一个参数为前缀匹配。如果写proxy_redirect / /将会给没有域名部分的重定向添加上(Nginx 这边的)域名。

注意:一个请求中proxy_redirect不会被多次应用。

一个复杂的重写示例(同时进行普通 URL 重写和代理中的重写):

location /download/ {
    rewrite ^/download/name/(.*)$ /download/$1 break;
    proxy_pass   http://127.0.0.1:7474;
    proxy_redirect http://127.0.0.1:7474/download/ /download/name/;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

给 Etherpad Lite 的重写

MediaWiki 的插件 EtherEditorGoogle Chrome 浏览器下要求 wiki 和 Etherpad Lite 使用完全相同的域名(端口号也要相同;火狐不需要)。

location ~ ^/(pad/|static/|offlinemanifest\.appcache$|socket\.io/|locales\.json$|javascripts/|pluginfw/|offline\.html$) {
        rewrite ^/pad/(.*)$ /$1 break;
        proxy_pass   http://127.0.0.1:9001;
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

目录列表的编码

syntax: charset encoding|off

default: charset off

context: http, server, location, if in location

参见HttpCharsetModule

功能

gzip

gzip  on;
gzip_comp_level 5;
gzip_proxied any;
gzip_types text/plain application/javascript text/css application/xml text/javascript application/json;

注:默认未启用。启用后默认只 gzip text/html 类型。[10]

似乎没有办法针对不同的 IP 使用不同的 gzip 设置(支持在 locationif 块中使用,但是不生效)。

限速

if ($http_user_agent ~ "MSIE") {
  limit_rate 2k;
}

[11] 指定的User-Agent返回错误,如阻止据说是迅雷的UA:[12]

if ($http_user_agent ~ "Mozilla/4.0\ \(compatible;\ MSIE\ 6.0;\ Windows\ NT\ 5.1;\ SV1;\ .NET\ CLR\ 1.1.4322;\ .NET\ CLR\ 2.0.50727\)") {
  return 404;
}

防盗链

授权

location里写[13]

auth_basic "Restricted";
auth_basic_user_file htpasswd;

即可,其中htpasswd是授权文件,和 Apache 的相同,可使用 htpasswd 命令维护或者 openssl passwd -apr1 生成。

注意,据来源说这样会把授权信息传给代理。

IP 限制

allow	127.0.0.1;
allow	::1;
deny	all;

缓存代理

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=one:10m use_temp_path=off max_size=10g inactive=100d;
resolver 127.0.0.1:53;
server {
        listen 1110;
        location / {
                proxy_pass http://$http_host/$uri;
                proxy_cache one;
                proxy_cache_valid 200 302 100d;
                proxy_cache_valid 404      1m;
        }
}

添加跨站支持

比如用于重定向 Grafana 的代理请求至 Graphite 源站(需要浏览器扩展进行重定向和添加鉴权信息):

  add_header Access-Control-Allow-Origin $http_origin;
  if ( $request_method = "OPTIONS" ) {
          add_header Access-Control-Allow-Origin $http_origin;
          add_header Access-Control-Allow-Headers $http_access_control_request_headers;
          add_header Access-Control-Allow-Method "POST, OPTIONS";
          return 204;
  }

旧版本的问题

  • 1.0.x 的 realip 模块不能正确处理多个 X-Forwarded-For 头,造成无法从 HAProxy 默认配置获取到正确的源 IP。[14]
  • 1.9.13 之前的版本,对于代理默认会重发非幂等的请求[15][16]

参见

外部链接

安全相关

参考资料