Universal Links 与 App Links 错误码详解
SWCERR00101、SWCERR00301、legacy_failure、1026:每个 Universal Links 和 Android App Links 错误到底是什么意思,在哪里能看到,以及怎样修好才能让应用重新打开。也可以交给 Appy 的免费验证工具,几秒钟就帮你找出问题。
先确认它真的坏了
很多“Universal Links 不生效”的反馈,其实是链接完全在按 Apple 的设计工作。Apple 的技术说明 TN3155 列出了应用本就不该被打开的几种情况:在 Safari 地址栏里输入或粘贴的 URL 属于直接导航,会留在浏览器里;指向当前页面同一域名的链接,也会留在浏览器里;第三方浏览器,包括其他应用内置的浏览器,会自行决定要不要把链接交给你的应用。
所以,动任何文件之前,先老老实实测一遍。把链接粘贴到 iPhone 的备忘录里,然后长按。如果配置正确,菜单里除了用浏览器打开,还会提供用你的应用打开的选项。用户在这里选了什么,就会成为该域名的默认行为,这也是昨天还正常的链接今天却打开 Safari 的又一个隐蔽原因。
如果备忘录里没有应用选项,就是关联出了问题,错误码会告诉你问题在哪。
捷径:Appy 免费验证工具
打开终端之前,先把你的域名粘贴到 Appy 的 Universal Links 验证工具里。它会分别从你自己的服务器和 Apple 的 CDN 抓取 apple-app-site-association 文件,让你一眼看出 Apple 手里的文件和你的是不是同一份。它还会通过 Google 的 Digital Asset Links API 查询 Android 从你的 assetlinks.json 里读到了什么。免费、无需注册,结果就一页,直接发给开发同事即可。
如果问题出在文件本身,Appy 的 AASA 与 assetlinks.json 生成器会根据你的 Team ID、bundle ID 和 SHA-256 指纹生成这两个文件。本文其余部分逐一讲解每个错误,验证工具一旦标出问题,你就清楚该修哪里。
问问 Apple 的 CDN 手里有什么
从 iOS 14 起,手机不再从你的服务器获取 apple-app-site-association 文件,而是从 Apple 的 CDN 获取,再由 CDN 去你那里抓取。所以第一个问题不是“我的文件对不对”,而是“Apple 手里的是什么”。
Apple CDN 为你的域名保存了什么
curl -sD - "https://app-site-association.cdn-apple.com/a/v1/links.example.com" -o /dev/null出错的域名会这样响应
HTTP/1.1 404 Not Found
Apple-Failure-Reason: SWCERR00101 Bad HTTP Response: 404 Not Found
Cache-Control: max-age=3600,publicCDN 眼中的你的服务器
curl -v -A "AASA-Bot/1.0" https://links.example.com/.well-known/apple-app-site-associationSWCERR 错误码及修复方法
Apple 没有公布这些错误码的列表。下面是 Apple CDN 在 Apple-Failure-Reason 响应头里返回的消息,每一条都在 Apple Developer Forums 的讨论或我们自己对 CDN 的请求中得到过确认。
SWCERR00101 · Bad HTTP Response: 404 Not Found
CDN 请求你的文件,拿到的却不是 200。最常见的情况是文件放在根域名上,而 entitlement 里写的是子域名,或者反过来。applinks: 里的每个主机都需要在 https://that-host/.well-known/apple-app-site-association 放一份自己的文件。如果这里是 502,说明 Apple 来请求时你的源站宕机了。
SWCERR00201 · Insecure (non-https) redirects forbidden
文件的 URL 重定向到了一个 http:// 地址。请在每个域名上直接通过 HTTPS 提供该文件。Apple 文档要求完全不能重定向;在我们的测试中,CDN 会跟随 https 到 https 的重定向,但降级到 http 一定失败。
SWCERR00202 · Connections to private/reserved IP addresses are not allowed
主机名解析到了私有或保留地址,所以 Apple 不会去连接。只存在于 VPN 内部的 staging 域名经常遇到这种情况。这类域名请用开发者模式(下文会讲),生产环境则用公网主机。
SWCERR00301 · Timeout
Apple 的抓取程序等了半天,没收到任何响应头。常见原因是防火墙、地域屏蔽,以及会对陌生客户端发起验证的机器人防护。Apple 说这个文件必须能从任何 IP 访问,而且不公布自己的 IP 段,所以请对所有人放行这个路径。
SWCERR00302 · Network error (temporary)
临时故障,通常和 DNS 有关。如果你的 DNS 没问题,一般下一次抓取时就会消失;对失败结果来说,下一次抓取会在一小时内发生。
SWCERR00303 · TLS error
证书过期、签发给了别的主机、是自签名证书,或者握手失败。Apple 要求使用受公众信任、并且准确覆盖该主机名的证书。example.com 的证书并不覆盖 links.example.com,除非证书里明确写了它。
SWCERR00305 · Network error
CDN 连上了,但传输中断了:EOF、connection refused,或者服务器声称做了压缩却没有压缩时出现的 gzip: invalid header。请检查这个路径的 WAF 和压缩设置。
SWCERR00401 · Bad JSON content
CDN 拿到的不是 JSON,通常是 HTML 的人机验证页或登录页。“invalid character ‘<’”这条细节一看便知。论坛里一位 Apple 工程师给出的解决办法是:让服务器对任何 user agent(包括 AASA-Bot/*)都直接返回原始文件。
用 swcutil 检查路径
Apple 拿到文件之后,下一个问题是你的链接能否与它匹配。在 Mac 上,swcutil 可以拿本地文件副本对真实 URL 做测试,Apple 工程师推荐用这个方法,而不是开发者模式。
下载测试
sudo swcutil dl -d links.example.com匹配测试
sudo swcutil verify -d links.example.com -j ./apple-app-site-association -u https://links.example.com/promo匹配成功时的输出
{ s = applinks, a = ABCD123.com.example.app, d = links.example.com }:
Pattern "https://links.example.com/promo" matched.手机自己怎么看
打开开发者模式后,在“设置 › 开发者 › Universal Links”里有一个 Diagnostics 页面:输入完整 URL,它会告诉你是否有已安装的应用认领了这个链接。想看完整情况,就抓一份 sysdiagnose,打开 swcutil_show.txt,搜索你的 app ID,看其中三行。
Site/Fmwk Approval 应该显示 approved。如果是 unspecified 或 denied,说明手机从没为这个应用和域名拿到过经批准的文件:检查 entitlement、能否公开访问以及 Team ID 前缀。User Approval 显示用户是否为这个域名选择了浏览器。Last Checked 和 Next Check 显示 Apple 上次抓取你文件的时间。
最后这一点解释了大多数“我已经修好了,可什么都没变”的情况。安装之后,手机大约每周才重新检查一次文件。想马上测试修复效果,就重装应用。对于 staging 环境,在 applinks 条目里加上 ?mode=developer:这样会绕过 CDN,但只对使用开发证书签名的构建生效,而且只在你已开启该选项的设备上生效。
值得记住的几个数字
- 我们测试中 Apple CDN 保留正常文件的时长
- 6 小时
- 保留失败结果的时长
- 1 小时
- iPhone 安装后重新检查的频率
- 约 1 周
- Android 上检查验证结果前至少要等待的时间
- 20 秒
我们测试中 Apple CDN 保留正常文件的时长
保留失败结果的时长
iPhone 安装后重新检查的频率
Android 上检查验证结果前至少要等待的时间
Android App Links:pm get-app-links 报告的状态
Android 中间没有 CDN。设备上的验证程序会自己去抓取 https://your-host/.well-known/assetlinks.json;从 Android 12 起,未通过验证的链接不再弹出选择框,而是直接在浏览器中打开。所以缺少证书指纹时,表现恰恰就是“链接能用,只是不在我的应用里打开”。
Android 12 还改变了多个主机的判定方式。现在每个主机单独验证;在 Android 11 及更低版本上,只要有一个主机验证失败,所有主机都会失败。另外在 Android 15 上,对 assetlinks.json 的修改最多可能需要七天才能到达所有设备。
在你的电脑上重新运行验证
重置、验证、读取
adb shell pm set-app-links --package com.example.app 0 all
adb shell pm verify-app-links --re-verify com.example.app
adb shell pm get-app-links com.example.app典型输出
example.com: verified
sub.example.com: legacy_failure
example.org: 1026每种状态的含义
verified
域名已通过验证。如果链接仍然打开浏览器,就去看 intent filter:android:autoVerify="true"、BROWSABLE 类别,以及 http 和 https 两种 scheme。
none
还没有任何记录。等几分钟,再运行一次 verify-app-links。
legacy_failure
旧版验证程序拒绝了这个域名,却没说原因。先从文件本身查起:HTTP 200、application/json、HTTPS、没有重定向。
1024 及以上(例如 1026)
特定验证程序的错误。Android 文档指向网络和托管:同样是 200、JSON、HTTPS、不重定向这几条规则。
approved / denied
由 shell 命令强制设定,而不是验证得出的。在相信任何结果之前,先用 set-app-links 重置。
migrated / restored / system_configured
从旧版验证迁移而来、随用户数据恢复,或由设备配置设定。这些不是错误,但也不能证明你的文件今天能正常工作。
在你电脑上正确、到了 Play 商店却出错的指纹
Android 上最常见的故障和托管毫无关系。启用 Play App Signing 后,Google 会用自己的应用签名密钥重新签署你的应用。你用 keytool 从上传密钥得到的 SHA-256,并不是用户手机上的那个,所以本地构建能通过,正式版构建却失败。
请改为从 Play Console › Release › Setup › App signing 复制指纹。sha256_cert_fingerprints 数组可以放多个值,所以把调试密钥也留在里面,用大写。还要记住:从 example.com 重定向到 www.example.com,或从 http 重定向到 https,都意味着 Android 不会验证你的任何链接。
看看 Google 从你的文件里读到了什么
Digital Asset Links API
curl "https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://example.com&relation=delegate_permission/common.handle_all_urls"修文件的同时,推广照常进行
关联文件坏了,不该让你损失安装量。在任何套餐下,Appy 链接都会把每部手机送到对应的应用商店,包括华为应用市场;在 Pro 套餐下,它还会先尝试打开你的应用,已经装了应用的人会直接进入应用。不需要 AASA 文件,不需要 SDK,也不用发新版本。等 Universal Links 恢复正常,继续用同一条 Appy 链接就行,已经印出去的码和发出去的帖子都不用改。
appy.to/yourapp
排查期间照常工作
- iPhone你的应用(Pro)或 App Store
- Android你的应用(Pro)或 Google Play
- 华为华为应用市场
- 电脑你的网站
延伸阅读
错误码常见问题
在哪里能看到 SWCERR 错误码?
请求 https://app-site-association.cdn-apple.com/a/v1/your-domain 时的 Apple-Failure-Reason 响应头里、Mac 上 swcutil dl 的输出里,以及设备上的 swcd 日志里。
怎样让 Apple 拿到我修好的 AASA 文件?
Apple 的 CDN 缓存无法手动清除。缓存过期后它会重新抓取,在我们的测试中,失败结果的缓存时间是一小时。之后在测试手机上重装应用,让它去拿新副本,而不是等每周一次的检查。
有没有能一次检查所有这些的工具?
有。Appy 的 Universal Links 验证工具免费,而且无需注册。它会检查你服务器上和 Apple CDN 上的 AASA 文件,并通过 Google 的 Digital Asset Links API 检查你的 assetlinks.json,然后告诉你该修什么。
apple-app-site-association 文件有大小限制吗?
Apple 在 2016 年的文档中写过 128 KB 的上限。现在的文档没再提,但让文件保持小巧,就不用操心这个问题。
为什么我在 Safari 里输入链接时,打开的是浏览器?
这是预期行为。按设计,手动输入的 URL 和同一域名内的链接都会留在浏览器里。请改从备忘录或信息里测试。
Android 上的 legacy_failure 是什么意思?
旧版验证程序拒绝了这个域名,而 Android 不告诉你原因。检查 assetlinks.json 是否通过 HTTPS 以 application/json 返回 200 且没有重定向,并确认里面列出了 Play App Signing 的指纹。
能不能绕开这一切?
部分可以。在 Pro 套餐下,Appy 链接会先尝试你应用的 URL scheme,失败后再转到正确的应用商店,所以不需要在你自己的域名上放 AASA 文件也能工作。真正的 Universal Links 仍然需要上面的配置。
继续探索
Android App Links 验证失败:用 adb 五分钟定位问题
从 Android 12 起,域名验证未通过的 App Link 会在浏览器而非你的应用中打开——静默发生,而且并非每台设备都一样。这份 adb 排查清单能快速找到原因。
通用链接打开 Safari?修复 AASA、App Links、重定向与响应头
可执行排障流程,帮助你修复打开浏览器而非 App 的通用链接问题。
应用内浏览器如何处理深度链接:Instagram、TikTok、Facebook、Gmail、Telegram 等
同一条链接,在 Safari 里能打开应用,到了 Instagram 里就失灵。本文梳理每个应用用的是哪种浏览器、Apple、Google 和 Chrome 的规则,以及你能做些什么。
想看更多内容?浏览 博客全部主题.