universal links 设置的网站部分,就是你自己托管的两个JSON文件:iOS使用apple-app-site-association,Android使用assetlinks.json。当有人点击普通的https://链接时,它们能让应用代替网站打开该链接。两个文件都放在应用所处理的每个域名的/.well-known/下,都必须通过HTTPS提供且不能有重定向,并且各自指定了允许打开你网址的应用。应用也需要相应的设置(iOS上是授权,Android上是intent过滤器),下面会在每个文件旁边给出。
universal links与Android App Links的工作方式
两个系统都在网站和应用之间建立双向连接:应用声明它处理哪些域名,网站则确认这一点。Apple将universal links描述为标准的HTTP或HTTPS链接,因此同一个URL既可用于网站,也可用于应用;没有安装应用的用户则会在浏览器中打开网页(Apple)。按照Google的说法,Android App Links基于与其他深层链接相同的intent机制,但增加了一个验证步骤,需要你在网站上放置Digital Asset Links文件(Android开发者)。
| iOS universal links | Android App Links | |
|---|---|---|
| 网站上的文件 | apple-app-site-association |
assetlinks.json |
| 位置 | https://<domain>/.well-known/apple-app-site-association |
https://<domain>/.well-known/assetlinks.json |
| 应用中的声明 | Associated Domains授权,applinks:<domain> |
带android:autoVerify="true"的intent过滤器 |
| 识别应用的方式 | Team ID加Bundle ID | 包名加签名证书指纹 |
| 检查时机 | 安装时,通过Apple的CDN | 安装时;Android 15及更高版本还会在后台检查 |

“深层链接”是更宽泛的术语。普通的深层链接可能使用自定义scheme或未经验证的https://链接;universal links和App Links则是经过验证的那一类。
iOS文件:apple-app-site-association
创建一个名为apple-app-site-association的文件(没有扩展名),列出你的应用标识符,以及应用应当打开的URL模式。下面是Apple文档中示例结构的精简版:
{
"applinks": {
"details": [
{
"appIDs": ["ABCDE12345.com.example.app"],
"components": [
{ "/": "/buy/*", "comment": "Open URLs that start with /buy/ in the app." },
{ "/": "/help/website/*", "exclude": true, "comment": "Keep these in the browser." }
]
}
]
}
}
appIDs的值是应用标识符前缀(你的Team ID),加一个点,再加Bundle ID。components中的每一项匹配URL的一部分,"exclude": true表示匹配的URL不要在应用中打开(Apple,Supporting associated domains)。Apple也接受较旧的格式,使用appID和paths;其调试说明提醒不要混用两种格式(TN3155)。
将文件托管在https://<完整域名>/.well-known/apple-app-site-association,使用有效证书且不要重定向。Apple文档中的三条规则常会让人踩坑:
- 每个子域名都需要在应用中有自己的条目。位于
example.com的文件服务于applinks:example.com和applinks:*.example.com,而像www.example.com这样的具体子域名需要它自己的文件。 - 该文件必须可以从任何IP地址、以任何用户代理访问。防火墙、机器人过滤器或登录墙返回的403或404,会阻止Apple的服务器缓存它。
- 自iOS 14起,设备不再从你的服务器获取该文件。由Apple的CDN获取并缓存它,设备大约每周检查一次更新,而且无法直接使缓存失效;重新安装应用会下载更新的副本(TN3155)。
开发期间,你可以在授权条目中添加?mode=developer(applinks:example.com?mode=developer),这样设备会直接从你的服务器读取文件,而不是从Apple的CDN读取。
Android文件:assetlinks.json
assetlinks.json是一个语句数组,其中写明你的包名,以及签名应用的证书的SHA-256指纹。下面是Google针对一个应用的示例:
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example",
"sha256_cert_fingerprints":
["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
}
}]
请把包名和指纹替换成你自己的。要求来自Google的指南(2026年10月23日更新):
- 在
https://<domain>/.well-known/assetlinks.json提供,内容类型为application/json。 - 即使应用的intent过滤器也声明了
http,仍然使用HTTPS。 - 不能有重定向,无论是301还是302。
- 在应用处理的每个主机域名上都要发布。
- 一个文件可以列出多个应用,
sha256_cert_fingerprints字段可以接受多个指纹,例如调试版和正式版的指纹。
如果你使用Play应用签名,你在本地keytool得到的指纹通常与用户设备上的指纹不一致。Google建议从Play Console的“版本 > 设置 > 应用签名”中获取正确的JSON片段。
应用端是带autoVerify的intent过滤器。这是Android文档中的写法:
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="www.example.com" />
</intent-filter>
Android会验证此类过滤器中的每个主机名,而通配符主机(如*.example.com)需要根域名example.com上的文件(Android开发者)。在Android 15及更高版本上,验证还会定期运行,文件的改动最长可能需要七天才能生效;在Android 14及更低版本上,文件只在安装或更新时读取(Android开发者)。
服务器检查清单
两个文件通常因为同样几个原因而失败,因此可以对服务器做一次检查,同时覆盖两者:
| 检查项 | 原因 | 测试方法 |
|---|---|---|
| HTTPS | 两个平台都要求,Apple还要求有效证书 | 在浏览器中打开该URL |
| 没有301或302 | 两种文件都不支持重定向 | curl -sI应显示200 |
| iOS:对所有IP和用户代理公开 | Apple的CDN需要获取文件,防火墙或机器人过滤器会阻止它 | curl -A "MyAgent-Bot/1.0" <url>能返回文件 |
Android:Content-Type: application/json |
Google将其列为要求 | curl -sI查看响应头 |
| 你列出的每个域名和子域名都有相同的文件 | 每个主机都是单独验证的 | 逐个主机运行检查 |
关于内容类型,两个文件都以application/json提供,可以让设置保持一致。Apple的文档没有说明iOS文件的内容类型,因此这部分属于你的选择。下面是一个静态的nginx示例:
location = /.well-known/apple-app-site-association {
default_type application/json;
}
location = /.well-known/assetlinks.json {
default_type application/json;
}
如何检查它是否生效
先从外部检查文件,再在设备上检查实际行为。以下是Apple在TN3155中给出的调试步骤:
- 运行
curl -v https://<domain>/.well-known/apple-app-site-association,确认返回200和JSON内容,而不是301、302、403或404。 - 在Mac上,
sudo swcutil dl -d <domain>用于检查文件能否下载,sudo swcutil verify -d <domain> -j <path-to-JSON> -u <URL>用于检查某个URL是否匹配模式。 - 在iPhone上,把链接粘贴到备忘录应用中并长按。如果配置正确,“在应用中打开”和“在浏览器中打开”两个选项都会出现。
对于Android,Google在Android 12及更高版本上的手动验证方法如下(如果应用的目标版本低于Android 12,先运行adb shell am compat enable 175408749 com.example):
adb shell pm set-app-links --package com.example 0 all
adb shell pm verify-app-links --re-verify com.example
# 等待几分钟,然后:
adb shell pm get-app-links com.example
输出会列出每个域名及其状态,例如verified或legacy_failure。Google还提供了Statement List Generator和Tester(Digital Asset Links工具),用于测试已有的语句文件。如需更完整的测试流程,见移动网站测试清单。
哪些情况下链接不会打开应用
在iOS上,在浏览器地址栏中输入URL永远不会打开应用,因为那是浏览器内的直接导航;如果用户已经在你的域名上,同域名的链接也会留在Safari中(TN3155)。对于登录按钮等流程,Apple的解决办法是使用另一个子域名,并为它配置独立的applinks条目和AASA文件,这样系统就会把这次点击视为离开网站。
应用内浏览器需要单独测试。Apple表示,在浏览器应用或WKWebView中点击的链接可以打开你的应用,但同时也说明第三方浏览器自行决定是否启用universal links(TN3155中的“Universal links may not be enabled in every web browser”),而且如果某个应用通过自己的open(_:)调用打开你的链接,它不会反过来打开你的链接(Apple)。某个社交应用的浏览器是否会把链接交给你的应用,平台文档中没有说明,因此请在真机上测试你关心的每一个应用。
作为回退的智能应用横幅
智能应用横幅是网页上仅限iOS的提示:在你希望显示它的每个页面的<head>中添加一个meta标签。根据Apple的说明,如果已安装应用,它会打开应用;如果未安装,则链接到App Store。
<meta name="apple-itunes-app" content="app-id=myAppStoreID, app-argument=https://example.com/buy/42">
app-id是必填项,可选的app-argument会把一个URL传给应用,使其打开对应的界面。Apple列出了两个限制:横幅不会出现在框架(frame)内,也不会出现在iOS模拟器中。
如果你没有发布原生应用,就不需要这两个文件;渐进式网页应用是直接从浏览器安装的。
在手机尺寸下检查回退页面
所有没有安装应用的人,以及每个没有成功跳转到应用的链接,最终都会落到你的网页上,因此该页面必须在手机上正常工作。Mobile View Extension会以手机的用户代理、屏幕尺寸和像素比重新加载标签页,因此你可以在多种尺寸下看到回退页面的真实移动版本;也可以同时使用Chrome的设备模拟。但它无法测试应用跳转本身:它运行的是桌面版Chrome,没有触摸事件,也没有已安装的应用,因此链接处理需要用iPhone或Android手机,或iOS模拟器和Android模拟器。如果没有iOS硬件,见没有iPhone如何测试iPhone上的网站。
常见问题
universal links和深层链接有什么区别?
深层链接是宽泛的类别;universal links是Apple经过验证的那一类,基于标准的HTTP或HTTPS链接。深层链接也可以使用自定义URL scheme,它不与你控制的网站绑定。Android App Links是Android上经过验证的形式,在普通深层链接的基础上增加了Digital Asset Links检查。
apple-app-site-association应该放在哪里?
放在https://<domain>/.well-known/apple-app-site-association,不带文件扩展名,通过HTTPS提供,使用有效证书,并且不能有重定向。应用列出的每个子域名都需要自己的一份副本。
为什么我的universal link打开的是Safari而不是应用?
常见原因包括:重定向、文件返回403或404、缺少子域名条目,或点击的是同域名链接。Apple在TN3155中列出了这些情况;在地址栏中输入URL,或在Safari中浏览时点击同一域名的链接,都会让用户停留在浏览器中。先用curl -v检查文件。
两个文件都需要吗?
你只需要为你发布应用的每个平台提供对应的文件。同时拥有iOS应用和Android应用的网站两者都要提供,且两者彼此独立。