
1 定义ngx_http_headers_filter 函数 定义在 src/http/modules/ngx_http_headers_filter_module.c:LINE168staticngx_int_tngx_http_headers_filter(ngx_http_request_t*r){ngx_str_tvalue;ngx_uint_ti,safe_status;ngx_http_header_val_t*h;ngx_http_headers_conf_t*conf;if(r!r-main){returnngx_http_next_header_filter(r);}confngx_http_get_module_loc_conf(r,ngx_http_headers_filter_module);if(conf-expiresNGX_HTTP_EXPIRES_OFFconf-headersNULLconf-trailersNULL){returnngx_http_next_header_filter(r);}switch(r-headers_out.status){caseNGX_HTTP_OK:caseNGX_HTTP_CREATED:caseNGX_HTTP_NO_CONTENT:caseNGX_HTTP_PARTIAL_CONTENT:caseNGX_HTTP_MOVED_PERMANENTLY:caseNGX_HTTP_MOVED_TEMPORARILY:caseNGX_HTTP_SEE_OTHER:caseNGX_HTTP_NOT_MODIFIED:caseNGX_HTTP_TEMPORARY_REDIRECT:caseNGX_HTTP_PERMANENT_REDIRECT:safe_status1;break;default:safe_status0;break;}if(conf-expires!NGX_HTTP_EXPIRES_OFFsafe_status){if(ngx_http_set_expires(r,conf)!NGX_OK){returnNGX_ERROR;}}if(conf-headers){hconf-headers-elts;for(i0;iconf-headers-nelts;i){if(!safe_status!h[i].always){continue;}if(ngx_http_complex_value(r,h[i].value,value)!NGX_OK){returnNGX_ERROR;}if(h[i].handler(r,h[i],value)!NGX_OK){returnNGX_ERROR;}}}if(conf-trailers){hconf-trailers-elts;for(i0;iconf-trailers-nelts;i){if(!safe_status!h[i].always){continue;}r-expect_trailers1;break;}}returnngx_http_next_header_filter(r);}2 目的它是 ngx_http_headers_filter_module 模块的核心 而这个模块实现了 add_header、expires 等配置指令。1. 设计目的 这个函数被设计出来只为一个清晰的目标 在响应头发送给客户端之前的最后一刻 根据管理员在配置文件中写下的指令 批量化、自动化地向响应头中添加自定义字段 如 Cache-Control、X-Frame-Options、Access-Control-Allow-Origin 等 或者修改 Expires 和 Cache-Control 这类缓存相关头部。 它把“添加/修改响应头”这一常见的运维需求 从各个业务模块中抽离出来变成一个 声明式配置 加一个 统一执行点2. 为什么需要设计这样一个函数 如果没有这个函数Nginx 的配置系统将面临几个明显的问题。2-1 用户需要灵活添加任意响应头 在日常 Web 运维中经常需要添加自定义 HTTP 响应头 如果没有一个集中的机制用户想要添加一个简单的 X-Frame-Options 头 就需要修改 Nginx 源码或者依赖特定的第三方模块。 ngx_http_headers_filter 配合 add_header 指令 允许用户直接在配置文件中声明想要的头部无需编写任何 C 代码。 这就是它存在的根本原因——将 HTTP 头部的定制能力交给运维人员。2-2 缓存控制的集中管理 HTTP 缓存控制涉及 Expires 和 Cache-Control 头 它们的生成规则有一定复杂性需要考虑当前时间、配置的过期时间、响应状态码、是否为特定时间段等。 ngx_http_headers_filter 内部调用了 ngx_http_set_expires(r, conf) 根据配置中 expires 指令的值例如 expires 30d; 或 expires -1; 计算出正确的时间值并设置对应的头部。 这个逻辑如果不放在一个专门的过滤器中 就得由每个内容生产模块自己实现会造成大量重复而且很难保证行为一致。2-3 作为过滤器链的一环避免侵入各模块 Nginx 的架构中响应头由内容生成模块静态文件、代理、FastCGI 等准备好 然后经过一条过滤器链最后由底层过滤器序列化并发送。 ngx_http_headers_filter 被插入到这条链中。 这样的设计有几个好处 无侵入 静态文件模块、代理模块等不需要知道 add_header 的存在 它们照常生成 Content-Type、Content-Length 等头部。 这个过滤器会在后面自动附加管理员要求的头部。 后期修改能力 过滤器链的顺序是可以调整的。其他过滤器可以在它之前或之后执行 从而实现例如“先让 gzip 过滤器设置 Content-Encoding 再让 headers 过滤器添加自定义头”这样的精确控制。 状态码感知 该过滤器知道响应的最终状态码200、301、404 等 并据此决定是否添加头部。 例如add_header 指令默认只在成功响应200 等时生效 除非加了 always 参数。 这种逻辑只有在响应状态码已经确定之后才能正确执行 而这个时间点就是过滤器的执行时机。3. 总结 ngx_http_headers_filter 的设计目的可以概括为 提供一个可配置的、状态感知的、支持变量的 HTTP 响应头注入机制。 它存在的必要性体现在 运维灵活性 通过 add_header 和 expires 指令管理员无需编写模块就能控制响应头。 架构解耦 将头部添加逻辑集中到一个过滤器中内容模块无需感知避免代码重复和逻辑分散。 正确时机 在响应状态码确定后执行可以根据状态码决定是否添加头部也能正确解析请求相关的变量。 缓存控制统一 集中处理 Expires 和 Cache-Control确保缓存策略在整个服务器中行为一致。3 详解1 函数签名staticngx_int_tngx_http_headers_filter(ngx_http_request_t*r)1. 返回值类型ngx_int_t 用于表示函数执行结果的 状态码 返回一个整数状态码NGX_OK 表示成功NGX_ERROR 表示出错。 这个返回值不是 HTTP 状态码而是告诉调用者过滤器链调度者函数是否执行成功。2. 函数名ngx_http_headers_filter 函数名由下划线分隔的多个部分组成遵循 Nginx 的命名惯例 ngx 全局前缀表明这是 Nginx 源码的一部分用于防止命名冲突。 http 表明该函数属于 HTTP 子系统。 headers 指明该过滤器的操作对象是 HTTP 响应头。 具体来说它可以添加、修改或删除响应头。 filter 表明它是一个过滤器是 Nginx 过滤器链中的一环。 在 Nginx 架构中过滤器负责对输出数据进行分阶段的处理头部过滤器和体过滤器。 因此ngx_http_headers_filter 的完整含义是 HTTP 模块中用于处理添加/修改响应头的过滤器函数 该函数对应的模块是 ngx_http_headers_filter_module 它实现了 add_header、expires 等配置指令。 这个过滤器是这些指令得以生效的执行点。3. 参数列表(ngx_http_request_t *r) 它包含了请求的所有上下文信息2 逻辑流程1 子请求跳过 如果是子请求不处理直接放行。 2 配置检查 如果 expires 关闭且没有任何 add_header 或 add_trailer直接放行不做多余工作。 3 确定安全状态 根据响应状态码设置 safe_status。 成功和重定向类状态码被认为是“安全”的 默认允许添加自定义头错误状态码不允许除非用 always 覆盖。 4 处理 expires 如果配置了 expires 且状态安全 调用 ngx_http_set_expires 设置 Expires 和 Cache-Control 头。 5 处理 add_header 遍历所有自定义头部 跳过不符合安全条件且未设 always 的条目 对每个需添加的头部解析变量 然后调用其指定的处理函数写入 r-headers_out。 6 处理 add_trailer 7 传递请求 最后调用 ngx_http_next_header_filter(r) 将控制权交给下一个头部过滤器。 这个函数使得 Nginx 能够以一种统一、可配置的方式 在响应头发送前批量注入自定义头部满足各种运维和安全需求。{ngx_str_tvalue;ngx_uint_ti,safe_status;ngx_http_header_val_t*h;ngx_http_headers_conf_t*conf;局部变量声明1 子请求跳过if(r!r-main){returnngx_http_next_header_filter(r);}判断是否为主请求子请求直接传递给下一个过滤器confngx_http_get_module_loc_conf(r,ngx_http_headers_filter_module);获取模块配置2 配置检查if(conf-expiresNGX_HTTP_EXPIRES_OFFconf-headersNULLconf-trailersNULL){returnngx_http_next_header_filter(r);}检查是否有任何工作要做conf-expires NGX_HTTP_EXPIRES_OFFexpires指令被设置为 off即不设置过期头。conf-headers NULL没有通过add_header指令定义任何响应头。conf-trailers NULL没有通过add_trailer指令定义任何尾部头。如果三个条件全部满足说明当前 location 根本不需要添加或修改任何头部。此时直接调用ngx_http_next_header_filter(r)传递请求并返回。3 确定安全状态switch(r-headers_out.status){判断状态码的安全性根据状态码决定safe_status变量的值。某些状态码如成功和重定向被认为是“安全”的适合添加自定义头部而错误状态码如 4xx、5xx则默认不添加除非头部被标记了 always。caseNGX_HTTP_OK:caseNGX_HTTP_CREATED:caseNGX_HTTP_NO_CONTENT:caseNGX_HTTP_PARTIAL_CONTENT:caseNGX_HTTP_MOVED_PERMANENTLY:caseNGX_HTTP_MOVED_TEMPORARILY:caseNGX_HTTP_SEE_OTHER:caseNGX_HTTP_NOT_MODIFIED:caseNGX_HTTP_TEMPORARY_REDIRECT:caseNGX_HTTP_PERMANENT_REDIRECT:safe_status1;break;列出安全状态码这些状态码涵盖成功和重定向场景在这些情况下通常期望自定义头部能够被发送。设置安全标志为真如果状态码匹配上述任何一个将safe_status设为 1真default:safe_status0;break;}对于其他状态码主要是 4xx 客户端错误和 5xx 服务器错误将 safe_status 设为 0假。意义对于错误响应默认不应该添加add_header定义的头部除非头部指定了 always 参数。这符合用户的一般预期例如404 页面通常不需要额外的自定义头。4 处理 expiresif(conf-expires!NGX_HTTP_EXPIRES_OFFsafe_status){if(ngx_http_set_expires(r,conf)!NGX_OK){returnNGX_ERROR;}}处理 expires 指令5 处理 add_headerif(conf-headers){hconf-headers-elts;for(i0;iconf-headers-nelts;i){if(!safe_status!h[i].always){continue;}if(ngx_http_complex_value(r,h[i].value,value)!NGX_OK){returnNGX_ERROR;}if(h[i].handler(r,h[i],value)!NGX_OK){returnNGX_ERROR;}}}检查是否有自定义响应头6 处理 add_trailerif(conf-trailers){hconf-trailers-elts;for(i0;iconf-trailers-nelts;i){if(!safe_status!h[i].always){continue;}r-expect_trailers1;break;}}检查是否有自定义尾部头7 传递请求returnngx_http_next_header_filter(r);}将请求传递给下一个过滤器