nginx(二十六)ngx_http_headers_nodule模块

一   add_header指令

1) ngx_http_headers_nodule是'filter'模块

2)特点:nginx接收到完整的'后端响应内容后',才会对'HTTP 响应头'部做'加工'处理

3) 允许通过修改 nginx.conf 配置文件,在返回给用户的响应中'添加(add)'任意的 HTTP 头部

注意: 是无脑'add'的能力,而不是'modify修改'能力

模块地址

1)有些'约定俗成响应头'是'浏览器能识别'的      -->"RFC文档中的通用头"

2) 有些'自定义'的响应头是通过'业务代码'来识别  -->"自定义标头"

①   基本描述

nginx(二十六)ngx_http_headers_nodule模块_第1张图片

细节点:

  1) nginx配置中'子块[eg:location]'中的'add_header'会'覆盖'父块'[eg:server全局]'

  2) 而'不是'继承

通俗: 每一层都可以从'上层继承' add_header;但是如果'当前层'添加了add_header,则是'覆盖'

+++++++++++++++++++  "分割线"  +++++++++++++++++++

1) add_header 指令  --> nginx在给客户端响应'content'的时候'增加'的响应头

备注:'默认'当且仅当'上述所列'的部分'2xx和3xx'状态码时才有效

2) 如果提供了'第三个参数 always',那么'无论状态码'是多少,'都会'带上

备注: 即使是'404的错误页面'

3) 每一层都可以'从上层继承 'add_header,

备注: 但是如果'子层'也添加了add_header,则'覆盖'所有的'add_header'

+++++++++++++'不能继承header问题'+++++++++++++

   1. 把外层的add_header'复制'一份写到内层

   2. 当add_header指令用的'很多'时,可以'抽离成.conf文件',通过include进行引入

nginx(二十六)ngx_http_headers_nodule模块_第2张图片

参考链接

②    add_header丢失或失效

# 特点:坑比较多的指令,处理阶段比location晚;

# 如果写在location中,并且涉及rewrite其它location

# 那么'上一个location中'未被处理的add_header会丢失

location = /wzj1 {
    add_header name1 18;
    rewrite / /wzj2;
}

location = /wzj2 {
    add_header name2 28;
    return 204;
}

说明: 'name1'这个头'丢失'

典型场景:try_files 指令导致 add_header 失效

失效根因: 相关指令'涉及内部internal重定向',导致add_header指令对应的阶段'未执行','丢失'

③  always可选参数   对任意响应码都生效

应用场景1: add_header 在 '错误(4xx)'页面 使用

应用场景2: DELETE请求方法'返回202',导致add_header增加的响应头'丢失'

应用场景3: 实际工作中往往前端需要'捕获服务端异常响应',在nginx跨域add_header上加上always

备注3:跨域涉及'OPTIONS'预检请求的'204'状态码,add_header支持'该状态码'

深层次: 有些'服务'通过获取'特定'的Header来处理业务

nginx跨域不生效

补获跨域错误 

版本问题导致的报错 

遗留:

 1) add_header 后面的'always参数'可以用'变量'吗?  --> "不可以"

 2) add_header 的'Header'可以有'$'特殊符号,但是'nginx不会处理作为变量',而是'$字面字符'

 3) add_header 的'header_value'可以使用'$var'变量

location /method {

  if ( $request_method = 'DELETE' ){
      set $flag "always";
  }
  
  add_header Always $flag;
}

④  响应头重复

建议:nginx中同一'level',不能有相同的'响应头'

1)响应头重复的原因

1、请求'链路'比较长  eg --> 'waf --> APIG --> nginx -->后端服务' 

  说明:每一层都'添加'同一个响应头,多次'添加'导致;一定要了解'组网'形式

2、nginx自身'add_header '同一个头'多次'导致

2)响应头重复的一些潜在问题

1)在'不同厂家'或者同一厂家'不同版本'的浏览器,对'重复'响应头的处理存在'差异',要具体分析

2)三种策略:将前面的'覆盖'、多个数值'合并'、'并存'

3)可能'引发'的问题

 场景:可能导致设置的'响应头'防护'减弱'甚至'失效' 

 原因:某些响应头'只能识别特定'的参数

3)如何解决

'隐藏'上游的响应头,不要出现'响应头重复'的问题

 proxy_hide_header

4)最佳实践

1)多个部门进行'协调',只在所有'请求流'的'入口'处设置,例如nginx或者APIG

2)'业务代码层面'不再设置

⑤  add_header不生效常见原因

1) '作用域'导致'add_header'被覆盖  --> "如何合理利用"

2)  add_header响应头'重复',导致浏览器'错误'识别响应头

3)  部分'非2xx和3xx'状态码和'跨域'导致add_header'失效'  --> 需要"always"参数

4)  uri重写,涉及'nginx11个阶段'导致'add_hader'丢失

5)  add_header 响应头的'Header'带'特殊字符[下划线等]'导致失效 

6)  多个'location'路由匹配'客户端请求',正则的优先级导致'下面'location中add_header未生效

⑥  添加安全响应头

涉及的安全响应头

Strict-Transport-Security   HSTS

X-Content-Type-Options

X-Download-Options

X-Frame-Options  其它参考

X-XSS-Protection

Content-Security-Policy

# 禁用浏览器的类型猜测
add_header X-Content-Type-Options "nosniff";
# 兼容一些老的浏览器
add_header X-Download-Options "noopen";
# XSS防护的
add_header X-XSS-Protection "1; mode=block";                                                                                                                                                                               
# 告诉浏览器强制https请求  1年 --> 365天
add_header Strict-Transport-Security "max-age=31536000;includeSubDomains";
# 消减点击劫持
add_header X-Frame-Options "DENY";

黑客工具之AppScan详细使用教程

⑦  default_type

+++++++++++ "nginx设置" +++++++++++

1) default_type application/octet-stream; # 默认的'Content-Type'

特点: 如果根据'文件后缀'不能判定'content-type'的资源类型,则返回'默认的default_type'

2) charset utf-8;                         # 避免'乱码'

解读: 

  1) nginx在返回'资源类型'的时候,首先根据'文件名后缀'找'mime.types'文件

  2) 找不到则'返回'default_type + charset

  3) 对应'浏览器'响应头:Content-Type 'application/octet-stream; charset=utf-8';

                 +++++++++++ '分割线' +++++++++++

add_header Content-Type "application/octet-stream; charset=utf-8";

和上述方式的'区别':default_type 、charset 可以在'子域'定义add_header,不会被覆盖

⑧  add_header用在debug阶段

背景: 

  1) ngin作为'反向代理的时候',后端有'很多节点'

  2) 为了让'开发'知道请求落在'哪个'后端节点上,便于'快速'排查

  3) 前提: 非'线上的生产环境'、并且没有统一的'日志'查看平台

+++++++++++++++++ "具体的实施" +++++++++++++++++

add_header BACK_IP   $upstream_addr;

add_header BACK_CODE $upstream_status

⑨  add_header防止重复响应头的另一种做法

思考: nginx能'隐藏'上游的多个'相同的响应头'吗?

前提: 客户端'浏览器'有'两个'响应头

+++++++++ "具体实施"  +++++++++

map $upstream_http_cache_control $cache_control {

  default "";

  "" "no-store";

}

# 后端'没有返回'对应的响应头,则nginx自身'添加'默认值

add_header Cache-Control $cache_control always

'暗含'的知识点:

  1) map中只有'变量'被使用的时候,才会执行'map'

  2) map作用一个完整'请求-响应'整个'生命'周期

  3) add_header指令'后于'$upstream_http_name变量执行 ->

     保证:add_header执行的时候'$upstream_http_name'不为空

# 进一步"补充"思考: if($upstream_http_name) { 先隐藏上游,再添加自定义头的值} 保证唯一

⑩   利用不同作用域add_header覆盖的特点进行缓存时效控制

需求场景: 

  1) 默认'全局'不使用'缓存'            --> 'http|server级别颗粒度'

  2) location /video/special        --> nginx'控制'缓存时间

  3) location /wzj                  --> 后端'控制'缓存时间

注意: location和if in location中的'add_header'是属于'同一层级'的

思路: 把公共的'add_header'部分'抽离'出来放到'add_header.conf'中

nginx(二十六)ngx_http_headers_nodule模块_第3张图片

二    expires指令

①  基本描述

说明: 这个响应头'控制'浏览器的'访问'行为,控制'页面资源'在'浏览器缓存'的时间

nginx(二十六)ngx_http_headers_nodule模块_第4张图片

②   与map结合

nginx(二十六)ngx_http_headers_nodule模块_第5张图片

③  前置知识

1)浏览器在磁盘存放缓存的位置

'自己linux': /home/kiosk/.config/google-chrome/Default/Application Cache/Cache

nginx(二十六)ngx_http_headers_nodule模块_第6张图片

原理参考

++++++++++"浏览器的缓存策略"++++++++++

说明: Etag的'优先级'比Last-Modified高

细节: 

  1) 判断该资源'在浏览器'是否有'上一次缓存'的Etag、Last-Modified

  2) 如果'有'根据'优先级别'填充'对应的请求头'中

304: 浏览器可以使用'本地的过期缓存'

nginx(二十六)ngx_http_headers_nodule模块_第7张图片

1)Etag响应头

nginx(二十六)ngx_http_headers_nodule模块_第8张图片

2)Last-Modified 响应头

Last-Modified的属性标记'此文件'在服务器端'最后被修改'的时间

3)IF-Modified-Since 请求头

nginx(二十六)ngx_http_headers_nodule模块_第9张图片

4)IF-None-Match请求头

nginx(二十六)ngx_http_headers_nodule模块_第10张图片

5)强制浏览器访问不使用本地缓存

nginx(二十六)ngx_http_headers_nodule模块_第11张图片

说明: 用'chrome'演示浏览器携带'请求头' -->相当于'ctrl+f5'快捷键

备注: 

 1) HTTP/1.0 caches协议'没有'实现'Cache-Control [在http1.1实现]'

 2) HTTP/1.0'实现'了 Pragma: no-cache的功能

nginx(二十六)ngx_http_headers_nodule模块_第12张图片

HTTP请求头和响应头中cache-control的区别

F5和CTRL+F5两种刷新的区别

④  nginx决策浏览器过期缓存是否有效

nginx服务端: 会计算(使用cpu)'资源的Etag',与请求头的'做比较'

1)etag

nginx(二十六)ngx_http_headers_nodule模块_第13张图片

nginx(二十六)ngx_http_headers_nodule模块_第14张图片

etag用java解析成可读的案例

2)if_modified_since

nginx(二十六)ngx_http_headers_nodule模块_第15张图片

nginx(二十六)ngx_http_headers_nodule模块_第16张图片

nginx 通过 expires、etag、if_modified_since 指令实现对客户端缓存的控制

强缓存和协商缓存、Last-Modified和ETag响应头、If-None-Match和If-Modified-Since请求头

3)not_modified过滤模块原理图

[1]、If-Match 

基于: 填充'Etag'

nginx(二十六)ngx_http_headers_nodule模块_第17张图片

[2]、If-Unmodified-SInce

说明: 基于资源的'修改'时间,填充'Last-Modified'响应头的值

nginx(二十六)ngx_http_headers_nodule模块_第18张图片

[3]、原理

nginx(二十六)ngx_http_headers_nodule模块_第19张图片

 [4]、curl模拟发送请求

关注点: 'nginx'如何处理这些'请求头'

演示: 

  1) 浏览器拿着'过期缓存'的请求访问nginx,nginx'依据'哪些指令和头部的比较过程

  2) 决定给浏览器返回'304'、'200'、'412'

nginx(二十六)ngx_http_headers_nodule模块_第20张图片

⑤  expires案例讲解

体会: 设置不同的'expires'指令,返回给'客户端的响应头',进而'如何控制'浏览器的访问行为

nginx决策'浏览器过期缓存'是否有效

观察: 'expires'不同的取值,'响应头'的一些现象

演示: 

  1) nginx作为web'静态'服务器

  2) 通过'expires'告诉浏览器'资源(缓存)'的过期时间 -->nginx'控制'浏览器去使用缓存

1)expire 10m

nginx(二十六)ngx_http_headers_nodule模块_第21张图片

思考:为什么设置的是'10m'后过期,但是在'缓存有效期'访问总是304,而'不是'200(from cache)

可能原因:

   1)chrome'浏览器'行为

   请求头自带: 'Cache-Control: max-age=0'

备注:跟'浏览器的厂商'和'浏览器的版本'都有关

   2)因为'Expires'使用的是绝对日期,如果服务端和客户端'时钟不同步'的话

原因 

2)expires max

nginx(二十六)ngx_http_headers_nodule模块_第22张图片

3)expires -1

nginx(二十六)ngx_http_headers_nodule模块_第23张图片

4)expires epoch

nginx(二十六)ngx_http_headers_nodule模块_第24张图片

5)expires @23h10m

nginx(二十六)ngx_http_headers_nodule模块_第25张图片

三   add_trailer

①   基本描述

解读: 为将自定义的'特殊头信息'添加到'body之后'

nginx(二十六)ngx_http_headers_nodule模块_第26张图片

②   Transfer-Encoding   TE

说明: 最新的 'HTTP 规范'里,只定义了一种'传输编码' -->'分块编码[chunked]'

补充: '响应头'

1)背景

++++++++++ "为什么要使用分块传输"  ++++++++++

1)有时候,web服务器生成'动态'HTTP Response是'无法'在'Header(Content-length)'就确定'消息大小'的 

2) 服务端通过在响应头设置'Transfer-Encoding: chunked',表示Content Body将用'Chunked编码'传输内容

场景: 而对于'动态的内容'或者在'发送数据前不能判定长度'的情况下,可以使用'分块的方法'来传送编码

补充: 也可以传输'大'文件

2)分块传输原理

nginx(二十六)ngx_http_headers_nodule模块_第27张图片

3)nginx使用chunk传输前提

1) 分块传输是'HTTP1.1'引入的,利用'TCP的连接'复用,'多次'传输,使用HTTP1.1'长连接'

+++++++++++++"(1)nginx配置长连接"+++++++++++++

proxy_http_version 1.1;           # 默认http版本为1.0
proxy_set_header Connection "";   # 置空串后,相当于不传递Connection,HTTP1.1默认就是'keep-alive'

+++++++++++++"(2)nginx支持chunk传输"+++++++++++++
	
chunked_transfer_encoding on      # 默认就是'开启的'

chunked_transfer_encoding 

nginx(二十六)ngx_http_headers_nodule模块_第28张图片

③   Trailer概念

强调:

  1) Trailer生效是'依赖'于分块传输

  2) 必须'显示'的设置响应头"Transfer-Encoding: chunked"

  1)Vertx创建HTTP Server服务

nginx(二十六)ngx_http_headers_nodule模块_第29张图片

2)nginx作为代理的配置

nginx(二十六)ngx_http_headers_nodule模块_第30张图片

 3)客户端访问并抓包

说明: 客户端也是'172.25.2.157'

抓包命令: tcpdump -n -X -i any port 80 -w /tmp/chunk.cap

思考: 为什么'原始的trailer'会丢失?

备注: 当'不设置add_trailer',在客户端没有获取到原始传递的'trailer'

nginx(二十六)ngx_http_headers_nodule模块_第31张图片

nginx(二十六)ngx_http_headers_nodule模块_第32张图片

++++++++++++'url'++++++++++++

1) $uri和$document_uri标识'解码后'的请求路径,'不含'查询参数-->可能有'CRCF'攻击

2) $request_uri标识完整的'没有被解码'的url

   --> "不带主机名",带参数  [/a/b.html?arg=10]-->"推荐"

补充:$query_string 与$args'相同'

nginx非root用户启动

你可能感兴趣的:(nginx,1024程序员节,add_header)