# Easy Ways to Set Up Your Own Deep Linking System

## What is Deep Linking?

Deep linking is a technique that takes users directly to a specific page or screen of an app. A deep link is simply a regular web URL. When opened in a browser, it displays a webpage, like a specific post. However, in the context of a mobile app, it opens the app instead of the browser.

Let's look at a real-world example. Imagine you're scrolling through Instagram reels in your free time and come across a funny reel you want to share with a friend. You share the reel through a link on another platform, like WhatsApp. That link is a deep link. When your friend opens the link on their mobile app and already has Instagram installed, it will open in the app. If not, it will redirect them to the Play Store to install the app—this is called deferred deep linking. If your friend opens the link in a browser on a laptop or desktop, it will redirect to the same reel in the browser.

Now that you understand what deep linking is, we will move on to the implementation part. Before we start, let's discuss what we will use in this process.

## Tech Used

* React Native : We have used this to build mobile app you can use other tech as well.
    
* Aws Ec2 : Cloud server hosting Nginx and your domain
    
* Nginx: Web server to serve Android, IOS configuration file
    
* Node js: Running Server with redirecting web page if application not available on mobile phone you can use any other backend tech as well instead of this.
    

Now that we understand what we are using and the alternatives, let's start the implementation.

First, we will begin with the backend implementation.

## Creating an API Endpoint in Node.js

### app.ts

Add below router in app.ts file

```javascript
...OtherCode
app.use("/s", deeplinkRoutes);
```

### routes/deep-linking.routes.ts

Setup deep linking routes

```javascript
 import { Router } from "express";
import DeeplinkController from "../controllers/deeplink.controller";
const deeplinkRoutes = Router();

deeplinkRoutes.get("/:id", DeeplinkController.deeplink);
export default deeplinkRoutes;
```

### controllers/deeplink.routes.ts

Create controller for deeplink

```javascript
import { Request, Response } from "express";

class DeeplinkController {
  static async deeplink(
    req: Request<{ id: string }>,
    res: Response
  ) {
    const { id } = req.params;
    // Serve HTML page instead of JSON
    const htmlContent = `<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>TITLE</title>
  <script>
    // Try to open the app
    setTimeout(function() {
      window.location.href = "<APP_NAME>://s/${id}";
    }, 50);
    
    // Check if iOS
    var userAgent = navigator.userAgent || navigator.vendor || window.opera;
    if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
      // For iOS, redirect to App Store
      setTimeout(function() {
        window.location.href = <APP_STORE_LINK>;
      }, 2000);
    } else {
      // For Android, redirect to Play Store
      setTimeout(function() {
        window.location.href = <PLAY_STORE_LINK>;
      }, 2000);
    }
  </script>
</head>
<body>
  <div style="text-align: center; padding-top: 100px;">
    <h1>Opening App...</h1>
    <p>If the app doesn't open automatically, <a href="<PLAY_STORE_LINK>">download it from the Play Store</a> or <a href="<APP_STORE_LINK>">App Store</a>.</p>
  </div>
</body>
</html>`;

    res.setHeader("Content-Type", "text/html");
    res.status(200).send(htmlContent);
    return;
  }
}

export default DeeplinkController;
```

This endpoint will handle redirection to the Play Store or App Store if the app is not installed on the device.

### Android: assetlinks.json

```json
[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.yourapp",
      "sha256_cert_fingerprints": ["YOUR_SHA256_CERT_FINGERPRINT"]
    }
  }
]
```

sha256\_cert\_fingerprints is used for app security. Get it using:

```plaintext
keytool -list -v -keystore my-release-key.keystore -alias my-key-alias
```

### IOS: apple-app-site-association

```json
{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "TEAM_ID.com.yourapp",
        "paths": []
      }
    ]
  }
}
```

These two files are needed for App Links and Universal Links to verify that your app has permission to open specific links.

Without these rules, when a user clicks a link like “https://myapp.com/s/:id”, it will always open in the browser instead of the app.

## Why are these Files Needed?

### Android: assetlinks.json

* Android needs this file to confirm that the domain (myapp.com) can open links in the app.
    
* It stops **malicious apps** from pretending to own other domains.
    
* It must be available at:
    
    [**https://myapp.com/.well-known/assetlinks.json**](https://myapp.com/.well-known/assetlinks.json)
    

### IOS: apple-app-site-association (AASA)

* IOS uses this file to link your domain (my app.com) with your app.
    
* Without this file, Universal Links will not open your app.
    
* It must be hosted at:
    
    [**https://myapp.com/.well-known/apple-app-site-association**](https://myapp.com/.well-known/apple-app-site-association)
    

## How Do These Files Work?

* A user clicks a deep link (https://myapp.com/s/asf313ehvas56dshtfoisag).
    
* The OS checks if the app is installed:
    
    * If installed → It looks for the assetlinks.json (Android) or AASA (iOS) file.
        
    * If verified → The app opens.
        
    * If verification fails or the app is not installed → The link opens in a web browser.
        
* File Verification Steps:
    
    * Android/iOS checks https://myapp.com/.well-known/ for the file.
        
    * If the app’s package name (com.yourapp) or App ID matches, the OS allows deep linking.
        

Now that we understand the role of these two files, we will serve them through Nginx on an EC2 instance.Step 1: Prepare the apple-app-site-association file and assetlinks.json file

First, log in to the EC2 instance using SSH, then follow these steps:

### Create the .well-known Directory

Run the following command on your EC2 instance:

```bash
sudo mkdir -p /var/www/html/.well-known
```

Now create **assetlinks.json** and apple-app-site-association in that path.

### Set Correct permissions

```bash
sudo chmod 644 /var/www/html/.well-known/apple-app-site-association
sudo chown www-data:www-data /var/www/html/.well-known/apple-app-site-association
sudo chmod 644 /var/www/html/.well-known/assetlinks.json
sudo chown www-data:www-data /var/www/html/.well-known/assetlinks.json
```

## Step 2: Configure Nginx to Serve the file

Go to nginx path

```bash
cd /etc/nginx
```

Create conf.d and application conf file if not available

```bash
ubuntu@EC2:/etc/nginx$ mkdir conf.d && cd conf.d
ubuntu@EC2:/etc/nginx/conf.d$ sudo vi app_name.conf
```

Find the server {} block and add this inside it:

```nginx
location /.well-known/assetlinks.json {
    add_header Content-Type application/json;
    root /var/www/html;
}
```

## Step 3: Restart Nginx

```powershell
sudo systemctl restart nginx
```

Check if Nginx is running:

```powershell
sudo systemctl status nginx
```

## Step 4: Verify the File is Accessible

Now, visit:

```plaintext
https://mydomain.com/.well-known/apple-app-site-association
https://mydomain.com/.well-known/assetlinks.json
```

> Note: Do add a SSL certificate

With this we are done with backend implementation now let’s implement on mobile app side.

## Mobile Application Setup

### Step 1: React Native Setup

Install Dependencies

```bash
npm install @react-navigation/native react-native-screens react-native-safe-area-context react-native-gesture-handler
npm install react-native-reanimated
```

Configure Navigation with Deep Linking

```javascript
const linking = {
  prefixes: ['https://mydomain.com','<app_name>://'], // Also added custome schema for redirection from browser
  config: {
    screens: {
      Home: '',
      Profile: 's/:id',
    },
  },
};

<NavigationContainer linking={linking}>
  {/* Your Stack Navigator */}
</NavigationContainer>
```

### Step 2: Android App Links Integration

Add Intent Filter to AndroidManifest.xml

inside&lt;activity android:name=”.MainActivity:&gt;:

```xml
<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="<APP_NAME>" android:host="mydomain.com" />
</intent-filter>
```

### Step 3: Universal Links Integration

**Entroll in Apple Developer Program**

You must enroll to get your Team ID and use Associated Domains.

**Enable Associated Domains in Xcode**

* Open your iOS project in **Xcode**.
    
* Select your app target.
    
* Go to the **“Signing & Capabilities”** tab.
    
* Click the **“+ Capability”** button.
    
* Select **“Associated Domains”** from the list.
    
* In the new **“Associated Domains”** section, click the **+** and add:
    

```plaintext
applinks:mydomain.com
```

That's it! Congratulations, you have successfully created a custom deep linking implementation. Now, if you open https://mydomain.com/s/f15f8008-c9ab-49b2-a995-4c8f488df2f6, it will open directly in the app.

## Conclusion

Implementing deep linking in a React Native app using your own domain—without relying on Firebase—gives you **complete control** over link behavior, branding, and user experience. By integrating **Android App Links** and **iOS Universal Links**, and properly setting up domain verification through assetlinks.json and apple-app-site-association, your app can open specific screens directly from links shared via WhatsApp, email, or anywhere else.

Setting up custom deep linking requires some initial backend work (like SSL, hosting .well-known files, and domain verification), but it’s a **future-proof** and **production-ready** approach that works across platforms. Once everything is set up correctly, users with your app installed will enjoy **seamless navigation**, and those without it will be smoothly redirected to the App Store or Play Store.

If you’re building a serious mobile product, choosing this method ensures your linking experience is secure, fast, and completely under your control—with **no third-party dependency**.
