1.HTTP过滤模块
调用流程
# 接收HTTP头部
preaccess阶段 limit_req模块 limit_conn模块
access阶段 access模块 auth_basic模块
content阶段 concat模块 static模块
header过滤模块 image_filter gzip
# 发送 HTTP头部
body过滤模块 image_filter gzip
返回响应-加工响应内容
HTTP 过滤模块
- copy_filter: 复制包体内容
- postpone flter: 处理子请求
- header filter: 构造响应头部
- write filter: 发送响应
sub模块
替换响应中的字符串
ngx_http_sub_filter_module 模块默认未编译进Nginx, --with-http_sub_module启动
server {
listen 8303;
server_name lcalhost;
error_log logs/myerror.log info;
location / {
sub_filter 'Nginx.oRg' '$host/nginx'; # 不区分大小写, 替换 nginx.org 为 localhost/nginx
sub_filter 'nginX.cOm' '$host/nginx'; # 不区分大小写, 替换 nginx.com 为 localhost/nginx
#sub_filter_once on;
sub_filter_once off; # 是否只替换一次
#sub_filter_last_modified off;
sub_filter_last_modified on; # 是否改变文件最后修改日期
}
}
curl localhost:8303/
# 输出内容
# nginx.org 统一 替换为 localhost/nginx
# nginx.com 统一 替换为 localhost/nginx
# <a href="http://localhost/nginx/">localhost/nginx</a>.<br/>
# Commercial support is available at
# <a href="http://localhost/nginx/">localhost/nginx</a>.</p>
# 头部返回 :
# ETag: W/"640d6cbb-264"
# Last-Modified: Sun, 12 Mar 2023 06:10:03 GMT
响应前后添加内容 addition
Syntax: add_before_body uri;
Default: —
Context: http, server, location
Syntax: add_after_body uri;
Default: —
Context: http, server, location
Syntax: addition_types mime-type ...;
Default: addition_types text/html;
Context: http, server, location
- 增加内容通过新的url响应完成
- 默认未编译, 使用 --with-http_addition_module 添加
2.Nginx变量的运行原理
变量的提供模块与使用模块
1.nginx启动
提供变量的模块 preconfiguration中定义新的变量解析出变量的方法 -> 变量名
2.HITTP头部读取完毕
使用变量的模块 比如http的access日志,解析nginx.conf时定义变量使用方式 变量值处理请求
变量的特性
惰性求值:变量值可以时刻变化,其值为使用的那-时刻的值
存放变量的哈希表
Context:http
hash_bucket_size 64;
hash_max_size 1024;
例子
log_format vartest '$remote_addr - $remote_user [$time_local] "$request" '
'$status bytes_sent=$bytes_sent body_bytes_sent=$body_bytes_sent "$http_referer" '
'"$http_user_agent" "$sent_http_abc"';
server {
server_name test1.com localhost;
#error_log logs/myerror.log debug;
access_log logs/vartest.log vartest;
listen 9090;
location / {
set $limit_rate 10k;
return 200 '
arg_a: $arg_a,arg_b: $arg_b,args: $args
connection: $connection,connection_requests: $connection_requests
cookie_a: $cookie_a
uri: $uri,document_uri: $document_uri, request_uri: $request_uri
request: $request
request_id: $request_id
server: $server_addr,$server_name,$server_port,$server_protocol
tcpinfo: $tcpinfo_rtt, $tcpinfo_rttvar, $tcpinfo_snd_cwnd, $tcpinfo_rcv_space
host: $host,server_name: $server_name,http_host: $http_host
limit_rate: $limit_rate
hostname: $hostname
content_length: $content_length
status: $status
body_bytes_sent: $body_bytes_sent,bytes_sent: $bytes_sent
time: $request_time,$msec,$time_iso8601,$time_local
';
}
}
# 测试 注意如果参数是多个 要用双引号 括起来
curl -H 'Content-Length: 0' -H 'Cookie: a=c1' 'localhost:8304?a=11&b=222'
# 返回
# arg_a: 11,arg_b: ,args: a=11
# connection: 19,connection_requests: 1
# cookie_a: c1
# uri: /,document_uri: /, request_uri: /?a=11
# request: GET /?a=11 HTTP/1.1
# request_id: 50ab2a1577458bd272aa83bc0dcc3796
# server: 127.0.0.1,test1.com,8304,HTTP/1.1
# tcpinfo: 0, 0, 10, 43690
# host: localhost,server_name: test1.com,http_host: localhost:8304
# limit_rate: 10240
# hostname: ecs04.novalocal
# content_length: 0
# status: 200
# body_bytes_sent: 0,bytes_sent: 0
# time: 0.000,1679110855.841,2023-03-18T11:40:55+08:00,18/Mar/2023:11:40:55 +0800
HTTP 框架提供的变量
- HTTP 请求相关的变量
- TCP 连接相关的变量
- Nginx 处理请求过程中产生的变量
- 发送HTTP 响应时相关的变量
- Nginx 系统变量
HTTP请求相关的变量 (一)
- arg_参数名 URL中某个具体参数的值
- query_string 与 args 变量完全相同
- args 全部URL参数
- is_args 如果请求URL中有参数则返回?否则返回空
- content_length HTTP请求中标识包体长度的 Content-Length 头部的值
- content_type 标识请求包体类型的Content-Type 头部的值
- uri 请求的 URI(不同于URL,不包括?后的参数)
- document_uri 与uri完全相同
- request uri 请求的URL (包括URI以及完整的参数)
- scheme 协议名,例如HTTP或者HTTPS
- request_method 请求方法,例如 GET 或者 POST
- request_length 所有请求内容的大小,包括请求行、头部、包体等
- remote_user 由HTTP Basic Authentication 协议传入的用户名
- request_body_file 临时存放请求包体的文件 1. 如果包体非常小则不会存文件 2. client_body_in_fle only 强制所有包体存入文件,且可决定是否删除
- request_body 请求中的包体,这个变量当且仅当使用反向代理,且设定用内存暂存包体时才有效
- request 原始的url请求,含有方法与协议版本,例如GET /?a=1&b=22 HTTP/1.1
host
- 先从请求行中获取
- 如果含有 Host 头部,则用其值替换掉请求行中的主机名
- 如果前两者都取不到,则使用匹配上的 server_name
http_头部名字
返回一个具体请求头部的值
特殊:
- http_host
- http_user_agent
- http_referer
- http_via
- http_x_forwarded_for
- http_cookie
TCP 连接相关的变量
- binary_remote_addr 客户端地址的整型格式,对于IPv4是4字节,对于IPv6是16字节
- connection 递增的连接序号
- connection_requests 当前连接上执行过的请求数,对keepalive连接有意义
- remote_addr 客户端地址
- remote_port 客户端端口
- proxy_protocol_addr 若使用了proxy_protocol协议则返回协议中的地址,否则返回空
Nginx 处理请求过程中产生的变量
- request_time 请求处理到现在的耗时,单位为秒,精确到毫秒
- server_name 匹配上请求的server name值
- https 如果开启了TLS/SSL,则返回on,否则返回空
- request_completion 若请求处理完则返回OK,否则返回空
- request_id 以16进制输出的请求标识id,该id共含有16个字节,是随机生成的
- request_filename 待访问文件的完整路径
- document_root 由URI和root/alias规则生成的文件夹路径
- realpath_root 将document root中的软接等换成真实路径
- limit_rate 返回客户端响应时的速度上限,单位为每秒字节数。可以通过 set 指令修改对请求产生效果
发送HTTP响应时相关的变量
- body_bytes_sent 响应中body包体的长度
- bytes_sent 全部http响应的长度
- status http响应中的返回码
- sent_trailer 名字 把响应结尾内容里值返回
sent http 头部名字: 响应中某个具体头部的值
特殊处理
- sent_http_content_type
- sent_http_content_length
- sent_http_location
- sent_http_last_modified
- sent_http_connection
- sent_http_keep_alive
- sent_http_transfer_encoding
- sent_http_cache_control
- sent_http_link
Nginx 系统变量
- time_local 以本地时间标准输出的当前时间,例如14/Nov/2018:15:55:37 +0800
- time_iso8601 使用ISO 8601标准输出的当前时间,例如2018-11-14T15:55:37+08:00
- nginx_version Nginx版本号
- pid 所属worker进程的进程id
- pipe 使用了管道则返回 p,否则返回
- hostname 所在服务器的主机名,与hostname命令输出一致
- msec 1970年1月1日到现在的时间,单位为秒,小数点后精确到毫秒
3.防盗链 referer
场景 A.com网站有很多自己的图片资源 ,B.com网站直接把A的图片放在自己网站当自己资源显示。
原理: 当在B网站显示A网站的图片url ,如 A.com/a.png , 请求这个地址时会携带上当时请求的域名也就是 B.com 携带在http头上referer 。A.com 会判断检查referer是否是自己的域名,发现是 B.com则拒绝返回图片。
默认编译进Nginx,通过--without-http_referer_module禁用
valid referers
可同时携带多个参数,表示多个 referer 头部都生效
参数值
- none 允许缺失referer头部的请求访问.
- block 允许referer头部没有对应的值的请求访问
- server_names 若referer中站点域名与server_name中本机域名某个匹配则允许该请求访问
- 表示域名及URL的字符串,对域名可在前缀或者后缀中含有*通配符,若referer头部的值匹配字符串后,则允许访问
- 正则表达式,若referer头部的值匹配正则表达式后,则允许访问
invalid_referer 变量
- 允许访问时变量值为空
- 不允许访问时变量值为1
例子
server {
listen 8305;
server_name test1.com;
error_log logs/myerror.log debug;
root html;
location /{
valid_referers none blocked server_names
*.test1.pub www.test1.org.cn/nginx/
~\.google\.;
if ($invalid_referer) {
return 403;
}
return 200 'valid\n';
}
}
# 测试
curl -H 'referer: http://www.test1.org.cn/ttt' test1.com:8305/ # 403
curl -H 'referer: http://www.test1.pub/ttt' test1.com:8305/ # valid
curl -H 'referer: ' test1.com:8305/ # valid
curl test1.com:8305/ # valid
curl -H 'referer: http://www.test1.com' test1.com:8305/ # 403
curl -H 'referer: http://test1.com:8305' test1.com:8305/ # valid
curl -H 'referer: http://image.baidu.com/search/detail' test1.com:8305/ # 403
curl -H 'referer: http://image.google.com/search/detail' test1.com:8305/ # valid
--add-module=./module/nginx-http-concat-master --with-http_sub_module --with-http_secure_link_module
4.防盗链2:secure link 模块
ngx_http_secure_link_module 默认未编译nginx --with-http_secure_link_module 添加
变量
- secure_link
- secure_link_expires
实现
通过验证URL中哈希值的方式防盗链
功能
过程
- 由某服务器(也可以是nginx)生成加密后的安全链接url,返回给客户端
- 客户端使用安全url访问nginx,由nginx的secure link变量判断是否验证通过
原理
- 哈希算法是不可逆的
- 客户端只能拿到执行过哈希算法的URL
- 仅生成URL的服务器、验证URL是否安全的nginx这者,才保存执行哈希算法前的原始字符串
原始字符串通常由以下部分有序组成:
- 资源位置,例如HTTP中指定资源的URI,防止攻击者拿到安全URL后可以访问任意资源
- 用户信息,例如用户P地址,限制其他用户盗用安全
- 时间戳,使安全URL及时过期。
- 密钥,仅服务器端拥有,增加攻击者猜测出原始字符串难度
变量值及带过期时间的
secure_link
- 值为空字符串 验证不通过
- 值为 0 URL过期
- 值为 1 验证通过
secure_link_expires
时间戳的值
变量值及带过期时间的配置
1. 命令行生成安全链接
原请求: /testl.txt?md5=md5生成值&expires=时间戳 (如2147483647)
生成md5: echo -n '时间戳URL客户端IP密钥' | openssl md5 -binary | openssl base64 | tr +/ - | tr -d =
2. Nginx 配置
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri$remote_addr secret"; # secret可以是 自己的定义个key 也可以不写
只对URI加密
命令行生成安全链接
link
#生成的安全请求
/prefix/md5/link
#生成md5 link+secret
echo -n 'linksecret' | openssl md5 –hex
#Nginx配置
secure_link_secret secret;
例子
server {
listen 8306;
server_name localhost;
error_log logs/myerror.log info;
default_type text/plain;
location /{ # 完整的校验
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri$remote_addr secret"; # 这里定义规则 时间戳+请求地址+ip地址 secret
if ($secure_link = "") { # 为空验证不通过
return 403;
}
if ($secure_link = "0") { # url过期
return 410;
}
# $secure_link == 1 验证通过
return 200 '$secure_link:$secure_link_expires\n';
}
location /p/ { # 测试简单URI校验
secure_link_secret secret; # secret可以是自定义的生成key
if ($secure_link = "") {
return 403;
}
rewrite ^ /secure/$secure_link;
}
location /secure/ {
alias html/;
internal;
}
}
cd html
echo 'test11111' > test1.txt
# 时间戳生成 1705746324
# 测试完整的校验
# 生成md5
echo -n '1705746324/test1.txt171.35.40.133 secret' | openssl md5 -binary | openssl base64 | tr +/ - | tr -d =
# 输出
# UWdn7fs8wXGbNv-2T1HIGw
# 测试地址
curl '171.35.40.133:8306/test1.txt?md5=UWdn7fs8wXGbNv-2T1HIGw&expires=1705746324'
# 输出
#test11111
# 测试简单URI的
# md5生成 uri+secret , secret可以是自定义的生成key
echo -n 'test1.txtsecret' | openssl md5 -hex
# 92347d744b5a9a666debebe2c13bf2c7
# 测试
curl '171.35.40.133:8306/p/92347d744b5a9a666debebe2c13bf2c7/test1.txt'
# 输出
#test11111
5.map
ngx_http_map_module ,默认编译进Nginx
基于已有变量,使用类似 switch{case: ... default: ..}的语法创建新变量,为其他基于变量值实现功能的模块提供更多的可能性
已有变量
字符串
- 个或者多个变量
- 变量与字符串的组合
default 规则
- 没有匹配到任何规则时使用default
- 缺失default时,返回空字符串给新变量 规则
case 规则
- 字符串严格匹配
- 使用hostnames指令,可以对域名使用前 缀*泛域名匹配
- 使用hostnames指令,可以对域名使用后 缀*泛域名匹配
和*正则表达式匹配,后者忽略大小写
其他
- 使用include语法提升可读性
- 使用volatile禁止变量值缓存
例子
map $http_host $name {
hostnames;
default 0;
~map\.test\w+\.org.cn 1;
*.test.org.cn 2;
map.test.tech 3;
map.test.* 4;
}
map $http_user_agent $mobile {
default 0;
"~Opera Mini" 1;
}
server {
listen 8307;
server_name localhost;
default_type text/plain;
location /{
return 200 '$name:$mobile\n';
}
}
# 测试 map
curl -H 'Host: map.test.tech' localhost:8307
# 输出 3:0:
curl -H 'Host: map2.test.tech' localhost:8307
# 输出 0:0:
curl -H 'Host: map.test.tech3' localhost:8307
# 输出 4:0:
6.split_client 模块
ngx_http_split_clients_module 默认编译nginx
实现 AB 测试:
基于已有变量创建新变量,为其他AB测试提供更多的可能性
- 对已有变量的值执行MurmurHash2算法得到32位整型哈希数字,记为hash
- 32位无符号整型的最大数字2^32-1,记为max
- 哈希数字与最大数字相除hash/max,可以得到百分比percent
- 配置指令中指示了各个百分比构成的范围,如0-1%.1%-5%等,及范围对应的值
- 当percent落在哪个范围里,新变量的值就对应着其后的参数
例子
split_clients "${http_testcli}" $variant {
0.51% .one;
20.0% .two;
50.5% .three;
#40% .four;
* "";
}
server {
listen 8308;
server_name localhost;
error_log logs/error.log debug;
default_type text/plain;
location /{
return 200 'ABtestfile$variant\n';
}
}
# 测试
curl -H 'testcli: 5897sadfasfsafsa98' localhost:8308
# 输出 ABtestfile.three
curl -H 'testcli: 5897sadfasfsafsa8211' localhost:8308
# 输出 ABtestfile.two
7. geo-ip转换变量
根据客户端地址创建新变量: geo 模块 ,ngx_http_geo_module 默认编译进nginx
规则
- 如果geo指令后不输入remote_addr变量作为IP地址
- {}内的指令匹配: 优先最长匹配
- 通过IP地址及子网掩码的方式,定义IP范围,当IP地址在范围内时新变量使用其后的参数值
- default指定了当以上范围都未匹配上时,新变量的默认值通过proxy指令指定可信地址(参考realip模块),此时$remote_addr的值为- - - XForwarded-For头部值中最后一个IP地址
- proxy recursive允许循环地址索
- include,优化可读性
- delete删除指定网络
例子
geo $country {
default ZZ;
#include conf/geo.conf;
proxy 116.62.160.193;
127.0.0.0/24 US;
127.0.0.1/32 RU;
10.1.0.0/16 RU;
192.168.1.0/24 UK;
}
server {
listen 8309;
server_name localhost;
location /{
return 200 '$country\n';
}
}
curl -H 'X-Forwarded-For: 10.1.0.0,127.0.0.2' localhost:8309
curl -H 'X-Forwarded-For: 10.1.0.0,127.0.0.1' localhost:8309
curl -H 'X-Forwarded-For:10.1.0.0,127.0.0.1,1.2.3.4' localhost:8309
curl -H 'X-Forwarded-For: 10.1.0.0,127.0.0.1,192.168.1.123' localhost:8309
8.geo 基于MaxMind
ngx_http_geoip_module 默认未编译进nginx , --with-http_geoip_module添加
geoip_city 指令提供的变量
$geoip_latitude #纬度
$geoip_longitude #经度
$geoip_city_continent code #属于全球哪个洲,例如EU或者AS与geoip country指令生成的变量重叠
$geoip_city_country_code #两个字母的国家代码,比如CN或者US三个字母的国家代码,比如CHN或者USA$geoip city country code3 :
$geoip_city_country_name #国家名称,例如“China”,“United States洲或表省的编码,例如02Sgeoip region:
Sgeoip_region name #洲或者省的名称,例如Zhejiang或者Saint Petersburg
$geoip_city #城市名
$geoip_postal_code #邮编号
例子
geoip_country /usr/local/nginx/conf/GeoIP/GeoIP.dat;
geoip_city /usr/local/nginx/conf/GeoIP/GeoLiteCity.dat;
geoip_proxy 116.62.160.193/32;
geoip_proxy_recursive on;
server {
listen 8310;
server_name localhost;
error_log logs/myerror.log info;
keepalive_requests 2;
keepalive_timeout 75s 20;
location /{
return 200 'country:$geoip_country_code,$geoip_country_code3,$geoip_country_name
country from city:$geoip_city_country_code,$geoip_city_country_code3,$geoip_city_country_name
city:$geoip_area_code,$geoip_city_continent_code,$geoip_dma_code
$geoip_latitude,$geoip_longitude,$geoip_region,$geoip_region_name,$geoip_city,$geoip_postal_code
';
}
}
# 安装libmaxminddb插件
yum install libmaxminddb-devel
# 安装 geoip插件
yum install -y GeoIP-devel.x86_64
# 下载数据库
# 安装编译
./configure --add-module=./module/nginx-http-concat-master --with-http_sub_module --with-http_secure_link_module --with-http_geoip_module
## 安装 下载dat 字典
https://dev.maxmind.com/geoip/legacy/downloadable/
# 测试
curl -H 'X-Forwarded-For: 77.48.21.58,104.248.224.193,121.8.98.197' localhost:8310
# country:CN,CHN,China
curl -H 'X-Forwarded-For: 77.48.21.58,104.248.224.193' localhost:8310
# country:US,USA,United states
9.keepalive 行为控制的指令
多个HTTP请求通过复用TCP连接
效果
- 减少握手次数
- 通过减少并发连接数减少了服务器
- 资源的消耗
- 降低TCP拥塞控制的影响
协议
Connection头部:
- close:表示请求处理完即关闭连接
- keepalive: 表示复用连接处理下一条请求
Keep-Alive头部:
其值为timeout=n后面的数字n单位是秒,告诉客户端连接至少保留n秒
指令
Syntax: keepalive_disable none | browser ...;
Default: keepalive_disable msie6;
Context: http, server, location
Syntax: keepalive_requests number;
Default: keepalive_requests 100;
Context: http, server, location
Syntax: keepalive_timeout timeout [header_timeout];
Default: keepalive_timeout 75s;
Context: http, server, location