Nginx 简介

Nginx 简介

Nginx(发音同 engine x)是一个异步框架的 Web 服务器,也可以用作反向代理,负载平衡器 和 HTTP 缓存。该软件由 Igor Sysoev 创建,并于 2004 年首次公开发布。同名公司成立于 2011 年,以提供支持。Nginx 是一款免费的开源软件,根据类 BSD 许可证的条款发布。一大部分 Web 服务器使用 Nginx ,通常作为负载均衡器。

Nginx 的特点

  • 更快:
    • 单次请求会得到更快的响应。
    • 在高并发环境下,Nginx 比其他 WEB 服务器有更快的响应。
  • 高扩展性:
    • Nginx 是基于模块化设计,由多个耦合度极低的模块组成,因此具有很高的扩展性。许多高流量的网站都倾向于开发符合自己业务特性的定制模块。
  • 高可靠性:
    • Nginx 的可靠性来自于其核心框架代码的优秀设计,模块设计的简单性。另外,官方提供的常用模块都非常稳定,每个 worker 进程相对独立,master 进程在一个 worker 进程出错时可以快速拉起新的 worker 子进程提供服务。
  • 低内存消耗:
    • 一般情况下,10000 个非活跃的 HTTP Keep-Alive 连接在 Nginx 中仅消耗 2.5MB 的内存,这是 Nginx 支持高并发连接的基础。
    • 单机支持 10 万以上的并发连接:理论上,Nginx 支持的并发连接上限取决于内存,10 万远未封顶。
  • 热部署:
    • master 进程与 worker 进程的分离设计,使得 Nginx 能够提供热部署功能,即在 7x24 小时不间断服务的前提下,升级 Nginx 的可执行文件。当然,它也支持不停止服务就更新配置项,更换日志文件等功能。
  • 最自由的 BSD 许可协议:
    • 这是 Nginx 可以快速发展的强大动力。BSD 许可协议不只是允许用户免费使用 Nginx ,它还允许用户在自己的项目中直接使用或修改 Nginx 源码,然后发布。

你可能需要掌握的

  • Linux 服务器和一些常用的操作命令
  • 域名,当然如果是本地玩玩也可以是 Hosts
  • 基本的正则表达式

常用工具

常见变量

这里列出常用的 Nginx 全局变量,也可以点击 实时查看 浏览。

服务器相关

  • nginx_version:1.11.2

当前运行的 Nginx 版本号

  • server_port:8080

服务器端口

  • server_addr:127.0.0.1

服务器端地址

  • server_name:127.0.0.1

服务器名称

  • server_protocol:HTTP/1.0

服务器的 HTTP 版本

  • status:200

HTTP 响应代码

  • time_iso8601:2018-09-02T15:14:27+08:00

服务器时间的 ISO 8610 格式

  • time_local:02/Sep/2018:15:14:27 +0800

服务器时间(LOG Format 格式)

  • document_root:/home/xiaowu/github/echo.xuexb.com

当前请求的文档根目录或别名

  • request_filename:/home/xiaowu/github/echo.xuexb.com/api/dump/path

当前连接请求的文件路径,由 rootalias 指令与 URI 请求生成

  • request_completion:``

如果请求成功,值为”OK”,如果请求未完成或者请求不是一个范围请求的最后一部分,则为空

  • pid:1234

工作进程的 PID

  • msec:1535872750.954

当前的 Unix 时间戳

  • limit_rate:0

用于设置响应的速度限制

  • pipe:.

如果请求来自管道通信,值为“p”,否则为“.”

  • connection_requests:1

TCP 连接当前的请求数量

  • connection:363861

TCP 连接的序列号

  • realpath_root:/home/xiaowu/github/echo.xuexb.com

当前请求的文档根目录或别名的真实路径,会将所有符号连接转换为真实路径

  • hostname:bj01

主机名

链接相关

  • scheme:http

请求使用的 WEB 协议

  • uri:/api/dump/path

请求中的当前 URI(不带请求参数),可以不同于浏览器传递的 $request_uri 的值,它可以通过内部重定向,或者使用 index 指令进行修改

  • document_uri:/api/dump/path

$uri

  • request_uri:/api/dump/path?a=1&%E4%B8%AD%E6%96%87=%E5%A5%BD%E7%9A%84

这个变量等于包含一些客户端请求参数的原始 URI ,它无法修改

  • request_method:GET

HTTP 请求方法

  • request_time:0.000

处理客户端请求使用的时间,从读取客户端的第一个字节开始计时

  • request_length:678

请求的长度(包括请求地址、请求头和请求主体)

  • args:a=1&%E4%B8%AD%E6%96%87=%E5%A5%BD%E7%9A%84

请求参数

  • query_string:``

$args

  • is_args:?

请求中是否有参数,有则为 ? 否则为空

  • arg_参数名:$arg_a => 1

请求中具体的参数

  • https:on

如果开启了 SSL 安全模式,则为 on 否则为空

客户端相关

  • http_accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

浏览器支持的 MIME 类型

  • http_accept_encoding:gzip, deflate, br

浏览器支持的压缩编码

  • http_accept_language:zh-CN,zh;q=0.9,en;q=0.8

浏览器支持的语言

  • http_cache_control:max-age=0

浏览器缓存

  • http_connection:``

客户端与服务连接类型

  • http_cookie:a=1; b=2

浏览器请求 cookie

  • http_host:echo.xuexb.com

浏览器请求 host

  • http_referer:https://echo.xuexb.com

浏览器来源

  • http_upgrade_insecure_requests:1

是一个请求首部,用来向服务器端发送信号,表示客户端优先选择加密及带有身份验证的响应,并且它可以成功处理 upgrade-insecure-requests CSP 指令

  • http_user_agent : Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36

用户设备标识

  • http_x_requested_with:true

异步请求标识

  • http_x_forwarded_for:198.13.61.105

反向代理 IP 组标识

主域 301 重定向

你的网站可能有多个域名访问,比如:www.wulicode.comwulicode.com 等,设置主域意思是不管用户输入哪个域名,都会 301 重定向到主域上,设置主域可以对 SEO 更友好,比如:

以 wulicode.com 为主域

1
2
3
www.wulicode.com => wulicode.com
www.wulicode.com/search/xxoo => wulicode.com/search/xxoo
www.wulicode.com/a/b/c/404.html => wulicode.com/a/b/c/404.html

这里把所有的 80 端口匹配域名都重新定向到 https 访问

domain.com.conf

1
2
3
4
5
6
7
8
9
server {
listen 80;

# 配置域名
server_name domain.com www.domain.com;

# 配置让这些 HTTP 的访问全部 301 重定向到 HTTPS 的
return 301 https://domain.com$request_uri;
}

nginx 优化连接数

未优化之前的连接数 NotEstablished 的数量到达了一定的峰值, 然后处于持平状态, 在这个时候服务器日志会报 500 错误

这个地方考虑到的可能性是链接数过多导致的服务器

/etc/sysctl.conf

1
net.ipv4.tcp_max_tw_buckets = 8000

nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
...
worker_processes 32;
worker_rlimit_nofile 65535;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;


events {
worker_connections 10240;
}
...

优化之后的数据

常见问题

不记录资源请求

1
2
3
4
5
6
server {
// static file log off
location ~* \.(ico|jpg|jpeg|gif|png|swf|flv|js|css)$ {
access_log off;
}
}

默认 IP 不允许访问网站

conf.d/default.conf

1
2
3
4
server {
listen 80 default;
return 404;
}

nginx 错误 - ERR_CONTENT_LENGTH_MISMATCH

问题说明

错误详细 :
GET http://sentry.liex.sour-lemon.com/_static/1594691975/sentry/dist/app.js net::ERR_CONTENT_LENGTH_MISMATCH 200 (OK)
GET http://sentry.liex.sour-lemon.com/_static/1594691975/sentry/dist/vendor.js net::ERR_CONTENT_LENGTH_MISMATCH 200 (OK)
Uncaught ReferenceError: $ is not defined

查看日志

2020/07/14 11:32:23 [crit] 16348#0: *1508 open() “/var/lib/nginx/tmp/proxy/3/28/0000000283” failed (13: Permission denied) while reading upstream, client: 60.216.119.220, server: sentry.liex.sour-lemon.com, request: “GET /_static/1594691975/sentry/dist/app.js HTTP/1.1”, upstream: “http://127.0.0.1:9080/_static/1594691975/sentry/dist/app.js", host: “sentry.liex.sour-lemon.com”, referrer: “http://sentry.liex.sour-lemon.com/join-request/sentry/"

解决方案

1
$ sudo chmod -R 775 /var/lib/nginx/

日志切割

关于日志切割参考 运维中的日志切割(logrotate)

nginx: [error] invalid PID number “” in “/run/nginx.pid”

先执行

1
$ nginx -c /etc/nginx/nginx.conf

nginx.conf 文件的路径可以从 nginx -t 的返回中找到。

1
$ nginx -s reload

不记录 404 日志

nginx 有个选项 log_not_found 用来开启和禁用未发现的文件日志, 可用区块是 http, server, location

1
2
3
4
5
6
server {
location = /favicon.ico {
log_not_found off;
access_log off;
}
}

配置不生效

更新配置后使用 [sudo] /your/path/nginx -s reload 热重载服务。

getpwnam(“nginx”) failed

表示该用户 nginx 不存在, 解决方法:

  1. nginx.conf 里添加 user nobody;
  2. 创建用户和用户对应的分组

getgrnam(“xiaowu”) failed

表示用户分组不存在,解决方法:

  1. nginx.conf 里添加 user nobody;
  2. 创建用户对应的分组

could not open error log file: open() “/logs/error.log” failed (13: Permission denied)

启动 Nginx 的用户权限不够导致无法写入日志文件,常见于非 root 用户启动报错。

bind() to 0.0.0.0:80 failed (48: Address already in use)

80 端口被占用启动失败,修改端口或者杀死占用者再启动即可。

open() “nginx.pid” failed (2: No such file or directory)

pid 进程 id 文件不存在,可能文件被删除或者已经停止,在停止 Nginx 时会使用该进程 id,如果不存在将失败,可以手动 kill 掉。

unknown “realpath_roots_xxx” variable

变量 $realpath_roots_xxx 不存在

“add_header” directive is not allowed here in xx

add_header 指令不能直接在 if 判断内,可以在 httpserverserver.locationserver.location.if 下。

“proxy_pass” cannot have URI part in location given by regular expression, or inside named location, or inside “if” statement, or inside “limit_except” block in xx

这是 proxy_pass 指令在正则匹配时不能使用包含路径的链接,如以下都会报错:

1
2
3
4
5
6
7
8
9
location ~* ^/api/ {
proxy_pass http://host/;
}

location / {
if ($uri ~* ^/api/) {
proxy_pass http://host/;
}
}

解决办法就是把 / 去掉,更多关于 proxy_pass 请看:proxy_pass 技巧

参考

[转] 利用 nginx 来屏蔽指定的 user_agent 的访问

原文地址 : 利用 nginx 来屏蔽指定的 user_agent 的访问

对于做国内站的我来说,我不希望国外蜘蛛来访问我的网站,特别是个别垃圾蜘蛛,它们访问特别频繁。这些垃圾流量多了之后,严重浪费服务器的带宽和资源。通过判断 user agent,在 nginx 中禁用这些蜘蛛可以节省一些流量,也可以防止一些恶意的访问。

1、进入 nginx 的配置目录,例如 cd /usr/local/nginx/conf

2、添加 agent_deny.conf 配置文件 vim agent_deny.conf

加入以下

1
2
3
4
5
6
7
8
9
10
11
12
13
# 禁止Scrapy等工具的抓取
if ($http_user_agent ~* (Scrapy|Curl|HttpClient)) {
return 403;
}
# 禁止指定UA及UA为空的访问
if ($http_user_agent ~ "FeedDemon|JikeSpider|Indy Library|Alexa Toolbar|AskTbFXTV|AhrefsBot|CrawlDaddy|CoolpadWebkit|Java|Feedly|UniversalFeedParser|ApacheBench|Microsoft URL Control|Swiftbot|ZmEu|oBot|jaunty|Python-urllib|lightDeckReports Bot|YYSpider|DigExt|YisouSpider|HttpClient|MJ12bot|heritrix|EasouSpider|LinkpadBot|Ezooms|^$" )
{
return 403;
}
#禁止非GET|HEAD|POST方式的抓取
if ($request_method !~ ^(GET|HEAD|POST)$) {
return 403;
}

还有加一些针对特殊的 user_agent 的访问

1
2
3
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;
}

这个是如何得出是频繁访问的 user_agent 呢,通过分析 nginx 的日志可以得出

1
tail -n 1000 /usr/local/nginx/logs/access.log | awk -F\" '{A[$(NF-1)]++}END{for(k in A)print A[k],k}' | sort -n | tail 分析访问次数

执行以上命令可以得出访问最多的 user_agent,通过人为判断是否正常来屏蔽

然后在 nginx.conf 的 location 中加入 include agent_deny.conf;

平滑重启 nginx

1
/usr/local/nginx/sbin/nginx –s reload

然后测试一下 设置是否成功

1
curl -I -A "BaiduSpider" www.test.com
1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Server: nginx
Date: Mon, 09 Feb 2015 03:37:20 GMT
Content-Type: text/html; charset=UTF-8 Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/5.5.19 Vary: Accept-Encoding, Cookie
Cache-Control: max-age=3, must-revalidate
WP-Super-Cache: Served supercache file from PHP
1
curl -I -A "JikeSpider" www.test.com
1
2
3
4
HTTP/1.1 403 Forbidden
Server: nginx Date: Mon, 09 Feb 2015 03:37:44 GMT
Content-Type: text/html
Content-Length: 162 Connection: keep-alive

到这里,nginx 通过判断 User-Agent 屏蔽蜘蛛访问网站就已经完成,可以根据实际情况对 agent_deny.conf 中的蜘蛛进行增加、删除或者修改。

Nginx 编译参数

命令

nginx -V
查看当前已安装版本的编译参数

src/nginx-x.x.x/configure --help

显示提示信息

参数详情

参数名称 备注
--prefix=value 指向安装目录
--sbin-path 指向(执行)程序文件(nginx
--conf-path= 指向配置文件(nginx.conf
--error-log-path= 指向错误日志目录
--pid-path= 指向 pid 文件(nginx.pid
--lock-path= 指向 lock 文件(nginx.lock)(安装文件锁定,防止安装文件被别人利用,或自己误操作。)进程 ID 文件
--user= 指定程序运行时的用户名
--group= 指定程序运行时的用户组名
--builddir= 指向编译目录
--with-rtsig_module 启用 rtsig 模块支持(实时信号)
--with-select_module 启用 select 模块支持(一种轮询模式,不推荐在高载环境下使用)禁用:--without-select_module
--with-poll_module 启用 poll 模块支持(功能与 select 相同,与 select 特性相同,为一种轮询模式,不推荐在高载环境下使用)
--with-file-aio 启用 file aio 支持(一种 APL 文件传输格式)
--with-ipv6 启用 ipv6 支持
--add-module= 启用外部模块支持
--with-cc= 指向 C 编译器路径
--with-cpp= 指向 C 预处理路径
--with-cc-opt= 设置 C 编译器参数
--with-ld-opt= 设置连接文件参数
--with-cpu-opt= 指定编译的 CPU,可用的值为:pentium, pentiumpro, pentium3, pentium4, athlon, opteron, amd64, sparc32, sparc64, ppc64
--without-pcre 禁用 PCRE 库
--with-pcre 启用 PCRE 库
--with-pcre= 指向 PCRE 库文件目录
--with-pcre-opt= 在编译时为 PCRE 库设置附加参数
--with-md5= 指向 MD5 库文件目录(消息摘要算法第五版,用以提供消息的完整性保护)
--with-md5-opt= 在编译时为 MD5 库设置附加参数
--with-md5-asm 使用 MD5 汇编源
--with-sha1= 指向 sha1 库目录(数字签名算法,主要用于数字签名)
--with-sha1-opt= 在编译时为 sha1 库设置附加参数
--with-sha1-asm 使用 sha1 汇编源
--with-perl= 设定 perl 库文件路径
--with-zlib= 指向 zlib 库目录
--with-zlib-opt= 在编译时为 zlib 设置附加参数
--with-zlib-asm= 为指定的 CPU 使用 zlib 汇编源进行优化,CPU 类型为 pentium, pentiumpro
--with-libatomic 为原子内存的更新操作的实现提供一个架构
--with-libatomic= 指向 libatomic_ops 安装目录
--with-openssl= 指向 openssl 安装目录
--with-openssl-opt 在编译时为 openssl 设置附加参数
--with-debug 启用 debug 日志
--with-http_ssl_module 启用 ngx_http_ssl_module 支持(使支持 HTTPS 请求,需已安装 openssl)
--with-http_realip_module 启用 ngx_http_realip_module 支持(这个模块允许从请求标头更改客户端的 IP 地址值,默认为关)
--with-http_addition_module 启用 ngx_http_addition_module 支持(作为一个输出过滤器,支持不完全缓冲,分部分响应请求)
--with-http_xslt_module 启用 ngx_http_xslt_module 支持(过滤转换 XML 请求)
--with-http_image_filter_module 启用 ngx_http_image_filter_module 支持(传输 JPEG/GIF/PNG 图片的一个过滤器)(默认为不启用。GD 库要用到)
--with-http_geoip_module 启用 ngx_http_geoip_module 支持(该模块创建基于与 MaxMind GeoIP 二进制文件相配的客户端 IP 地址的 ngx_http_geoip_module 变量)
--with-http_sub_module 启用 ngx_http_sub_module 支持(允许用一些其他文本替换 Nginx 响应中的一些文本)
--with-http_dav_module 启用 ngx_http_dav_module 支持(增加 PUT、DELETE、MKCOL 创建集合,COPY 和 MOVE 方法)默认情况下为关闭,需编译开启
--with-http_flv_module 启用 ngx_http_flv_module 支持(提供寻求内存使用基于时间的偏移量文件)
--with-http_gzip_static_module 启用 ngx_http_gzip_static_module 支持(在线实时压缩输出数据流)
--with-http_random_index_module 启用 ngx_http_random_index_module 支持(从目录中随机挑选一个目录索引)
--with-http_secure_link_module 启用 ngx_http_secure_link_module 支持(计算和检查要求所需的安全链接网址)
--with-http_degradation_module 启用 ngx_http_degradation_module 支持(允许在内存不足的情况下返回 204 或 444 码)
--with-http_stub_status_module 启用 ngx_http_stub_status_module 支持(获取 Nginx 自上次启动以来的工作状态)
--without-http_charset_module 禁用 ngx_http_charset_module 支持(重新编码 WEB 页面,但只能是一个方向–服务器端到客户端,并且只有一个字节的编码可以被重新编码)
--without-http_gzip_module 禁用 ngx_http_gzip_module 支持(该模块同 --with-http_gzip_static_module 功能一样)
--without-http_ssi_module 禁用 ngx_http_ssi_module 支持(该模块提供了一个在输入端处理处理服务器包含文件(SSI)的过滤器,目前支持 SSI 命令的列表是不完整的)
--without-http_userid_module 禁用 ngx_http_userid_module 支持(该模块用来处理用来确定客户端后续请求的 cookie
--without-http_access_module 禁用 ngx_http_access_module 支持(该模块提供了一个简单的基于主机的访问控制。允许/拒绝基于 IP 地址)
--without-http_auth_basic_module 禁用 ngx_http_auth_basic_module(该模块是可以使用用户名和密码基于 HTTP 基本认证方法来保护你的站点或其部分内容)
--without-http_autoindex_module 禁用 ngx_http_autoindex_module 支持(该模块用于自动生成目录列表,只在 ngx_http_index_module 模块未找到索引文件时发出请求。)
--without-http_geo_module 禁用 ngx_http_geo_module 支持(创建一些变量,其值依赖于客户端的 IP 地址)
--without-http_map_module 禁用 ngx_http_map_module 支持(使用任意的键/值对设置配置变量)
--without-http_split_clients_module 禁用 ngx_http_split_clients_module 支持(该模块用来基于某些条件划分用户。条件如:ip 地址、报头、cookies 等等)
--without-http_referer_module 禁用 ngx_http_referer_module 支持(该模块用来过滤请求,拒绝报头中 Referer 值不正确的请求)
--without-http_rewrite_module 禁用 ngx_http_rewrite_module ,链接重写
--without-http_proxy_module 禁用 ngx_http_proxy_module 支持(有关代理服务器)
--without-http_fastcgi_module 禁用 ngx_http_fastcgi_module 支持(该模块允许 Nginx 与 FastCGI 进程交互,并通过传递参数来控制 FastCGI 进程工作。 )FastCGI 一个常驻型的公共网关接口。
--without-http_uwsgi_module 禁用 ngx_http_uwsgi_module 支持(该模块用来医用 uwsgi 协议,uWSGI 服务器相关)
--without-http_scgi_module 禁用 ngx_http_scgi_module 支持
--without-http_memcached_module 禁用 ngx_http_memcached_module 支持(该模块用来提供简单的缓存,以提高系统效率)
-without-http_limit_zone_module 禁用 ngx_http_limit_zone_module 支持(该模块可以针对条件,进行会话的并发连接数控制)
--without-http_limit_req_module 禁用 ngx_http_limit_req_module 支持(该模块允许你对于一个地址进行请求数量的限制用一个给定的 session 或一个特定的事件)
--without-http_empty_gif_module 禁用 ngx_http_empty_gif_module 支持(该模块在内存中常驻了一个 1*1 的透明 GIF 图像,可以被非常快速的调用)
--without-http_browser_module 禁用 ngx_http_browser_module 支持
--without-http_upstream_ip_hash_module 禁用 ngx_http_upstream_ip_hash_module 支持(该模块用于简单的负载均衡)
--with-http_perl_module 启用 ngx_http_perl_module 支持(该模块使 nginx 可以直接使用 perl 或通过 ssi 调用 perl)
--with-perl_modules_path= 设定 perl 模块路径
--http-log-path= 设定 access log 路径
--http-client-body-temp-path= 设定 HTTP 客户端请求临时文件路径
--http-proxy-temp-path= 设定 HTTP 代理临时文件路径
--http-fastcgi-temp-path= 设定 HTTP Fastcgi 临时文件路径
--http-uwsgi-temp-path= 设定 HTTP uwsgi 临时文件路径
--http-scgi-temp-path= 设定 HTTP scgi 临时文件路径
--without-http 禁用 HTTP server 功能
--without-http-cache 禁用 HTTP Cache 功能
--with-mail 启用 POP3/IMAP4/SMTP 代理模块支持
--with-mail_ssl_module 启用 ngx_mail_ssl_module 支持
--without-mail_pop3_module 禁用 POP3 协议
--without-mail_imap_module 禁用 IMAP 协议
--without-mail_smtp_module 禁用 SMTP 协议
--with-google_perftools_module 启用 ngx_google_perftools_module 支持(调试用,剖析程序性能瓶颈)
--with-cpp_test_module 启用 ngx_cpp_test_module 支持

部分摘自:http://www.ttlsa.com/nginx/nginx-configure-descriptions/

配置图片防盗链

防盗链是指当图片不是自己网站打开时返回 403 或者指定图片,是通过请求的来路判断是否是自己的站点来设置响应。

语法

valid_referers none | blocked | server_names | string

  • none:表示没有来路
  • blocked:表示有来路
  • server_names:来路里包含当前域名
  • string:(忽略端口)
    • 如果是字符串:一个域名验证的规则,* 表示通配符
    • 如果是以 ~ 开头:正则表达式,排除 https://或 http://开头的字符串

以上参数可以叠加一起使用。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
server {

# 配置所有图片
location ~* \.(gif|jpg|png|bmp)$ {
# 验证可以是没有来路、或者有来路时来路匹配xuexb.com、或者匹配当前域名
valid_referers none blocked *.xuexb.com server_names;

# 如果验证不通过则返回403
if ($invalid_referer) {
return 403;
}
}
}

配置泛域名转发

有的时候,我们需要配置一些自定义的子域名,如:

  • xuexb.user.demo.com
  • a01.user.demo.com

这时候就需要域名的 DNS 解析一个泛域名 *.user.demo.com 到服务器,Nginx 可以配置如下:

子域名转发到子目录

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
server_name ~^([\w-]+)\.user\.demo\.com$;

location / {
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_set_header X-NginX-Proxy true;
proxy_pass http://127.0.0.1:8080/$1$request_uri;
}
}

以上配置表示:

  • xuexb.user.demo.com/path?a=1 -> 127.0.0.1:8080/xuexb/path?a=1
  • a01.user.demo.com/path?a=1 -> 127.0.0.1:8080/a01/path?a=1

这样后端就可以根据子目录解析不同的规则,甚至 Nginx 可以再进行链接重写。

子域名配置不同的目录

1
2
3
4
5
6
server {
listen 80;
server_name ~^([\w-]+)\.user\.demo\.com$;

root /home/user/wwwroot/user/$1;
}

以上配置可以把不同的子域名分发到不同的目录中,做到路径分离的功能,如:

  • xuexb.user.demo.com -> /home/user/wwwroot/user/xuexb;
  • a01.user.demo.com -> /home/user/wwwroot/user/a01;

OpenResty 介绍

OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

OpenResty® 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。

OpenResty® 的目标是让你的 Web 服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。

详细文档参考 : https://openresty.org/cn/

优点

  • 自带编译 echo 模块, 可以进行服务器的各种输出调试

使用 nginx-http-concat

nginx-http-concat 是一个 Nginx 扩展模块,用来合并 HTTP 请求。

1. 下载

访问 https://github.com/alibaba/nginx-http-concat/releases 官网下载最新的源包,如:

1
2
3
4
5
# 下载
wget https://github.com/alibaba/nginx-http-concat/archive/1.2.2.tar.gz

# 解压并记录解压后的目录
tar xzf 1.2.2.tar.gz

2. 安装

使用 编译安装 ,在配置 configure 时添加参数:

1
2
3
4
5
6
# 配置
./configure 其他编译参数 --add-module=/刚才解压的目录

# 安装
make
[sudo] make install

注意: 如果是重新编译安装时不要运行 make install,可参数: 重新编译安装

如我的配置:

1
2
3
./configure
--...
--add-module=/home/work/src/nginx-http-concat-1.2.2

如果有多个 --add-module 分别对应写上即可

3. 配置

使用 location 匹配到你想要匹配的路径,对其设置参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
location /static/css/ {
concat on;
concat_types text/css;
concat_max_files 20;
}

location /static/js/ {
concat on;
concat_types application/javascript;
concat_max_files 30;
}
}

重启服务,如: nginx -s reload

更新配置项点击: https://github.com/alibaba/nginx-http-concat#module-directives

4. 使用

现在就可以通过 url 中的??来合并了,比如: /static/css/??a.css,path/b.css

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). The value can contain variables.

There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level.

If the always parameter is specified (1.7.5), the header field will be added regardless of the response code.

意思也就是说话在响应状态码成功时,add_header 指令才生效,并且当前《作用域》下没有 add_header 指令时,会向上层继承。

在使用过程中难免会遇到上级指令被覆盖的情况,如:

1
2
3
4
5
6
7
8
9
10
11
server {
add_header x-name nginx;

location / {
root /path;
}

location /static/ {
add_header x-name2 nginx2;
}
}

当匹配到 / 时,由于 location / 中没有 add_header 指令,所以会继承 server 中的 x-name ,而当匹配到 /static/ 时,由于内容已经有 add_header 指令,则上层的 x-name 不会被继承,导致只会输出 x-name2

使用 include 语法

因为项目中的应用往往配置会有很多,我们可以把具体的功能拆分成独立的配置文件,使用 include 引用进来,如:

  • inc/no-cache.conf - 无缓存
  • inc/cache-max.conf - 缓存最大
  • inc/sts.conf - STS
  • inc/xss.conf - XSS 安全过滤
  • inc/php.conf - PHP FastCGI

这样就可以按需引用了,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
location / {
include inc/sts.conf;
include inc/security.conf;
include vhost/xiaoshuo.io/no-cache.conf;
}

# png,jpg 转 webp
location ~* \.(jpg|png|meibanfawojiuxiangchangdianyirangquanzhongdadian)$ {
include inc/sts.conf;
include inc/security.conf;
include vhost/xiaoshuo.io/cache-max.conf;

if ($cookie_webp = '1') {
rewrite ^/(.*)$ /$1.webp last;
}
}
location ~* \.webp$ {
include inc/sts.conf;
include inc/security.conf;
include vhost/xiaoshuo.io/cache-max.conf;
try_files $uri $uri/ @webp;
}
location @webp {
content_by_lua_file 'lua/webp.lua';
}

# http-concat + max cache
location /style/js/ {
include inc/sts.conf;
include inc/security.conf;
include vhost/xiaoshuo.io/cache-max.conf;
concat on;
concat_types application/javascript;
concat_max_files 30;
default_type application/javascript;
}
location /style/css/ {
include inc/sts.conf;
include inc/security.conf;
include vhost/xiaoshuo.io/cache-max.conf;
concat on;
concat_types text/css;
concat_max_files 30;
default_type text/css;
}

# 时效性高的接口
location ~* ^/((so\/(.+))||ajax)\.php$ {
include inc/sts.conf;
include inc/security.conf;
include vhost/xiaoshuo.io/php.conf;
add_header cache-control 'private, max-age=0, no-cache';
}

# 其实所有的 php
location ~* \.php$ {
set $skip_cache 0;
if ($request_method = POST) {
set $skip_cache 1;
}
if ($arg_nocache = "1") {
set $skip_cache 1;
}

fastcgi_cache_key $scheme$request_method$host$request_uri;
fastcgi_cache_use_stale
error
timeout
invalid_header
updating
http_500
http_503;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache_valid 200 5m;
fastcgi_cache_valid any 1m;
fastcgi_cache m_xiaoshuo_php_cache;

add_header x-php-cache-status $upstream_cache_status;
include inc/sts.conf;
include inc/security.conf;
include vhost/xiaoshuo.io/cache-small.conf;
include vhost/xiaoshuo.io/php.conf;
}

Nginx 的静态页面刷新\_缓存的解决方案

mockplus 的技术方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Request
Request URL: https://share.mockplus.cn/go/G5XU3GW4MDTC8U4G/resources/scripts/jquery-1.7.1.min.js
Request Method: GET
Status Code: 200 OK
Remote Address: 121.40.151.193:443
Referrer Policy: no-referrer-when-downgrade

# Response
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/javascript; charset=utf-8
Date: Wed, 29 Aug 2018 14:54:56 GMT
Server: nginx/1.12.2
Transfer-Encoding: chunked

分析: 没开启 Etag, 没有告知浏览器进行缓存, 且每次请求(开启缓存情况下)都是 200 正确响应. 重新从服务端获取数据. 对服务器压力大

netlify 的技术方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# request
Request URL: https://eloquent-almeida-b21318.netlify.com/resources/scripts/jquery-1.7.1.min.js
Request Method: GET
Status Code: 304
Remote Address: 35.189.132.21:443
Referrer Policy: no-referrer-when-downgrade
# response
cache-control: public, max-age=0, must-revalidate
date: Wed, 29 Aug 2018 14:49:01 GMT
etag: "538863031fcda87f03398bbc78c06c81-ssl-df"
server: Netlify
status: 304
vary: Accept-Encoding
x-nf-request-id: 6a89484b-8029-4059-aea9-08991fe66ab6-11566228

分析: 启用 Etag, 告知浏览器没有内容更新时候获取本地存储,只是从服务端获取状态
缓存处理方式:
可缓存性 : [public] 表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存。
到期 : [max-age=] 设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)。与 Expires 相反,时间是相对于请求的时间。
重新验证和重新加载 : [must-revalidate] 缓存必须在使用之前验证旧资源的状态,并且不可使用过期资源。

解决方案

我们采用 netlify 的解决方案

  1. 开启 ETag
  2. 缓存机制采用可缓存, 服务端验证方式
1
2
3
4
5
6
7
8
9
10
11
12
# request
Request URL: http://.../ne14ke/resources/scripts/jquery-1.7.1.min.js
Request Method: GET
Status Code: 304 Not Modified
Remote Address: 139.129.212.119:80
Referrer Policy: no-referrer-when-downgrade
# response
Cache-Control: public, max-age=0, must-revalidate
Connection: keep-alive
ETag: "5b86b33d-16ed9"
Last-Modified: Wed, 29 Aug 2018 14:52:45 GMT
Server: nginx/1.12.2

当文件改变之后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# request
Request URL: http://.../.../jquery-1.7.1.min.js
Request Method: GET
Status Code: 200 OK
Remote Address: 139.129.212.119:80
Referrer Policy: no-referrer-when-downgrade
Accept-Ranges: bytes
# response
Cache-Control: public, max-age=0, must-revalidate
Connection: keep-alive
Content-Length: 93924
Content-Type: application/javascript
ETag: "5b86b5e2-16ee4"
Last-Modified: Wed, 29 Aug 2018 15:04:02 GMT
Server: nginx/1.12.2

我们可以看到又重新进行了请求(200), 并且采用了 ETag (重新生成), 目的达到.

技术点配置

nginx.conf

1
2
# 开启 etag
etag on;

server{}

1
2
3
4
5
6
# 服务器设置/缓存控制
location ~ .*\.(js|css|png|svg)?$ {
# 取消缓存
# 告知浏览器处理缓存的方式
add_header Cache-Control "public, max-age=0, must-revalidate";
}

统计 Nginx 访问量

PV(Page View):即页面浏览量或者点击量,用户每一次对网站中每个页面访问均记录 1 个 PV。用户对同一页面的多次访问,访问量累积。

UV(Unique Visitor):指通过互联网浏览这个网页的人,电脑称为一个访客、手机也称为一个访客,一天之内相同的客户端只能被计算一次。

IP(Internet Protocol):指独立 IP 访问站点的 IP 总数,一天内相同 IP 只能算一次。

VV(Visit View):指所有访客一天内访问网站的次数,当访客完成所有浏览并最终关闭网站的所有页面时变完成了一次访问,同一访客一天内可能有多次访问行为,访问次数累积。

接下来看一下 Nginx 的配置文件:

/etc/nginx/nginx.conf

再看一下 access.log:

/var/log/nginx/access.log

access.log 文件里面的$remote_addr、$remote_user…等

查看各个访问量:

1.根据访问 IP 统计 UV

1
awk '{print $1}'  /var/log/nginx/access.log|sort | uniq -c |wc -l

2.统计访问 URL 统计 PV

1
awk '{print $7}' /var/log/nginx/access.log|wc -l

3.查询访问最频繁的 URL

1
awk '{print $7}' /var/log/nginx/access.log|sort | uniq -c |sort -n -k 1 -r|more

4.查询访问最频繁的 IP

1
awk '{print $1}' /var/log/nginx/access.log|sort | uniq -c |sort -n -k 1 -r|more

5.根据时间段统计查看日

1
cat  /var/log/nginx/access.log| sed -n '/14/Mar/2017:21/,/14/Mar/2017:22/p'|more

针对每天的访问信息写一个脚本,并将统计信息输出到/pv.html 文件里面,之保留 30 天的信息。方便直接浏览此页面查看,但要限制特定 IP 才能访问此页面,其他 IP 的 403!

1
2
3
4
5
6
7
year=`date +%Y`
month=`date +%m`
datedate=`date +%F`
date=`date +%Y%m%d`
pv=`awk '{print $7}' /xx/logs/nginx/xxx/"$year"/"$month"/"$datedate"-access.log | wc -l`
ip=`awk '{print $1}' /xx/logs/nginx/xxx/"$year"/"$month"/"$datedate"-access.log | sort -n | uniq -c | wc -l`
echo -e "\n$date Pv is $pv;; $date Ip is $ip.." >> /xx/xxx/pv.htm l sort -rn /xx/xxx/pv.html | sed -i '31d' /xx/xxx/pv.html | sort -r

此外还要修改 nginx 配置文件:

1
2
3
4
5
6
location = /pv.html {
allow  xxx.xxx.xxx.xxx;
deny allow;
}
nginx -r
dervice nginx reload

最后,将 pv.sh 加入定时任务:

1
crontab -e

Node.js 反向代理

服务端如果使用nodejs运行服务,由于端口不能同时多个服务占用,而服务器中可能又是多个网站,那么可以使用 Nginx 做反向代理,比如有这些网站域名和端口:

域名 端口
www.xxoo.com 8001
www.xo.com 8002
www.xo.cn 8003

当然一个服务器上的网站可能还有更多,可以通过配置 Nginx 转发来代理这些端口分发,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
server {
server_name www.xxoo.com;
listen 80;

# 设置这个网站的根目录
root /wwwroot/www.xxoo.com/;

# 由于下面配置了文件不存在则代码到 Node.js 中,那么直接访问目录(不带默认主页)的话会有问题,这里做下判断
# 如果访问目录下有 index.html 文件,则直接重写到该文件
# break 表示重写且停止,但 url 不变,而 permanent 表示301重定向,url 会更新
if ( -f $request_filename/index.html ){
rewrite (.*) $1/index.html break;
}

# 如果请求的文件不存在,则代理到 Node.js
if ( !-f $request_filename ){
rewrite (.*) /index.js;
}

# 代理node服务 8001
location = /index.js {
# 设置一些代理的header信息,这些信息将被透传到 Node.js 服务的header信息里
proxy_set_header Connection "";
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_set_header X-NginX-Proxy true;

# 代理服务
proxy_pass http://127.0.0.1:8001$request_uri;

# 忽略其他重写
proxy_redirect off;
}
}

配置之后,比如你网站根目录里有 index.html 文件,访问 url 如:

访问链接 解析过程 备注
www.xxoo.com/index.html Nginx 由于文件存在,直接使用 Nginx 输出
www.xxoo.com Nginx 由于判断该目录下有 index.html 文件,则自动重写到该文件,但 url 不变
www.xxoo.com/xx.html Nginx -> Node.js:8001 由于文件不存在,使用 Nginx 代理到 Node.js 的 8001 端口
www.xxoo.com/xxoo/ Nginx -> Node.js:8001 首先判断该目录是否存在
如果存在再判断是否有 index.html 文件
一旦不成立,直接代理到 Node.js

Mac 编译 Nginx 并增加 echo 模块

方法 1 : 使用 openrestry

安装地址 : http://openresty.org/en/installation.html

1
$ brew install openresty/brew/openresty

如果是 nginx 也安装了, 可以停掉 nginx , 将配置文件复制到 openresty 文件夹 /usr/local/etc/openresty , 启动 openresty 即可运行

1
2
$ brew services stop nginx
$ brew services start openresty/brew/openresty

编译 echo module 并加载(失败)

!!! 此方法不能使用, 仅仅作为记录

解压源文件
Nginx 目录 : /usr/local/share/nginx/src

包含的文件列表

1
2
-rw-r--r--  1 duoli  staff   1.4K  8 11 22:52 configure_args.txt
-rw-r--r-- 1 duoli staff 658K 8 11 22:52 src.tar.xz

解压 源文件

1
2
$ xz -dk src.tar.xz
$ tar -zxvf src.tar

查看编译参数

1
2
3
4
$ cat configure_args.txt
...
--sbin-path=/usr/local/Cellar/nginx/1.19.2/bin/nginx
...

下载 echo module

当前目录: /usr/local/share/nginx/src/

1
2
3
$ mkdir modules
$ cd modules
$ git clone https://github.com/openresty/echo-nginx-module

我们编译的基础之上在加上 echo 目录, 由于无需覆盖, 所以这里修改 nginx 的文件目录 --sbin-path=/usr/local/Cellar/nginx/1.19.2/bin/nginx-echo

这里增加编译参数 --with-echo-module=/usr/local/share/nginx/src/modules/echo-nginx-module

这里使用 echo 官方使用动态模块加入:
https://github.com/openresty/echo-nginx-module#installation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ ./configure --prefix=/usr/local/Cellar/nginx/1.19.2 \
--add-dynamic-module=/usr/local/share/nginx/src/modules/echo-nginx-module \
--with-cc-opt='-DNGX_HTTP_HEADERS' $(nginx -V)

checking for OS
+ Darwin 19.6.0 x86_64
checking for C compiler ... found
+ using Clang C compiler
+ clang version: 11.0.3 (clang-1103.0.32.62)
...
...
creating objs/Makefile

Configuration summary
+ using system PCRE library
+ OpenSSL library is not used
+ using system zlib library

nginx path prefix: "/usr/local/Cellar/nginx/1.19.2"
nginx binary file: "/usr/local/Cellar/nginx/1.19.2/sbin/nginx"
nginx modules path: "/usr/local/Cellar/nginx/1.19.2/modules"
...

$ make -j2
$ make install

这样存在动态模块 /usr/local/Cellar/nginx/1.19.2/modules

1
2
$ ll
-rwxr-xr-x 1 duoli staff 49K 8 25 17:08 ngx_http_echo_module.so

nginx 配置加载动态模块

但是这样加载的模块出现 不兼容

1
nginx: [emerg] module "/usr/local/Cellar/nginx/1.19.2/modules/ngx_http_echo_module.so" is not binary compatible in /usr/local/etc/nginx/nginx.conf:11

原因可能是版本不兼容: https://github.com/openresty/echo-nginx-module#compatibility

Linux 中编译安装和配置 Nginx

注意:本示例在 Centos 6.5 、Centos 7.3 中运行。

下载安装包并解压

1
2
3
4
5
6
7
8
# 进入约定的安装包目录
cd /约定目录/src/

# 下载nginx安装文件,这里以 nginx-1.11.1 为例,其他的nginx包可以去官网查找
wget http://nginx.org/download/nginx-1.11.1.tar.gz

# 把安装包解压,会自动解压到 /约定目/src/nginx-1.11.1/ 文件夹里
tar xzf nginx-1.11.1.tar.gz

nginx的配置文件

其实完全可以使用 Nginx 的默认配置文件,默认配置文件编译后的地址在 程序目录/conf/nginx.conf ,但如果你会频繁的更新 Nginx 版本的话配置文件最后”分离”,类似前后端分离一样,这样可以少”吵架”(解耦合),我们单独的把 Nginx 的所有配置,包括所有站点的配置、SSL 证书都放在 /约定目录/nginx-conf/ 目录里,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/约定目录/nginx-conf/

# nginx配置文件
./nginx.conf

# nginx的其他配置
./mime.types

# 站点配置
./conf/
# 各个子站点目录
./www.xxoo.com.conf
./www.a.com.conf
...

/约定目录/nginx-conf/conf/ 目录里存放以网站为单位的配置文件,文件名以网站域名命名,这样可以配置分离,互不影响,而且好定位问题。

配置nginx安装参数

1
2
3
4
5
6
7
8
# 创建对应版本的程序目录,这个目录用来存放编译后的文件
mkdir -p /约定目录/local/nginx-1.11.1/

# 进入安装包解压后的目录
cd /约定目录/src/nginx-1.11.1/

# 开始配置
./configure --prefix=/home/local/nginx-1.11.1 --conf-path=/home/local/nginx-conf/vhost/nginx.conf --with-http_ssl_module --with-http_realip_module --with-http_dav_module --with-http_gzip_static_module --with-http_v2_module

其他的安装编译配置可点击:Nginx 编译参数

编译nginx

1
2
3
# 开始编译并安装,可能需要sudo权限
make
[sudo] make install

验证是否安装成功

1
2
# 进入nginx执行目录
cd /约定目录/local/nginx-1.11.1/sbin/

运行 ./nginx -v 查看版本,结果如:

1
nginx version: nginx/1.11.1

如果你有开启 http_ssl_module ,可运行:./nginx -V 查看是否支持,结果如:

1
2
3
4
5
nginx version: nginx/1.11.1
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC)
built with OpenSSL 1.0.1e-fips 11 Feb 2013
TLS SNI support enabled
...

重新编译安装

是指已经安装成功, 但想对 Nginx 添加个模块或者修改配置时需要重新编译。

1. 下载对应版本 (如果不想更新版本可以忽略)

可以使用 nginx -V 查看当前程序的编译参数

1
2
3
4
5
6
# 下载

wget http://nginx.org/download/nginx-1.11.13.tar.gz

# 解压
tar xzf nginx-1.11.13.tar.gz

2. 重新编译

1
2
3
4
5
6
# 配置
./configure --prefix=xxx 你的新参数

# 编译
[sudo] make
这时侯切记不要make install, 因为make install就会把文件复制到安装目录

3. 复制程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 新复制老版本, 以防出错
cp /path/nginx /path/nginx.back

# 停止服务
/path/nginx -s stop

# 复制新版本到安装目录
cp objs/nginx /path/nginx

# 查看新版本
/path/nginx -t

# 启动新版本
/path/nginx

注意:如果在运行中直接覆盖 Nginx 会报: cp: 无法创建普通文件"nginx": 文本文件忙

proxy_pass url 反向代理的坑

说到 Nginx 就不得不说 Nginx 的反向代理是多么的好用,一个指令 proxy_pass 搞定反向代理,对于接口代理、负载均衡很是实用,但 proxy_pass 指令后面的参数很有讲究。

网上有很多什么绝对路径、相对路径的说法,其实在实际的应用中就分为两种情况:

url 只是 host

这里指不包含 $uri ,如:

  • http://host - √
  • https://host - √
  • http://host:port - √
  • https://host:port - √
  • http://host/ - x
  • http://host:port/ - x

这时候 location 匹配的完整路径将直接透传给 url ,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 访问:   /                               后端:   /
// 访问: /api/xx 后端: /api/xx
// 访问: /api/xx?aa 后端: /api/xx?aa
location / {
proxy_pass http://node:8080;
}

// 访问: /api/ 后端: /api/
// 访问: /api/xx 后端: /api/xx
// 访问: /api/xx?aa 后端: /api/xx?aa
// 访问: /api-xx?aa 后端:
location /api/ {
proxy_pass http://node:8080;
}

// 访问: /api/ 后端: /api/
// 访问: /api/xx 后端: /api/xx
// 访问: /api/xx?aa 后端: /api/xx?aa
// 访问: /api-xx?aa 后端: /api-xx?aa
location /api {
proxy_pass http://node:8080;
}

url 包含路径

注意,这里的路径哪怕只是一个 / 也是存在的,如:

  • http://host - x
  • https//host/ - √
  • http://host:port - x
  • https://host:port/ - √
  • http://host/api - √
  • http://host/api/ - √

proxy_pass urlurl 包含路径时,匹配时会根据 location 的匹配后的链接透传给 url ,注意匹配后就是这样:

location 规则 访问的原始链接 匹配之后的路径
location / /
location / /a a
location / /a/b/c?d a/b/c?d
location /a/ /a/
location /a/ /a/b/c?d b/c?d

明白匹配之后的路径后,在 proxy_pass url 包含路径时,将会把匹配之后的路径透传给 url ,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 访问:   /                               后端:   /
// 访问: /api/xx 后端: /api/xx
// 访问: /api/xx?aa 后端: /api/xx?aa
location / {
proxy_pass http://node:8080/;
}

// 访问: /api/ 后端: /
// 访问: /api/xx 后端: /xx
// 访问: /api/xx?aa 后端: /xx?aa
// 访问: /api-xx?aa 未匹配
location /api/ {
proxy_pass http://node:8080/;
}

// 访问: /api 后端: /
// 访问: /api/ 后端: //
// 访问: /api/xx 后端: //xx
// 访问: /api/xx?aa 后端: //xx?aa
// 访问: /api-xx?aa 后端: /-xx?aa
location /api {
proxy_pass http://node:8080/;
}

// 访问: /api/ 后端: /v1
// 访问: /api/xx 后端: /v1xx
// 访问: /api/xx?aa 后端: /v1xx
// 访问: /api-xx?aa 未匹配
location /api/ {
proxy_pass http://node:8080/v1;
}

// 访问: /api/ 后端: /v1/
// 访问: /api/xx 后端: /v1/xx
// 访问: /api/xx?aa 后端: /v1/xx
// 访问: /api-xx?aa 未匹配
location /api/ {
proxy_pass http://node:8080/v1/;
}

由以上规则可以看出,当 proxy_pass url 中包含路径时,结尾的 / 最好同 location 匹配规则一致。

当 proxy_pass 遇到正则

location 以正则形式匹配时,proxy_pass 就不能以 / 结束了,也就是不能包含路径了,比如错误的:

1
2
3
4
5
6
7
8
9
location ~* ^/api/ {
proxy_pass http://host/;
}

location / {
if ($uri ~* ^/api/) {
proxy_pass http://host/;
}
}

解决办法就是把链接中的路径去掉。

重写代理链接 - url rewrite

当原始链接(浏览器访问的链接)和代理服务器链接规则不一致时,可以使用 Nginx URL Rewrite 功能去动态的重写,如:

1
2
3
4
location ~* ^/api/ {
rewrite ^/api/(.*) /?path=$1 break;
proxy_pass http://node:8080;
}

以上请求会把匹配 /api/ 的链接重写为 /?path= 的链接透传给 node:8080 服务,有意思的是当使用 rewrite 指令并且生效后,proxy_pass url 链接中的路径会被忽略,如:

1
2
3
4
5
6
7
8
// 访问:   /                               后端:   /node/
// 访问: /api 后端: /node/api
// 访问: /api/ 后端: /?path=
// 访问: /api/a/b/c 后端: /?path=a/b/c
location / {
rewrite ^/api/(.*) /?path=$1 break;
proxy_pass http://node:8080/node/;
}

配置 HTTPS

首先配置支持 HTTPS 必须让 Nginx 开启 http_ssl_module 模块,点击查看 nginx 编译安装参数 ,可以使用nginx -V查看是否开启TLS SNI support enabled

购买/生成 SSL 证书,可以使用免费的证书,在这里申请就很可以 腾讯云 SSL 证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 配置 HTTPS

# 配置个http的站点,用来做重定向,当然如果你不需要把 HTTP->HTTPS 可以把这个配置删了
server {
listen 80;

# 配置域名
server_name domain.com www.domain.com;

# 配置让这些 HTTP 的访问全部 301 重定向到 HTTPS 的
return 301 https://domain.com$request_uri;
}

# 配置 HTTPS
server {
listen 443 ssl;
server_name www.domain.com domain.com;

# https
ssl_certificate https/domain.com.crt;
ssl_certificate_key https/domain.com.key;

# other setting
}

注意,这里证书的格式是 .crt 的。

配置后的访问规则

输入链接 最终访问链接
http://www.domain.com https://domain.com
http://www.domain.com/404/500 https://domain.com/404/500
http://domain.com https://domain.com
https://www.domain.com https://domain.com
https://domain.com/500 https://domain.com/500

对于网站需要 https 和 http 共同来访问的可以

1
2
3
4
5
6
7
8
9
10

server {
listen 80;
listen 443 ssl;
server_name www.domain.com domain.com;

# https setting...

# other setting...
}

参考链接

Nginx 目录建议

安装 Nginx 时可以选择配置文件的路径,由于 Nginx 程序后续可能会升级版本,但配置基本上就是一份,那么推荐使用配置和程序分离的方式,遵循:

  1. 配置文件独立管理, 不存放在 nginx 程序目录内
  2. 每个站点独立一个配置文件
  3. 每个站点独立的日志文件
  4. 提取公用的配置文件

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$dir/wwwroot/                           - 网站根目录,以域名为文件夹名称
./xuexb.com/
./static.xuexb.com/

$dir/src/ - 安装源包

$dir/local/nginx/ - nginx相关根目录
./conf/ - 配置文件
./nginx.conf - 配置主入口
./inc - 通用配置
./vhost/ - 各站点的配置,以 `域名.conf` 命名
./xuexb.com.conf
./static.xuexb.com.conf

./1.11.1/ - 各个版本的nginx
./1.11.2/

$dir/logs/ - 日志相关目录,内以 `域名.type.log` 命名
./last/ - 最新的日志
./xuexb.com.error.log
./xuexb.com.access.log
./back/ - 天级备份日志
./20170908/

这样分离之后不管是 Nginx 主程序版本升级,还是修改某个站点配置,还是快速查找某个站点日志都是得心应手~

配置浏览器缓存

使用 expires 参数。

不缓存

1
2
3
server {
expires -1;
}

输出 Response Headers:

1
Cache-Control:no-cache

当文件没有变更时会返回 304 ,有变更时会是 200 ,如果强制命中 200 可以再添加: if_modified_since off; 忽略 Request Headers 里的 If-Modified-Since 字段。

缓存

1
2
3
server {
expires 1d;
}

1d 为 1 天,单位如下:

1
2
3
4
5
6
7
8
ms  milliseconds
s seconds
m minutes
h hours
d days
w weeks
M months,30 days
y years,365 days

如果希望最大缓存可以:

1
2
3
server {
expires max;
}

输出 Response Headers:

1
Cache-Control:max-age=315360000

根据链接设置缓存时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server {
# 设置为1月
set $expires_time 1M;

# 针对后台不缓存
if ($request_uri ~* ^/admin(\/.*)?$) {
set $expires_time -1;
}

# 针对静态文件缓存最大
if ($request_uri ~* ^/static(\/.*)?$) {
set $expires_time max;
}

# 设置吧
expires $expires_time;
}

Nginx 状态码配置和错误文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server {
# 配置访问 /test.js 时报 403 错
location /test.js {
return 403;
}

# 配置访问 /404 时报 404 错
location /404 {
return 404;
}

# 配置访问 /500 时报 500 错
location /500 {
return 500;
}

# 把指定状态码指向这个文件 uri
error_page 500 502 503 504 /status.html;
error_page 404 /status.html;
}

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
server {
listen 80;
server_name test.me;

root /Users/xiaowu/work/test.me;

# 用if匹配任何以 403 开头的,会匹配到 /4034444
if ($request_uri ~* ^/403) {
return 403;
}

# 用location匹配 /500/ /500,但不匹配 /500/1
location ~* "^/500/?$" {
return 500;
}

# 用if匹配以 /501/ 开头的,匹配 /501/1,/501/1/2 但不匹配 /501
if ($request_uri ~* ^/501/) {
return 501;
}

# 用location匹配 /502/ /502 /502/1 /502/1/2
location ~* "^/502(/.*)?$" {
return 502;
}

# 用location只匹配 /503
location = /503 {
return 503;
}
}

error_page配置小提示

注意 error_page 配置时加 = 和不加 = 的区别,加了 = 表示响应为指定的 http status code ,默认为 200,不加 = 为原错误的状态码~

1
2
3
4
5
6
7
8
9
10
11
12
# 这样可以访问错误页面时 http status 为 404 ,并且页面内容是 404.html 的内容
error_page 404 /404.html
error_page 404 500 /404.html;

# 这样配置访问错误页面时 http status 为 200 ,但页面内容是 404.html 的内容
error_page 404 500 = /404.html;

# 这样配置访问错误页面时 http status 为 404 ,但页面内容是 404.html 的内容
error_page 404 500 =404 /404.html;

# 也可以把404请求直接301到某个域上
error_page 404 =301 https://xuexb.com/404;

这样就可以根据自己需求配置错误页为指定的状态码,因为非 200 的状态码可能会被浏览器拦截。

配置 CORS 跨域

设置允许所有的请求

1
2
3
4
5
server {
location / {
add_header 'Access-Control-Allow-Origin' '*';
}
}

只允许GET请求

1
2
3
4
5
6
server {
location / {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Request-Method' 'GET';
}
}

请求白名单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server {
location / {
# 白名单
if ($http_origin ~* (baidu\.com|github.xuexb.com)$) {
add_header 'Access-Control-Allow-Origin' '$http_origin';

# 允许cookie
add_header 'Access-Control-Allow-Credentials' true;

# 只允许某些方法
add_header 'Access-Control-Request-Method' 'GET, POST, OPTIONS';

# 支持获取其她字段, 需要前端设置 `xhr.withCredentials = true`
add_header 'Access-Control-Allow-Headers' 'User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
}
}

iconfont 字体跨域配置

1
2
3
4
5
6
7
8
server {
root xxx;

# 使用location来匹配以字体文件
location ~* \.(eot|otf|ttf|woff|svg)$ {
add_header Access-Control-Allow-Origin *;
}
}

但如果你的 location 已经配置了, 可以使用 if 判断添加, 如:

1
2
3
4
5
6
7
8
server {
location / {
# 使用判断请求文件来添加
if ($document_uri ~ \.(eot|otf|ttf|woff|svg)$) {
add_header Access-Control-Allow-Origin *;
}
}
}

[译+] Nginx 使用 HTTP Basic Authentication 来限制访问

原文地址 : Restricting Access with HTTP Basic Authentication

介绍

你可以通过使用用户名/密码身份验证来限制对网站或网站某些部分的访问。用户名和密码取自密码文件, 这个文件通常使用密码文件创建工具来生成, 例如: apache2-utils

HTTP 基本身份验证也可以与其他访问限制方法结合使用,例如通过 IP 地址地理位置 限制访问。

先决条件

  • NGINX Plus 或 NGINX
  • 密码文件创建程序,例如 apache2-utils(Debian,Ubuntu)或 httpd-tools(RHEL / CentOS / Oracle Linux)。

创建密码文件

要创建用户名-密码对,需要使用密码文件创建工具,例如,apache2-utilshttpd-tools

1). 确认已安装 apache2-utils(Debian,Ubuntu)或 httpd-tools(RHEL / CentOS / Oracle Linux)

2). 创建密码文件和第一个用户。运行 htpasswd 带有 -c 选项(创建一个新文件)的程序,文件路径名作为第一个参数,用户名作为第二个参数:

1
$ sudo htpasswd -c /etc/apache2/.htpasswd user1

按 Enter,然后在提示时键入 user1 的密码。

3). 创建其他用户密码对。省略 -c选项,因为该文件已经存在:

1
$ sudo htpasswd /etc/apache2/.htpasswd user2

4). 你可以确认该文件包含的成对的用户名和加密的密码:

1
2
3
4
$ cat /etc/apache2/.htpasswd
user1:$apr1$/woC1jnP$KAh0SsVn5qeSMjTtn0E9Q0
user2:$apr1$QdR8fNLT$vbCEEzDj7LyqCMyNpSoBh/
user3:$apr1$Mr5A0e.U$0j39Hp5FfxRkneklXaMrr/

配置 NGINX 和 NGINX Plus 以进行 HTTP 基本身份验证

  • 在要保护的位置,指定 auth_basic 指令并为密码保护的区域设置标题。要求提供凭证时,该区域的名称将显示在用户名/密码对话框窗口中:
1
2
3
4
location /api {
auth_basic “Administrator’s Area”;
#...
}
1
2
3
4
location /api {
auth_basic “Administrator’s Area”;
auth_basic_user_file /etc/apache2/.htpasswd;
}

另外,你可以使用基本身份验证来限制对整个网站的访问,但仍将某些网站区域设为公开。在这种情况下,设置指定目录的 auth_basic 的值设置为 off

1
2
3
4
5
6
7
8
9
server {
...
auth_basic "Administrator’s Area";
auth_basic_user_file conf/htpasswd;

location /public/ {
auth_basic off;
}
}

将基本身份验证与 IP 地址访问限制相结合

HTTP 基本身份验证可以有效地结合 IP 地址的访问限制。可以至少实现两种方案:

  • 用户必须同时经过身份验证并具有有效的 IP 地址

  • 用户必须经过身份验证或具有有效的 IP 地址

    1). 使用allowdeny指令允许或拒绝来自特定 IP 地址的访问

1
2
3
4
5
6
7
location /api {
#...
deny 192.168.1.2;
allow 192.168.1.1/24;
allow 127.0.0.1;
deny all;
}

仅对 192.168.1.1/24 网络(192.168.1.2 地址除外)授予访问权限。请注意,allowdeny 指令将按其定义的顺序应用。

2). 将 IP 和 HTTP 身份验证的限制与 satisfy 指令结合使用。如果将指令设置为 all,则客户端同时满足两个条件,则将授予访问权限。如果将指令设置为 any,如果客户端至少满足至少一个条件,会授予访问权限:

1
2
3
4
5
6
7
8
9
10
11
12
location /api {
#...
satisfy all;

deny 192.168.1.2;
allow 192.168.1.1/24;
allow 127.0.0.1;
deny all;

auth_basic "Administrator’s Area";
auth_basic_user_file conf/htpasswd;
}

完整的例子

该示例显示了如何通过简单身份验证以及 IP 地址访问限制来保护您的状态区域:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
http {
server {
listen 192.168.1.23:8080;
root /usr/share/nginx/html;

location /api {
api;
satisfy all;

deny 192.168.1.2;
allow 192.168.1.1/24;
allow 127.0.0.1;
deny all;

auth_basic "Administrator’s Area";
auth_basic_user_file /etc/apache2/.htpasswd;
}
}
}

当您访问状态页面时,系统会提示您登录:

如果提供的名称和密码与密码文件不匹配,则会出现 401 (Authorization Required) 错误

使用 wget/curl 访问

浏览器中使用
直接在浏览器中输入地址, 会弹出用户密码输入框, 输入即可访问

1
2
3
4
5
# 使用 wget
$ wget --http-user=magina --http-passwd=123456 http://res.yinnote.com/xxx.zip

# 使用 curl
$ curl -u magina:123456 -O http://res.yinnote.com/xxx.zip

[转] Nginx 出现 413 Request Entity Too Large 错误解决方法

原文地址 : Nginx 出现 413 Request Entity Too Large 错误解决方法

Nginx 出现的 413 Request Entity Too Large 错误,这个错误一般在上传文件的时候出现
解决方法就是
打开 nginx 主配置文件 nginx.conf,一般在 /usr/local/nginx/conf/nginx.conf 这个位置,找到 http{}, 或者 server{} 段, 或者 location / {} 段,修改或者添加:

1
client_max_body_size 20m;

然后重启 nginx,

1
sudo /etc/init.d/nginxd reload

即可。要是以 php 运行的话,这个大小 client_max_body_size 要和 php.ini 中的如下值的最大值差不多或者稍大,这样就不会因为提交数据大小不一致出现错误。

1
2
post_max_size = 2M
upload_max_filesize = 2M

重启 NGINX

1
kill -HUP `cat /usr/local/nginx/nginx.pid`

恢复正常

配置默认主页、目录浏览

设置默认主页

直接可以使用目录形式打开的页面称为默认主页,一般常见的有: index.htmlindex.htmindex.php 这些,要吧通过配置来完成,如:

1
2
3
4
5
6
server {
root /网站根目录;

# 设置默认主页,支持多个,按优先级来,空格分格
index index.html index.htm index.php;
}

设置目录浏览

当一个目录内没有默认主页的文件时,直接访问目录会报 403 Forbidden 错误,而启用目录浏览功能后可以直接列出当前目录下的文件、文件夹,如:

1
2
3
4
5
6
7
8
9
server {
root /网站根目录;

# 优先使用默认主页
index index.html index.htm;

# 当默认主页不存在时直接列出目录内文件树
autoindex on;
}

但多 autoindex 相关可官方文档: http://nginx.org/en/docs/http/ngx_http_autoindex_module.html

注意,线上运行环境最好别开启该配置,因为这将直接暴露你的文件~

日志切割 - Shell

方式一(推荐) : 使用 logrotate 做日志切割

关于日志切割参考 运维中的日志切割(logrotate)

方式二: shell 脚本

由于 Nginx 的日志(包括 access 和 error)默认都是存放在一个文件夹内,当时间长了,日志文件体积越来越大,不易管理,可以自己写个脚本来处理,其实就是定时定点去把日志移动下位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/sh

# 日志源目录
sourceDir='/webdata/logs/proj/'

# 日志备份目录
backDir='/webdata/logs/proj_backup/'

echo "split-logs start: $(date +%Y-%m-%d %H:%M:%S)"

ls $sourceDir | while read filename
do
mkdir -p "$backDir/$(date +%Y%m%d)/"
mv "$sourceDir/$filename" "$backDir/$(date +%Y%m%d)/"
echo "$sourceDir/$filename => $backDir/$(date +%Y%m%d)/$filename"
done

# 刷新nginx
kill -USR1 `cat /var/run/nginx.pid`

echo "split-logs end: $(date +%Y-%m-%d %H:%M:%S)"
echo "----------------"

有几个注意的:

  1. 日志源目录 - 该目录存放日志文件,如 access.log,error.log,当然如果你的站点比较多,可以用域名来进行分组,比如: web.access.log,web.error.log 。
  2. 日志备份目录 - 会以Ymd命名文件夹名存放在该目录。
  3. 刷新 Nginx - 由于是直接移动日志文件,移动后需要刷新下 Nginx 使其再生成日志文件,当然也可以 nginx -s reload ,但 reload 要比 kill 的慢,而 kill -USR1 是无损的。
  4. 定时任务 - 需要使用 root 帐户下使用 crontab -e 插入一条定时任务,定时 23:55 开始分割,后面是把错误和信息输出到指定文件,方便调试,如:
1
55 23 * * * sh /你的目录/split-log >> /var/log/nginx/crontab.log 2>&1

当然这只是日志切割的一种,还有很多种方法~

[原] filebeat nginx 模块的自定义字段

说明

filebeat 提供了多种 Module 预制模块,简化了各种日志的格式化, 但是默认的字段并不能满足实际需求, 例如我们需要记录额外的 Nginx 字段
例如 请求时间、后端响应时间、主机头等信息
那么在 filebeat 的 nginx module 中需要同步定义

Nginx 的模块位置在 /usr/share/filebeat/module/nginx, 下边是目录结构.

目录结构

1
2
3
4
5
6
7
8
9
├── access
│   ├── config
│   │   └── nginx-access.yml
│   ├── ingest
│   │   └── default.json # 默认的解析字段
│   ├── machine_learning
│   │   └── ....json
│   └── manifest.yml
└── module.yml

默认的解析模块

这里我们需要修改的是 patterns 中的数据, 注意这里是经过 json 转义的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"description": "Pipeline for parsing Nginx access logs. Requires the geoip and user_agent plugins.",
"processors": [
{
"grok": {
"field": "message",
"patterns": [
"\"?%{IP_LIST:nginx.access.remote_ip_list} - %{DATA:user.name} \\[%{HTTPDATE:nginx.access.time}\\] \"%{GREEDYDATA:nginx.access.info}\" %{NUMBER:http.response.status_code:long} %{NUMBER:http.response.body.bytes:long} \"%{DATA:http.request.referrer}\" \"%{DATA:user_agent.original}\""
],
"pattern_definitions": {
"IP_LIST": "%{IP}(\"?,?\\s*%{IP})*"
},
"ignore_missing": true
}
}
]
}

更改 nginx 日志的格式

之前

1
2
3
log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" ';

之后

1
2
3
4
log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'"$host" $request_time $upstream_response_time';

这里我们增加了三个字段

1
192.168.1.112 - - [25/Apr/2019:18:22:01 +0800] "GET /help/show/20 HTTP/1.1" 200 7474 "http://t.dailian.iliexiang.com/help?cat_id=2" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36" "-" "t.dailian.iliexiang.com" 0.063 0.021

更新 Patterns

支持的 Patterns grok-patterns

1
vim /usr/share/filebeat/module/nginx/access/ingest/default.json

之前

1
"\"?%{IP_LIST:nginx.access.remote_ip_list} - %{DATA:user.name} \\[%{HTTPDATE:nginx.access.time}\\] \"%{GREEDYDATA:nginx.access.info}\" %{NUMBER:http.response.status_code:long} %{NUMBER:http.response.body.bytes:long} \"%{DATA:http.request.referrer}\" \"%{DATA:user_agent.original}\""

之后

1
"?%{IP_LIST:nginx.access.remote_ip_list} - %{DATA:user.name} \[%{HTTPDATE:nginx.access.time}\] \"%{GREEDYDATA:nginx.access.info}\" %{NUMBER:http.response.status_code:long} %{NUMBER:http.response.body.bytes:long} "%{DATA:http.request.referrer}" \"%{DATA:user_agent.original}\" \"%{DATA:nginx.access.x_forwarded_for}\" \"%{DATA:nginx.access.host}\" %{NUMBER:nginx.access.request_time:float} %{NUMBER:nginx.access.upstream_response_time:float}

调试工具: 使用 kibana 的 Debuger

1
http://192.168.1.21:5601/app/kibana#/dev_tools/grokdebugger?_g=()

这里需要填写自定义的 Patterns , 否则无法识别
Custom Patterns

1
IP_LIST %{IP}(\"?,?\\s*%{IP})*

更新 Fields

编辑字段

1
vim /etc/filebeat/fields.yml

在文件 /etc/filebeat/fields.yml, 找到 nginx 字段, 添加以上的三个字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- name: x_forwarded_for
type: group
description: >
Forwarded IP
- name: host
type: group
description: >
Server hostname.
- name: request_time
type: group
description: >
Url Request Time
- name: upstream_response_time
type: group
description: >
Upstream Response Time.

让新修改的文件生效

先检查配置文件是否正确

1
# filebeat test config
1
# systemctl restart filebeat
1
2
3
4
# 获取所有的 pipeline
GET _ingest/pipeline

DELETE _ingest/pipeline/filebeat-7.0.0-nginx-access-default

模拟请求 pattern

1
2
3
4
5
6
7
8
9
10
POST _ingest/pipeline/filebeat-6.6.1-nginx-access-default/_simulate
{
"docs":[
{
"_source": {
"message": "10.10.10.10 - - [17/Oct/2017:03:48:00 +0200] \"GET /my_page/40 HTTP/1.1\" 200 75793 \"-\" \"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)\" 0.277"
}
}
]
}

[转] 关于 Nginx 下开启 php-fpm 输出 php 错误日志的设置

原文地址: 关于 Nginx 下开启 php-fpm 输出 php 错误日志的设置

最近在本地搭建的 LNMP 的开发环境。为了开发的时候不影响前端的正常开发就屏蔽的 PHP 里面 php.ini 中的一些错误提示。但是这样一来,就影响到了后端开发的一些问题比如不能及时调试开发中的一些问题。

nginx 与 apache 不一样,在 apache 中可以直接指定 php 的错误日志,那样在 php 执行中的错误信息就直接输入到 php 的错误日志中,可以方便查询。

在 nginx 中事情就变成了这样:nginx 只对页面的访问做 access 记录日志。不会有 php 的 error log 信息。nginx 把对 php 的请求发给 php-fpm fastcgi 进程来处理,默认的 php-fpm 只会输出 php-fpm 的错误信息,在 php-fpm 的 errors log 里也看不到 php 的 errorlog。

原因是 php-fpm 的配置文件 php-fpm.conf 中默认是关闭 worker 进程的错误输出,直接把他们重定向到/dev/null,所以我们在 nginx 的 error log 和 php-fpm 的 errorlog 都看不到 php 的错误日志。

所以我们要进行如下的设置就能查看到 nginx 下 php-fpm 不记录 php 错误日志的方法:

1,修改 php-fpm.conf 中的配置,如果没有请增加:

1
2
3
4
5
6
[global]
; Note: the default prefix is` `/usr/local/php/var
error_log = log``/php_error_log

[www]
catch_workers_output =yes

2.修改 php.ini 中配置,没有则增加

1
2
3
log_errors = On
error_log = "/usr/local/php/var/log/error_log"
error_reporting=E_ALL&~E_NOTICE

3.重启 php-fpm,

当 PHP 执行错误时就能看到错误日志在”/usr/local/lnmp/php/var/log/php_error_log”中了

如果出现:

1
2
3
4
5
6
7
8
9
$ service php-fpm restart

Gracefully shutting down php-fpm . done

Starting php-fpm
[17-Apr-2014 18:40:52] ERROR: [/usr/local/php/etc/php-fpm.conf:5] unknown entry catch_workers_
[17-Apr-2014 18:40:52] ERROR: failed to load configurationfile'/usr/local/php/etc/php-fpm.conf'
[17-Apr-2014 18:40:52] ERROR: FPM initialization failed
failed

那请在第一步的时候,认真将配置写入相对应的组中,不然就出现上面的:ERROR: [/usr/local/php/etc/php-fpm.conf:5] unknown entry ‘catch_workers_output’

最后看看效果:

[转] nginx 配置 location 总结及 rewrite 规则写法

1. location 正则写法

一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
location  = / {
# 精确匹配 / ,主机名后面不能带任何字符串
[ configuration A ]
}

location / {
# 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求
# 但是正则和最长字符串会优先匹配
[ configuration B ]
}

location /documents/ {
# 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
[ configuration C ]
}

location ~ /documents/Abc {
# 匹配任何以 /documents/Abc 开头的地址,匹配符合以后,还要继续往下搜索
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
[ configuration CC ]
}

location ^~ /images/ {
# 匹配任何以 /images/ 开头的地址,匹配符合以后,停止往下搜索正则,采用这一条。
[ configuration D ]
}

location ~* \.(gif|jpg|jpeg)$ {
# 匹配所有以 gif,jpg或jpeg 结尾的请求
# 然而,所有请求 /images/ 下的图片会被 config D 处理,因为 ^~ 到达不了这一条正则
[ configuration E ]
}

location /images/ {
# 字符匹配到 /images/,继续往下,会发现 ^~ 存在
[ configuration F ]
}

location /images/abc {
# 最长字符匹配到 /images/abc,继续往下,会发现 ^~ 存在
# F与G的放置顺序是没有关系的
[ configuration G ]
}

location ~ /images/abc/ {
# 只有去掉 config D 才有效:先最长匹配 config G 开头的地址,继续往下搜索,匹配到这一条正则,采用
[ configuration H ]
}

location ~* /js/.*/\.js
  • = 开头表示精确匹配
    如 A 中只匹配根目录结尾的请求,后面不能带任何字符串。
  • ^~  开头表示 uri 以某个常规字符串开头,不是正则匹配
  • ~ 开头表示区分大小写的正则匹配;
  • ~* 开头表示不区分大小写的正则匹配
  • / 通用匹配, 如果没有其它匹配,任何请求都会匹配到

顺序 no 优先级:
(location =) > (location 完整路径) > (location ^~ 路径) > (location ,* 正则顺序) > (location 部分起始路径) > (/)

上面的匹配结果
按照上面的 location 写法,以下的匹配示例成立:

  • / -> config A
    精确完全匹配,即使/index.html 也匹配不了
  • /downloads/download.html -> config B
    匹配 B 以后,往下没有任何匹配,采用 B
  • /images/1.gif -> configuration D
    匹配到 F,往下匹配到 D,停止往下
  • /images/abc/def -> config D
    最长匹配到 G,往下匹配 D,停止往下
    你可以看到 任何以/images/开头的都会匹配到 D 并停止,FG 写在这里是没有任何意义的,H 是永远轮不到的,这里只是为了说明匹配顺序
  • /documents/document.html -> config C
    匹配到 C,往下没有任何匹配,采用 C
  • /documents/1.jpg -> configuration E
    匹配到 C,往下正则匹配到 E
  • /documents/Abc.jpg -> config CC
    最长匹配到 C,往下正则顺序匹配到 CC,不会往下到 E

实际使用建议

所以实际使用中,个人觉得至少有三个匹配规则定义,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说。
#这里是直接转发给后端应用服务器了,也可以是一个静态首页
# 第一个必选规则
location = / {
proxy_pass [http://tomcat](http://tomcat/):8080/index
}

# 第二个必选规则是处理静态文件请求,这是nginx作为http服务器的强项
# 有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用
location ^~ /static/ {
root /webroot/static/;
}

location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
root /webroot/res/;
}

#第三个规则就是通用规则,用来转发动态请求到后端应用服务器
#非静态文件请求就默认是动态请求,自己根据实际把握
#毕竟目前的一些框架的流行,带.php,.jsp后缀的情况很少了
location / {
proxy_pass [http://tomcat](http://tomcat/):8080/
}

http://tengine.taobao.org/book/chapter_02.html
http://nginx.org/en/docs/http/ngx_http_rewrite_module.html

2. Rewrite 规则

rewrite 功能就是,使用 nginx 提供的全局变量或自己设置的变量,结合正则表达式和标志位实现 url 重写以及重定向。rewrite 只能放在server{}, location{}, if{} 中,并且只能对域名后边的除去传递的参数外的字符串起作用,例如  http://seanlook.com/a/we/index.php?id=1&u=str  只对/a/we/index.php 重写。语法rewrite regex replacement [flag];

如果相对域名或参数字符串起作用,可以使用全局变量匹配,也可以使用 proxy_pass 反向代理。

表面看 rewritelocation 功能有点像,都能实现跳转,主要区别在于 rewrite 是在同一域名内更改获取资源的路径,而 location 是对一类路径做控制访问或反向代理,可以 proxy_pass 到其他机器。很多情况下 rewrite 也会写在 location 里,它们的执行顺序是:

  1. 执行 server 块的 rewrite 指令
  2. 执行 location 匹配
  3. 执行选定的 location 中的 rewrite 指令

如果其中某步 URI 被重写,则重新循环执行 1-3,直到找到真实存在的文件;循环超过 10 次,则返回 500 Internal Server Error 错误。

2.1 flag 标志位

  • last : 相当于 Apache 的[L]标记,表示完成 rewrite
  • break : 停止执行当前虚拟主机的后续 rewrite 指令集
  • redirect : 返回 302 临时重定向,地址栏会显示跳转后的地址
  • permanent : 返回 301 永久重定向,地址栏会显示跳转后的地址

因为 301 和 302 不能简单的只返回状态码,还必须有重定向的 URL,这就是 return 指令无法返回 301,302 的原因了。这里 last 和 break 区别有点难以理解:

  1. last 一般写在 server 和 if 中,而 break 一般使用在 location 中
  2. last 不终止重写后的 url 匹配,即新的 url 会再从 server 走一遍匹配流程,而 break 终止重写后的匹配
  3. break 和 last 都能组织继续执行后面的 rewrite 指令

2.2 if 指令与全局变量

if 判断指令
语法为if(condition){...},对给定的条件 condition 进行判断。如果为真,大括号内的 rewrite 指令将被执行,if 条件(conditon)可以是如下任何内容:

  • 当表达式只是一个变量时,如果值为空或任何以 0 开头的字符串都会当做 false
  • 直接比较变量和内容时,使用=!=
  • ~正则表达式匹配,~*不区分大小写的匹配,!~区分大小写的不匹配

-f!-f用来判断是否存在文件
-d!-d用来判断是否存在目录
-e!-e用来判断是否存在文件或目录
-x!-x用来判断文件是否可执行

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /msie/$1 break;
} //如果UA包含"MSIE",rewrite请求到/msid/目录下

if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
set $id $1;
} //如果cookie匹配正则,设置变量$id等于正则引用部分

if ($request_method = POST) {
return 405;
} //如果提交方法为POST,则返回状态405(Method not allowed)。return不能返回301,302

if ($slow) {
limit_rate 10k;
} //限速,$slow可以通过 set 指令设置

if (!-f $request_filename){
break;
proxy_pass http://127.0.0.1;
} //如果请求的文件名不存在,则反向代理到localhost 。这里的break也是停止rewrite检查

if ($args ~ post=140){
rewrite ^ http://[example.com/](http://example.com/) permanent;
} //如果query string中包含"post=140",永久重定向到example.com

location ~* \.(gif|jpg|png|swf|flv)$ {
valid_referers none blocked [www.jefflei.com](http://www.jefflei.com/) [www.leizhenfang.com](http://www.leizhenfang.com/);
if ($invalid_referer) {
return 404;
} //防盗链
}

全局变量
下面是可以用作 if 判断的全局变量

  • $args : #这个变量等于请求行中的参数,同$query_string
  • $content_length : 请求头中的 Content-length 字段。
  • $content_type : 请求头中的 Content-Type 字段。
  • $document_root : 当前请求在 root 指令中指定的值。
  • $host : 请求主机头字段,否则为服务器名称。
  • $http_user_agent : 客户端 agent 信息
  • $http_cookie : 客户端 cookie 信息
  • $limit_rate : 这个变量可以限制连接速率。
  • $request_method : 客户端请求的动作,通常为 GET 或 POST。
  • $remote_addr : 客户端的 IP 地址。
  • $remote_port : 客户端的端口。
  • $remote_user : 已经经过 Auth Basic Module 验证的用户名。
  • $request_filename : 当前请求的文件路径,由 root 或 alias 指令与 URI 请求生成。
  • $scheme : HTTP 方法(如 http,https)。
  • $server_protocol : 请求使用的协议,通常是 HTTP/1.0 或 HTTP/1.1。
  • $server_addr : 服务器地址,在完成一次系统调用后可以确定这个值。
  • $server_name : 服务器名称。
  • $server_port : 请求到达服务器的端口号。
  • $request_uri : 包含请求参数的原始 URI,不包含主机名,如:”/foo/bar.php?arg=baz”。
  • $uri : 不带请求参数的当前 URI,$uri 不包含主机名,如”/foo/bar.html”。
  • $document_uri : 与$uri 相同。

例:http://localhost:88/test1/test2/test.php
$host:localhost
$server_port:88
$request_uri:http://localhost:88/test1/test2/test.php
$document_uri:/test1/test2/test.php
$document_root:/var/www/html
$request_filename:/var/www/html/test1/test2/test.php

2.3 常用正则

  • . : 匹配除换行符以外的任意字符
  • ? : 重复 0 次或 1 次
  • + : 重复 1 次或更多次
  • * : 重复 0 次或更多次
  • \d :匹配数字
  • ^ : 匹配字符串的开始
  • ` : 匹配字符串的介绍
  • {n} : 重复 n 次
  • {n,} : 重复 n 次或更多次
  • [c] : 匹配单个字符 c
  • [a-z] : 匹配 a-z 小写字母的任意一个

小括号()之间匹配的内容,可以在后面通过$1来引用,$2表示的是前面第二个()里的内容。正则里面容易让人困惑的是\转义特殊字符。

2.4 rewrite 实例

_例 1_:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
http {

# 定义image日志格式
log_format imagelog '[$time_local] ' $image_file ' ' $image_type ' ' $body_bytes_sent ' ' $status;

# 开启重写日志
rewrite_log on;

server {
root /home/www;
location / {
# 重写规则信息
error_log logs/rewrite.log notice;
# 注意这里要用‘’单引号引起来,避免{}
rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)' /data?file=$3.$4;
# 注意不能在上面这条规则后面加上“last”参数,否则下面的set指令不会执行
set $image_file $3;
set $image_type $4;
}

location /data {
# 指定针对图片的日志格式,来分析图片类型和大小
access_log logs/images.log mian;
root /data/images;
# 应用前面定义的变量。判断首先文件在不在,不在再判断目录在不在,如果还不在就跳转到最后一个url里
try_files /$arg_file /image404.html;
}

location = /image404.html {
# 图片不存在返回特定的信息
return 404 "image not found\n";
}
}
}

对形如/images/ef/uh7b3/test.png的请求,重写到/data?file=test.png,于是匹配到location /data,先看/data/images/test.png文件存不存在,如果存在则正常响应,如果不存在则重写 tryfiles 到新的 image404 location,直接返回 404 状态码。

_例 2_:

1
rewrite ^/images/(.*)_(\d+)x(\d+)\.(png|jpg|gif)$ /resizer/$1.$4?width=$2&height=$3? last;

对形如/images/bla_500x400.jpg的文件请求,重写到/resizer/bla.jpg?width=500&height=400地址,并会继续尝试匹配 location。

_例 3_:
见  ssl 部分页面加密 。

参考

[WIP]Nginx 匹配 / 跳转站点

我们在网站部署的时候会遇到这样一种场景, 主站 / 目录跳转到 PC 端, 主站在手机模式下会跳转到手机站

对应的 PC 站和移动站的跳转一般情况下有这几种方式

  • 跳转适配(302)
  • 代码适配
  • 自适应(响应式)

我们处理的这种方式是属于代码适配, 但是来源的数据是不同的

PC 站 : 后端代码
手机端 : 前端代码(SPA)

参考