Deep Link & Universal Link Tester
Compose and validate custom URL schemes, check a live domain's apple-app-site-association or assetlinks.json, and work out exactly which path pattern a URL matches — and why it doesn't.
Resulting URL
myapp://profile/settings?tab=account
Scheme is valid.
import { Linking } from 'react-native'
await Linking.openURL('myapp://profile/settings?tab=account')
// Handle it on the receiving side:
Linking.addEventListener('url', ({ url }) => {
const { hostname, pathname, searchParams } = new URL(url)
// route on hostname + pathname
})<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.example.app</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array><activity android:name=".MainActivity" android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="profile" />
</intent-filter>
</activity>Why universal links break
The file redirects. iOS does not follow redirects when fetching apple-app-site-association. If your host 301s /.well-known/… to a CDN or adds a trailing slash, the link silently falls back to Safari. This tool fetches without following redirects, so you see what iOS sees.
An SPA serves index.html. A catch-all route returns your app shell with Content-Type: text/html and HTTP 200. It looks fine in a browser and fails every time on device.
Stale paths alongside components. iOS 13+ reads components and ignores paths. Leaving both in place makes older and newer devices behave differently.
An exclude rule sits too high. Rules are evaluated top-down and the first match wins, so an exclude entry above a permissive one quietly kills it. The path matcher reports which rule decided the outcome.