Nginx认证失败时未返回自定义401错误页面问题排查
问题现象
启用HTTP Basic Auth后,无凭证或凭证错误的请求会返回Nginx默认401页面,但POST请求触发的405等其他4xx错误,能正常返回自定义的e40x.html页面。e40x.html路径已确认正确,Nginx作为Docker容器反向代理的功能正常,仅需解决错误页处理逻辑问题。
原因分析
Nginx处理Basic Auth触发的401时,默认会自动添加WWW-Authenticate: Basic realm="..."响应头,让浏览器弹出认证窗口。但直接使用error_page 401 /e40x.html的配置,会导致Nginx内部重定向到自定义页面时丢失这个关键响应头,同时因为自定义页面的location设置了internal,认证失败的请求无法正确触发自定义页面返回,最终 fallback 到Nginx默认错误页。
解决方案
调整error_page配置,为401错误单独指定处理规则,确保返回自定义页面的同时保留WWW-Authenticate头:
1. 修改error_page规则
在server块内,为401错误添加命名location的处理规则:
# 保留其他4xx错误的原有配置,单独处理401 error_page 400 402 403 404 405 /e40x.html; error_page 401 = @401_custom; error_page 500 501 502 503 504 /e50x.html;
2. 添加命名location处理401
在server块内新增@401_custom的location配置:
location @401_custom { root /etc/nginx/conf; rewrite ^ /e40x.html break; # 手动添加认证头,确保浏览器能弹出认证窗口 add_header WWW-Authenticate 'Basic realm="Restricted Content"' always; # 关闭此location的认证,避免循环触发 auth_basic off; }
3. 保留原有自定义错误页location
确保原有的/e40x.html和/e50x.html配置不变:
location = /e50x.html { root /etc/nginx/conf; allow all; internal; } location = /e40x.html { root /etc/nginx/conf; allow all; internal; }
修改后的完整server配置
server { listen 3000; server_name .domain.com; # Custom errors redirect error_page 400 402 403 404 405 /e40x.html; error_page 401 = @401_custom; error_page 500 501 502 503 504 /e50x.html; # Basic auth definition auth_basic "Restricted Content"; auth_basic_user_file /etc/nginx/conf/.htpasswd; # Disable server tokens server_tokens off; more_set_headers 'Server: My-Value'; location / { proxy_pass http://localhost:3001; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; limit_except GET { deny all; } } location /health { auth_basic off; proxy_pass http://localhost:3001/health; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; limit_except GET { deny all; } } # 401自定义错误处理 location @401_custom { root /etc/nginx/conf; rewrite ^ /e40x.html break; add_header WWW-Authenticate 'Basic realm="Restricted Content"' always; auth_basic off; } # Custom errors definitions location = /e50x.html { root /etc/nginx/conf; allow all; internal; } location = /e40x.html { root /etc/nginx/conf; allow all; internal; } # Security headers definition add_header Content-Security-Policy "connect-src 'self' *.domain.com; font-src 'self' *.googleapis.com *.gstatic.com; frame-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' *.googleapis.com 'unsafe-inline'; frame-ancestors 'self'; img-src 'self' data: ; manifest-src 'self'; media-src 'self'; object-src 'self'; worker-src 'self';" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"; add_header X-Content-Type-Options "nosniff"; add_header X-Frame-Options "DENY"; }
效果验证
修改配置后重启Nginx,用curl测试无凭证请求:
curl -i http://your-domain.com:3000
应返回自定义的e40x.html内容,同时响应头包含WWW-Authenticate: Basic realm="Restricted Content",浏览器访问也会弹出认证窗口并显示自定义错误页。
内容的提问来源于stack exchange,提问作者voidhopper

