Firebase Dynamic Links Migration Guide
Migrate Firebase Dynamic Links to WarpLink with a tested link inventory, domain cutover, SDK steps for four stacks, and checks for old links and analytics.
To migrate from Firebase Dynamic Links, recreate your links on a domain you control, swap the Firebase Dynamic Links SDK for one that handles taps and deferred installs, and test every path on real devices. Links on Google's page.link domains cannot be saved, so replace them wherever they appear. Links on your own custom domain can keep working.
This guide walks through the full cutover to WarpLink: inventory, field mapping, SDK code for iOS, Android, React Native, and Flutter, old-URL handling, and a test matrix. Every code example shows the Firebase pattern you are removing next to the WarpLink code replacing it.
What Still Works After the Firebase Dynamic Links Shutdown?
Google says all Firebase Dynamic Links now return HTTP 404. On October 8, 2026 we requested 32 real page.link short links from 25 domains found in the September 2026 Common Crawl, the way an iPhone does. 25 still redirected, 15 to the app's own website or store and 10 to a Firebase preview page. The other 7 returned an error or no redirect. A redirect only sends the tap to a web destination. Link creation, the console, and SDK updates are gone, and Google can end the redirects at any time. We do not know when, so migrate before they stop. To see which of your own links still redirect, run them through our free Firebase Dynamic Links checker.
| Date | What happened | Source |
|---|---|---|
| August 25, 2025 | Firebase Dynamic Links shut down. Google says links on page.link subdomains and custom domains started returning HTTP 404, and creating new links stopped. | Deprecation FAQ |
| From August 25, 2025 | The Short Links API and Link Stats API started returning HTTP 400 or 403, gradually at first and then for every request. | Deprecation FAQ |
| After August 25, 2025 | Google deleted the page.link URL prefix domains. They cannot be kept or transferred, so links on them cannot be redirected. Links on your own custom domain can be. | Deprecation FAQ |
The FDL SDK depends on a decommissioned service. handleUniversalLink() and its Android, React Native, and Flutter equivalents cannot be relied on to resolve any link, and the SDK gets no fixes for new Xcode, Android, or React Native releases.
Google's own migration guide moves you to plain App Links and Universal Links. By Google's own comparison, that path restores deep links into an installed app but not store routing, deferred deep links, click analytics, or short links. This guide restores all of them, and adds install attribution. If you are still choosing a replacement, compare Firebase Dynamic Links alternatives first.
Which Old Links Can You Keep?
Decide this before you touch any code. Whether an old URL can keep working depends only on who controls its domain and what its path looks like.
| Where the old link lives | Example | Can the old URL keep working? | What to do |
|---|---|---|---|
A Google-hosted page.link subdomain | yourapp.page.link/aBc1 | No. Google deleted these domains, and you never controlled their DNS. | Recreate the link on your app's subdomain or a custom domain, then replace every copy you can still edit. |
| Your own subdomain, lowercase one-segment paths | links.yourapp.com/spring-sale | Yes, served directly. | Point the subdomain at WarpLink and recreate each link with its old path as the slug. |
| Your own subdomain, mixed-case suffixes or a path prefix | links.yourapp.com/aBc1, links.yourapp.com/go/sale | Yes, through a redirect. | Keep the domain on a host you run and answer each old path with a 301 to its new WarpLink URL. |
| Your root domain | yourapp.com/aBc1 | Yes, through a redirect. | Add 301 rules for the old paths on your web host. Put new links on a subdomain. |
Three product facts drive the table:
- Slugs are one lowercase segment. A WarpLink slug is 2 to 100 characters of lowercase letters, digits, and non-consecutive hyphens, with no slashes. Firebase's generated suffixes are mixed case, such as
aBc1, so they cannot be reused as slugs. - Custom domains are subdomains. Setup uses a
CNAMErecord, and DNS does not allow aCNAMEat the root of a domain, so WarpLink acceptslinks.yourapp.combut notyourapp.com. - DNS records do not redirect. A DNS record points a hostname at a server. It cannot map an old path to a new URL. A redirect has to come from an HTTP server, CDN rule, or edge function that answers on the old domain.
Every organization can run one custom domain free for 3 months. After that, custom domains are on every paid plan, from 3 domains on Starter.
Step 1: Inventory Links and Domains
Firebase Dynamic Links has no working export path. The REST APIs return errors and the console section is gone, so rebuild the list from what you still control:
- App code. Deep link URLs hardcoded in source, push notification payloads, and App Store or Play Store listings.
- Marketing materials. Emails, ads, social bios, partner pages, and printed QR codes.
- Analytics referrers. Any tool that logged traffic from your
page.linkor custom domain shows which links and campaigns carried traffic. - A Google Takeout export. If your team exported Dynamic Link metadata before the shutdown, that CSV holds the short link, destination, platform identifiers, fallbacks, social text, and UTM values for each link.
- The links themselves. For a link that still redirects, our Firebase Dynamic Links checker shows where it sends visitors, which recovers the destination you need to recreate it.
Record each link in a spreadsheet with these columns: old URL, decision-table row, destination URL, iOS and Android behavior, social preview text, UTM values, where it is published, and recent traffic. The last two columns tell you which links to recreate first and which copies you can still edit.
Step 2: Register Your App and Map Firebase Fields
Create your account and register the app
- Create your WarpLink account with email, GitHub, or Google, and create an organization.
- Open Apps and register your app. An organization registers an app before it creates links, and one app covers iOS and Android together, including a React Native or Flutter app.
- Open Keys and create an SDK key for the app. Mobile apps authenticate with an SDK key. An API key looks identical but cannot record installs, so deep links would work while attribution silently fails.
WarpLink generates and hosts your apple-app-site-association and assetlinks.json files from the app registration and serves them on your link domain. Leave the path patterns empty to claim every path, or list specific paths. Any later change to the app regenerates both files.
Map app-level fields
Firebase set platform details per link. WarpLink sets them once per app.
| Firebase source | WarpLink app field |
|---|---|
iOS bundle ID (ibi) | iOS bundle identifier |
| Apple Team ID (Firebase project settings) | iOS team ID |
iOS App Store ID (isi) | iOS App Store URL, written as https://apps.apple.com/app/id followed by the ID |
Android package name (apn) | Android package name, and the Android Play Store URL, written as https://play.google.com/store/apps/details?id= followed by the package name |
| SHA-256 certificate fingerprints (Firebase project settings) | Android SHA-256 fingerprints |
The store URLs are the default fallbacks for every link. A per-link fallback overrides them.
Map link parameters
| Firebase parameter | WarpLink link field |
|---|---|
link (deep link) | destination_url. The SDK hands it to your app as deepLink.destination. |
ifl (iOS fallback link) | ios_fallback_url |
afl (Android fallback link) | android_fallback_url |
ofl (desktop fallback link) | No separate field. Desktop browsers open destination_url. |
st / sd / si (social title, description, image) | og_title / og_description / og_image_url |
utm_source / utm_medium / utm_campaign | utm_source / utm_medium / utm_campaign |
efr (forced redirect, skip the preview page) | Not needed. WarpLink redirects real users with a 302 and serves preview tags only to bots. |
ios_url and android_url have no Firebase counterpart. They hold an optional per-platform deep link URL, such as yourapp://product/123, which the SDK returns as deepLink.deepLinkUrl.
If your app routed on the URL in Firebase's link parameter, set destination_url to the same URL and route on deepLink.destination. Your router keeps working unchanged. The full parameter list is in the Firebase migration reference.
Step 3: Move iOS, Android, React Native, and Flutter Handling
WarpLink's SDKs are at version 1.1.1 on iOS, Android, React Native, and Flutter. Each one takes a single onLink callback in configure(). Cold start, warm start, and deferred installs all arrive there, and deepLink.isDeferred tells a post-install match apart from a direct tap. There is no separate deferred call to port.
Declare your app's subdomain in linkDomains. The SDK decides whether an incoming URL is a WarpLink link the moment it arrives, so a host it has not been told about is handed back to your own router on a fresh install. Find the subdomain in the dashboard under the app's Settings, or see Moving to your app's subdomain.
iOS (Swift)
In Xcode, go to File > Add Package Dependencies, add https://github.com/WarpLinkApp/warplink-ios-sdk, select version 1.1.1 or later, and add the WarpLink library to your app target.
// Before (Firebase)
import FirebaseCore
import FirebaseDynamicLinks
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
return true
}
// Taps on a Dynamic Link
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
guard let url = userActivity.webpageURL else { return false }
return DynamicLinks.dynamicLinks().handleUniversalLink(url) { dynamicLink, _ in
if let link = dynamicLink?.url { route(to: link) }
}
}
// Deferred link on first open
func application(_ app: UIApplication, open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
guard let link = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url)?.url else { return false }
route(to: link)
return true
}
}
// After (WarpLink)
import WarpLink
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
WarpLink.configure(
apiKey: "wl_live_yoursdkkeyhere000000000000000000",
options: WarpLinkOptions(
linkDomains: ["yourapp.aplnk.to"],
onLink: { result in
// Cold start, warm start, and deferred installs all land here.
guard case .success(let deepLink) = result, let deepLink,
let link = URL(string: deepLink.deepLinkUrl ?? deepLink.destination) else { return }
route(to: link)
}
)
)
return true
}
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
WarpLink.continue(userActivity)
}
}
If your app uses a scene delegate, iOS calls the scene instead. Forward the activity there with WarpLink.continue(userActivity) in scene(_:continue:), or set your scene delegate class to WarpLinkSceneDelegate. The iOS SDK guide covers SwiftUI and the cold-start scene methods.
Then update Signing & Capabilities > Associated Domains:
- applinks:yourapp.page.link
+ applinks:yourapp.aplnk.to
If you serve links from a custom domain, add an applinks: entry for it too, and list it in linkDomains.
Android (Kotlin)
Swap the dependency in your module-level build.gradle.kts:
// Before (Firebase)
implementation("com.google.firebase:firebase-dynamic-links-ktx:21.2.0")
// After (WarpLink)
implementation("app.warplink:sdk:1.1.1")
// Before (Firebase)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Firebase.dynamicLinks.getDynamicLink(intent)
.addOnSuccessListener { data -> data?.link?.let { route(it) } }
.addOnFailureListener { e -> Log.e("DynamicLinks", "getDynamicLink failed", e) }
}
}
// After (WarpLink): your Application class
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
WarpLink.configure(
context = this,
apiKey = "wl_live_yoursdkkeyhere000000000000000000",
options = WarpLinkOptions(
linkDomains = listOf("yourapp.aplnk.to"),
onLink = { result ->
// Cold start, warm start, and deferred installs all land here.
result.onSuccess { deepLink ->
route(Uri.parse(deepLink.deepLinkUrl ?: deepLink.destination))
}
}
)
)
}
}
// After (WarpLink): the Activity that receives links, with launchMode="singleTask"
class MainActivity : AppCompatActivity() {
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
WarpLink.onNewIntent(intent)
}
}
Cold start needs no Activity code. onNewIntent forwards warm-start links, and android:launchMode="singleTask" makes Android reuse the running Activity instead of starting a second one. Then point the intent filter at your new host:
<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"/>
<!-- Before: android:host="yourapp.page.link" -->
<data android:scheme="https" android:host="yourapp.aplnk.to"/>
</intent-filter>
React Native
npm uninstall @react-native-firebase/dynamic-links
npm install @warplink/react-native
The iOS SDK ships through Swift Package Manager and needs dynamic linkage. Add use_frameworks! :linkage => :dynamic inside your app target in ios/Podfile, then run cd ios && pod install.
// Before (Firebase)
import dynamicLinks from '@react-native-firebase/dynamic-links';
const unsubscribe = dynamicLinks().onLink((link) => route(link.url));
const initialLink = await dynamicLinks().getInitialLink();
if (initialLink) route(initialLink.url);
// After (WarpLink): call once at startup, outside any component
import { WarpLink } from '@warplink/react-native';
WarpLink.configure({
apiKey: 'wl_live_yoursdkkeyhere000000000000000000',
linkDomains: ['yourapp.aplnk.to'],
onLink: ({ deepLink, error }) => {
if (error || !deepLink) return;
// Cold start, warm start, and deferred installs all land here.
route(deepLink.deepLinkUrl ?? deepLink.destination);
},
});
Android needs no host code beyond android:launchMode="singleTask". On iOS, replace the Firebase line in your existing application(_:continue:restorationHandler:) with WarpLinkModule.handleIncomingURL(url) and keep returning what your linking library returns, so React Navigation and OAuth callbacks keep working. The React Native SDK guide shows the full hook, the Objective-C form, and Expo prebuild.
Flutter
flutter pub remove firebase_dynamic_links
flutter pub add warplink_flutter
The plugin wraps the native iOS and Android SDKs, so there is no delegate or MainActivity code to write. Set minSdk = 26 in android/app/build.gradle.kts. The iOS SDK ships through Swift Package Manager, so there is no Podfile change. On Flutter 3.38 to 3.43, run flutter config --enable-swift-package-manager once.
Flutter has its own deep link handler, enabled by default. Switch it off so your router does not also receive WarpLink URLs as routes. In ios/Runner/Info.plist:
<key>FlutterDeepLinkingEnabled</key>
<false/>
In android/app/src/main/AndroidManifest.xml, inside <activity>:
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
Call configure from the initState of your root widget and give MaterialApp a navigatorKey. A cold-start link can arrive before the first frame, when no navigator exists yet, so navigateTo waits for it.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:warplink_flutter/warplink_flutter.dart';
final navigatorKey = GlobalKey<NavigatorState>();
void navigateTo(String url) {
final navigator = navigatorKey.currentState;
if (navigator != null) {
navigator.pushNamed('/product', arguments: url);
return;
}
// The first frame has not built the navigator yet.
WidgetsBinding.instance.addPostFrameCallback((_) {
navigatorKey.currentState?.pushNamed('/product', arguments: url);
});
}
void main() => runApp(const App());
class App extends StatefulWidget {
const App({super.key});
@override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
@override
void initState() {
super.initState();
// Before (Firebase): getInitialLink() plus an onLink.listen(...) subscription.
// After (WarpLink): one callback for cold start, warm start, and deferred links.
unawaited(
WarpLink.configure(
apiKey: 'wl_live_yoursdkkeyhere000000000000000000',
linkDomains: const ['yourapp.aplnk.to'],
onLink: (event) {
switch (event) {
case LinkEvent(:final deepLink):
navigateTo(deepLink.deepLinkUrl ?? deepLink.destination);
case ErrorEvent(:final error):
debugPrint('WarpLink ${error.wireCode}: ${error.message}');
}
},
),
);
}
@override
Widget build(BuildContext context) => MaterialApp(
navigatorKey: navigatorKey,
routes: <String, WidgetBuilder>{
'/': (context) => const Placeholder(), // your home screen
'/product': (context) => const Placeholder(), // your product screen
},
);
}
Update the Associated Domains entitlement and the Android intent filter host exactly as in the iOS and Android sections above. The Flutter SDK guide covers both.
Remove the Firebase Dynamic Links SDK
Migration is a replacement, not a coexistence. Remove the SDK and every FDL code path as you wire WarpLink in:
- iOS: remove the
FirebaseDynamicLinkspackage or pod, thehandleUniversalLinkanddynamicLink(fromCustomSchemeURL:)calls, the oldapplinks:entry, and theFirebaseDeepLinkPasteboardRetrievalEnabledInfo.plist key if you set it. - Android: remove
firebase-dynamic-links-ktx, thegetDynamicLink(intent)calls, and the intent filter for the old host. - React Native: remove
@react-native-firebase/dynamic-linksand itsonLink()andgetInitialLink()calls. - Flutter: remove
firebase_dynamic_linksand itsgetInitialLink()andonLinkcalls.
Keep FirebaseApp.configure() or Firebase.initializeApp() only if the app uses other Firebase services.
Step 4: Move Existing URLs
Recreate links with the REST API
Create links in the dashboard, or script them from your inventory with the REST API. Use an API key here. The SDK key belongs in the app.
curl -X POST https://api.warplink.app/v1/links \
-H "Authorization: Bearer wl_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"destination_url": "https://yourapp.com/product/123",
"app_id": "YOUR_APP_ID",
"slug": "spring-sale",
"ios_url": "yourapp://product/123",
"android_url": "yourapp://product/123",
"og_title": "Check out this product",
"og_description": "Our most popular product, now in three colors",
"og_image_url": "https://yourapp.com/product.jpg",
"utm_source": "newsletter",
"utm_medium": "email",
"utm_campaign": "spring_sale"
}'
The response holds the short link, on your app's subdomain by default, such as https://yourapp.aplnk.to/spring-sale. Add "domain": "links.yourapp.com" once that custom domain is live. Social crawlers get a full HTML page with Open Graph and Twitter Card tags, and real users get the redirect. The links API reference lists every field.
Serve old URLs from a custom domain you own
For row 2 of the decision table, the old URLs keep working with no redirect hop:
- Recreate each link with the same slug, for example
spring-saleforlinks.yourapp.com/spring-sale. - Add the subdomain under Settings > Domains and add the two
CNAMErecords the dashboard shows: one routes traffic, one lets WarpLink issue and renew the HTTPS certificate. - Remove the Dynamic Links rewrite from
firebase.jsonand switch DNS. Most changes apply in 5 to 15 minutes. - Add the domain to Associated Domains, the intent filter, and
linkDomains, so a tap opens the app instead of the browser. See custom domains for the full setup.
Redirect old URLs with mixed-case or nested paths
For rows 3 and 4, keep the old domain on a host you run and answer each old path with an HTTP 301 to its new WarpLink URL. Any HTTP server, CDN redirect rule, or edge function works. Build the rule list straight from your inventory spreadsheet, one old path to one new URL.
Universal Links and App Links do not always open the app after a redirect from another domain, so a tap on an old URL can reach the WarpLink link in the browser. WarpLink then sends a phone to the app's store page, which shows Open when the app is installed, and a desktop browser to the destination_url. A user who installs from that store page still gets the deferred deep link. Test old URLs on a device with the app installed.
Replace page.link URLs you can edit
For row 1, there is no redirect to set up. Replace the old URL wherever you can still edit it: email templates, CMS content, store listings, website buttons, social bios, and push templates. Reprint QR codes that carry a page.link URL. Ship an app update for any page.link URL hardcoded in the app.
Step 5: Verify Installed and First-Install Paths
Test on real devices. Simulators and emulators do not fully support Universal Links and App Links. Run every case on each platform you ship:
| Case | How to test | Expected result |
|---|---|---|
| Installed, cold start | Force-quit the app, then tap a link in Notes or Messages | The app opens on the linked screen. onLink fires with isDeferred false. |
| Installed, warm start | Background the app, then tap a link | The same screen opens in the running app. |
| Not installed | Delete the app, then tap a link | The per-link fallback opens, or the store URL from the app registration. |
| First install | Tap a link, install the app, and open it on the same device and network | On a match, onLink fires with isDeferred true and the original destination. With no match, onLink does not fire and the app opens on its normal start screen. |
| Desktop | Open the link in a laptop browser | The destination_url loads. |
| Social preview | Paste the link into Slack or iMessage | The title, description, and image from the og_ fields appear. |
| Attribution | Check the link's analytics after the first-install test | The install appears on the link. |
| Analytics | Tap the link a few times from different apps | Clicks appear on the link in real time. |
| Old URL | Open an old custom-domain URL | It lands on the same screen as the new link. |
iOS checks
- Open
https://yourapp.aplnk.to/.well-known/apple-app-site-associationand confirm your team ID, bundle ID, and paths. - Paste a link into Notes and long-press it. The menu should offer to open it in your app.
- If links open Safari, check Settings > Developer > Associated Domains Diagnostics on the device.
- To repeat the first-install test, delete the app. The completion marker goes with it, so the next install runs the check again, and it is reported as a reinstall that still counts as an install. To exercise a true first install, use a device that has never run your app.
Android checks
- Open
https://yourapp.aplnk.to/.well-known/assetlinks.jsonand confirm your package name and SHA-256 fingerprint. - Simulate a tap:
adb shell am start -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d "https://yourapp.aplnk.to/your-slug"
- If Android shows an "Open with" chooser, App Links verification failed. Check that the fingerprint matches the key that signed the installed build, including Play App Signing.
React Native and Flutter checks
Run the iOS and Android checks for each platform, then:
- Confirm no error reaches
onLinkon launch. A rejected SDK key or a configuration problem arrives there as an error, andconfigure()still completes. - Test a cold-start tap with the app force-quit. The link must reach
onLinkand navigate before your navigator is ready. - On Flutter, if a tap opens a route you did not define, check that Flutter's own deep linking flag is off in both
Info.plistandAndroidManifest.xml.
Common test issues
| Symptom | Likely cause | Fix |
|---|---|---|
| Link opens in the browser instead of the app | Association file not verified, or the domain is missing from the entitlement or intent filter | Check both well-known files and the applinks: entry or intent filter host |
| A fresh install hands the link to your own router | The link domain is not declared to the SDK | Add the host to linkDomains |
| First-install match never arrives | An API key in configure() instead of an SDK key | Create an SDK key under Keys and use it in the app |
| First-install match arrives on one test but not another | The device changed networks between the tap and the first open, or the open came after the link's match window | Repeat the test on one network and open the app soon after install. On Android, install from Google Play so the install referrer carries the match. |
| App opens but stays on the home screen | Your router does not handle the URL from onLink | Log deepLink.destination and deepLink.deepLinkUrl and add the route |
| Android shows a disambiguation dialog | Digital Asset Links not verified | Match the SHA-256 fingerprint to your signing certificate |
Firebase Dynamic Links vs. WarpLink: Feature Mapping
| Firebase Dynamic Links | WarpLink | Notes |
|---|---|---|
Short links (page.link/xyz) | Short links (yourapp.aplnk.to/xyz) | Custom domains: 1 free for 3 months, then on every paid plan |
DynamicLinkComponents and the Short Links API | REST API, dashboard, or MCP server | One API for every platform |
socialMetaTagParameters | og_title, og_description, og_image_url | Bots get OG and Twitter Card tags automatically |
analyticsParameters (UTM) | utm_source, utm_medium, utm_campaign | Reported in WarpLink analytics |
handleUniversalLink() / getDynamicLink(intent) | onLink callback in configure() | One callback for cold start, warm start, and deferred |
| Deferred deep links | Automatic deferred check in configure() | Arrives in onLink with isDeferred |
| Link open logging in Google Analytics | WarpLink click analytics | Real time, with click deduplication |
| First-open counts per link | Install attribution | Referrer, IDFV, and probabilistic match cascade with a confidence score |
| Custom domains | Custom domains | Subdomains, with managed HTTPS |
apple-app-site-association and assetlinks.json | Generated and hosted | Regenerated on every app change |
| No AI access | MCP server | Free on every plan |
Frequently Asked Questions
Is Firebase Dynamic Links shutting down?
It already has. Google shut down Firebase Dynamic Links on August 25, 2025. Link creation, the console, and the Short Links API are gone, and Google says every link on page.link and on custom domains now returns HTTP 404. Some links still redirect in our tests, but Google can end that at any time.
Do Firebase Dynamic Links still work?
Google says no. Google says all Firebase Dynamic Links now return HTTP 404. On October 8, 2026 we requested 32 real page.link short links from 25 domains found in the September 2026 Common Crawl, the way an iPhone does. 25 still redirected, 15 to the app's own website or store and 10 to a Firebase preview page. The other 7 returned an error or no redirect. A redirect is not proof that your link still reaches the right screen, so migrate before it stops.
What is replacing Firebase Dynamic Links?
Google points to plain App Links and Universal Links. They open an installed app, but they drop store routing, deferred deep links, click analytics, and short links. To keep those, move to a link provider on a domain you control. WarpLink covers all four, adds install attribution, and starts free at 10,000 clicks a month.
Is Firebase getting deprecated?
No. Only Dynamic Links shut down. The rest of Firebase keeps running, so remove only the Dynamic Links SDK and keep FirebaseApp.configure() if your app uses other Firebase services.
What happens to my existing Firebase short links?
Google says all Firebase short links return HTTP 404 since August 25, 2025, including links on page.link subdomains and custom domains. Some still answer with a redirect today, but they can stop at any time. Links on page.link cannot be saved, because Google deleted those domains. Links on a domain you own can keep working once you recreate them on WarpLink and point the domain at WarpLink or at a redirect.
Can I keep my Firebase custom domain?
Yes, if it is a subdomain you control, such as links.yourapp.com. Add it under Settings > Domains, add the two DNS records the dashboard shows, and recreate each link with its old path as the slug. WarpLink slugs are one lowercase path segment, so mixed-case suffixes, path prefixes, and root domains need an HTTP redirect on your own host instead.
Do I need to update my AASA file manually?
No. WarpLink generates and hosts your apple-app-site-association file automatically based on your app registration. When you register your iOS app with your bundle ID, team ID, and path patterns, WarpLink builds the AASA file and serves it from your link domain. Any changes to your app configuration trigger automatic regeneration.
Can I run Firebase Dynamic Links and WarpLink side by side?
No. The Firebase Dynamic Links service shut down on August 25, 2025, and the FDL SDK no longer gets fixes, so it is not a base to build on. Remove the Firebase Dynamic Links SDK and any FDL-specific code paths as you wire WarpLink in, since leaving the FDL SDK installed only adds binary size and a failing call path.
What about analytics continuity?
WarpLink analytics start from the moment you create your first link. The Dynamic Links section of the Firebase console is gone, so the only Firebase history left is any link-open events Google Analytics logged and any export you ran before the shutdown. The data models differ, so there is nothing to import. Note your cutover date and report the two periods side by side.
How does WarpLink handle install attribution differently?
Firebase Dynamic Links recovered a link after install and counted first opens per link, but it did not report how each install was matched. WarpLink attributes each install through a match cascade, the Play Install Referrer on Android, the IDFV on iOS, and a probabilistic device match on both, and returns a match type and confidence with every result. The SDK does not use the IDFA or show an App Tracking Transparency prompt. For the mechanics, see The Complete Guide to Deep Linking.
What if I only need simple URL shortening?
WarpLink handles simple URL shortening too. Register your app, then create a link with just a destination_url and you get a short link with click analytics, social previews, and UTM tracking. Add the deep link fields whenever you need them. The Quickstart walks through it.
Sources
- WarpLink test, October 8, 2026: 32 real
page.linkshort links from 25 domains found in the September 2026 Common Crawl, requested the way an iPhone does. 25 still redirected (15 to the app's own website or store, 10 to a Firebase preview page) and 7 returned an error or no redirect. - Firebase, Dynamic Links Deprecation FAQ: the shutdown date, the HTTP 404 and 400/403 behavior, and the URL prefix deletion.
- Firebase, Migrate from Dynamic Links to App Links and Universal Links: which features Google's own path keeps and which it drops.
- Apple, Supporting associated domains, and Android Developers, Verify App Links: the platform rules behind Steps 3 and 5.
Next Steps
- Keep the checklist open. The Firebase migration reference condenses this guide into a checklist for the cutover.
- Read the SDK guides. Full setup and API reference for iOS, Android, React Native, and Flutter.
- Size your plan. Every plan and click limit is on the pricing page.
If a step does not behave as described, check the common test issues above or reach us through the dashboard.
Try WarpLink on one real link
Deep linking, install attribution, and real-time analytics in one SDK. Free for 10K clicks a month, no sales call.
Stuck on setup? Email support@warplink.app and we will help you get one real link working.
WarpLink Team
Building affordable, reliable link infrastructure for mobile teams. Deep linking, install attribution, and real-time analytics in one SDK.
Related Posts
How to Test Deep Links on iOS and Android: Commands and Checklist
Test deep links the way users hit them: simctl and adb commands, Universal Link and App Link checks, cold vs warm start, deferred installs, and CI with Maestro.
One Link for Both App Stores: How to Route iOS and Android Users
One link for both app stores: send iPhone to the App Store, Android to Google Play, desktop to your site, and installed users into the app.
Deep Linking: The Complete Guide for Mobile Developers
Learn how mobile deep links open specific app screens on iOS and Android. Compare URL schemes, Universal Links, App Links, fallbacks, and install attribution.