
Nginx的ngx_http_auth_request_module模块是一个内置但默认未启用的标准模块,它通过一种“子请求委托”的方式实现请求认证。当该模块启用后,Nginx可以在处理任何请求之前,先向一个内部指定的位置发起一个HTTP子请求,并将原始请求的许多上下文信息传递给那个子请求。认证服务只需要根据这些信息返回相应的HTTP状态码:200系列表示认证通过,401或403表示认证失败。如果认证失败,Nginx会将子请求的响应直接返回给客户端;如果认证通过,则继续执行后续的代理或静态文件服务。整个过程对后端应用完全透明,非常适合用来搭建统一的认证网关、API鉴权层或SSO集成。
auth_request核心指令与工作流拆解
auth_request模块主要包含auth_request和auth_request_set两个指令。auth_request用于在http、server或location块中指定一个URI,该URI将被Nginx用来发起内部子请求以进行认证检查。指定的URI应当是一个内部location,通常使用internal指令保护,使其不会被外部直接访问。当子请求发出后,Nginx会等待其响应,并根据返回的状态码决定下一步动作:状态码200到299被视为认证成功,301、302等重定向状态码会触发客户端重定向;401或403则认证失败,Nginx会直接终止当前请求并将子请求的响应体发送给客户端。
一个典型的配置片段如下:
server {
listen 80;
server_name ippipp.com;
# 开启认证检查
auth_request /auth;
location / {
proxy_pass http://backend_app;
}
# 认证子请求对应的内部location
location = /auth {
internal;
proxy_pass http://127.0.0.1:8080/verify; # 外部认证服务
proxy_pass_request_body off; # 不传递原始请求体
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
这里auth_request /auth;会使得所有针对location /的请求先由/auth这个内部location处理。在/auth中,Nginx通过反向代理将请求发送给跑在127.0.0.1:8080的认证服务,该服务需要提供一个/verify接口。由于我们只关心认证结果,一般设置proxy_pass_request_body off;来丢弃请求体以节省带宽。通过proxy_set_header我们将原始请求的URI、客户端IP等信息传递给认证服务,以便它做出判断。
当认证服务返回200时,Nginx认为认证通过,继续执行proxy_pass http://backend_app;;若认证服务返回401,则客户端会收到401响应以及子请求的响应体内容(例如一段JSON错误信息)。这种机制允许认证服务返回自定义的错误页面,而无需在Nginx层面再做额外配置。
传递与获取认证子请求中的变量
仅仅判断通过或失败往往不够,实际场景中经常需要将认证服务生成的用户信息、角色、token等带回给后续的后端服务。这时就需要auth_request_set指令。该指令可以在认证子请求完成后,将其响应头中的值取出并赋给一个变量,供后续处理使用。
# 在auth_request指令后使用
auth_request_set $auth_user $upstream_http_x_user;
auth_request_set $auth_roles $upstream_http_x_roles;
location / {
auth_request /auth;
proxy_set_header X-User $auth_user;
proxy_set_header X-Roles $auth_roles;
proxy_pass http://backend_app;
}
这里假设认证服务在返回200时,还会在响应头中添加X-User和X-Roles等自定义头。Nginx通过$upstream_http_<header_name>的变量形式获取这些响应头(注意header名称中的短横线需要转成下划线)。通过auth_request_set将其赋值给自定义变量$auth_user和$auth_roles后,就可以在代理到后端时作为新请求头发送出去。这样后端应用无需再次解析token或查询用户中心,直接信任Nginx传递的头即可。
需要注意的是,auth_request_set指令必须放在auth_request指令之后,否则变量还不可用。此外,如果认证子请求返回4xx或5xx状态码,Nginx不会执行后续的auth_request_set,并且请求会被立即中断。因此,如果需要在认证失败时仍然传递一些信息,可以考虑设置auth_request指令的同时配合proxy_intercept_errors等指令,但一般场景下不会这样使用。
一种进阶用法是结合Nginx的map指令或if判断,基于认证结果执行不同的路由逻辑,比如根据角色将请求转发到不同后端。
处理认证子请求的请求体与性能优化
对于一般的GET请求或只需验证Cookie、header的认证场景,认证子请求无需原始请求体。可以通过proxy_pass_request_body off;来阻止Nginx将客户端POST的大体积请求体传递给认证服务,这能显著降低内存占用和延迟。但是,在某些特殊场景下,认证服务可能需要读取请求体来做HMAC验证或内容校验,此时就必须保留请求体传递。然而,auth_request模块本身并不支持直接读取并传递请求体,因为子请求会尝试从客户端接收请求体,但这可能导致原请求体被消耗,后续后端无法获取。官方文档明确指出:“If the request body is large, the subrequest may be handled before the original request body is fully received: to avoid such situation, you should use the proxy_request_buffering off directive or fastcgi_request_buffering off directive.”实际上,对于需要请求体的认证需求,更推荐的做法是在应用层做权限校验,或者使用深度集成的nginx模块如lua-nginx-module来实现。
另一个重要的优化点是认证子请求的缓存。如果每次请求都去调用外部认证服务,高并发下会给认证服务带来巨大压力,并且增加延迟。可以利用Nginx的proxy_cache或fastcgi_cache结合auth_request来实现认证结果缓存。具体做法是,在内部location中开启缓存,以原始请求中的Cookie、Authorization头等作为缓存键的一部分。这样,同一用户的短时间内多次请求只需要第一次进行真实认证,后续直接从缓存中获取结果,既提升了性能也解放了认证服务。不过要注意,缓存的过期策略需要根据token的生命周期设置,避免出现权限提升漏洞。如果token有效期较短,可以适当配置较短的缓存时间。
此外,Nginx Plus提供了更高级的动态认证方案,例如可以基于JWT声明的内容直接进行认证和路由,但开源Nginx配合auth_request已经可以解决大多数外部委托认证的需求。配合error_page指令,还能把认证失败转到统一的登陆页面,实现SSO跳转逻辑。
通过合理设计认证子请求的接口契约和Nginx变量传递,我们可以构建出一层轻量、高性能、语言无关的认证网关,为整个微服务或单体应用提供统一的安全屏障。
nginxauth_request_module第三方认证修改时间:2026-08-12 20:36:48