401 Unauthorized表示请求需身份验证但未提供或凭证无效;移动端需主动在首次请求中携带Authorization头,Nginx须返回标准WWW-Authenticate头且realm简洁合规,不可依赖浏览器式自动弹窗与重试。

移动端 App 调用 Nginx 后端接口时,若启用了 HTTP Basic Auth,常出现“401 Unauthorized”但无弹窗、或认证凭据未被正确发送等问题。核心原因不是 Nginx 配置错误,而是移动端网络栈(如 OkHttp、NSURLSession、WebView)对 Basic Auth 的默认行为与浏览器不同:不自动弹出登录框、不缓存凭据、不自动重试 401 请求、甚至忽略 WWW-Authenticate 响应头。
确认 Nginx 的 Basic Auth 配置是否符合移动端要求
Nginx 必须返回标准的 WWW-Authenticate 响应头,且 Realm 值需简洁、无空格和特殊字符(如中文),否则某些 Android WebView 或旧版 OkHttp 会解析失败:
- ✅ 正确写法:
auth_basic "api-realm";(英文短字符串,带引号) - ❌ 问题写法:
auth_basic "API 访问(请勿泄露)";(含空格、括号、中文) - 确保
auth_basic_user_file路径可读,且密码文件使用htpasswd -B(bcrypt)或-s(SHA)生成,避免过时的 crypt 格式在部分客户端校验失败
检查移动端是否主动构造并携带 Authorization 头
绝大多数原生 App 不依赖服务端触发认证流程,而是由客户端代码显式添加 Base64 编码的凭据头:
- Android(OkHttp)示例:
request.newBuilder().header("Authorization", "Basic " + Base64.encodeToString("user:pass".getBytes(), Base64.NO_WRAP)) - iOS(URLSession)需在
URLSessionDelegate的urlSession(_:task:didReceive:completionHandler:)中手动处理 401 并重发请求(不能仅靠系统自动响应) - 关键点:不要等待 Nginx 返回 401 后再补头——应从首次请求就带上
Authorization,否则部分网络库(如 Retrofit 默认配置)不会自动重试
抓包验证请求/响应头是否完整合规
用 Charles 或 mitmproxy 抓取真实 App 流量,重点比对以下内容:
- 请求中是否有
Authorization: Basic xxx?值是否 Base64 编码正确(解码后为user:pass格式,无多余空格) - 响应中是否有
WWW-Authenticate: Basic realm="api-realm"?注意大小写、空格、引号必须严格匹配 RFC 7235 - 是否存在跨域(CORS)干扰?若 App 通过 WebView 发起请求,而 Nginx 未配置
add_header Access-Control-Allow-Headers "Authorization";和add_header Access-Control-Allow-Credentials "true";,预检请求(OPTIONS)会失败,导致凭据不被发送
临时启用日志定位具体拒绝原因
在 Nginx 配置中开启详细错误日志,快速区分是认证失败还是其他拦截:
- 在 server 或 location 块中添加:
error_log /var/log/nginx/basic-auth-debug.log debug; - 重启后触发请求,查看日志中是否出现类似
user "xxx": password mismatch或no user/password was provided - 若日志显示
no user/password was provided,说明客户端根本没发 Authorization 头;若显示密码错误,则需检查 App 端编码逻辑或密码文件是否更新
不复杂但容易忽略:移动端适配 Basic Auth 的本质,是把“浏览器自动协商”变成“客户端主动声明”。只要确保头字段合规、凭据前置发送、抓包可验证,问题基本就定位清楚了。


















