nginx 学习3-http模块2

333 阅读5分钟

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 过滤模块

  1. copy_filter: 复制包体内容
  2. postpone flter: 处理子请求
  3. header filter: 构造响应头部
  4. 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
  1. 增加内容通过新的url响应完成
  2. 默认未编译, 使用 --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 框架提供的变量

  1. HTTP 请求相关的变量
  2. TCP 连接相关的变量
  3. Nginx 处理请求过程中产生的变量
  4. 发送HTTP 响应时相关的变量
  5. 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指令后不输入address,那么默认使用address,那么默认使用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