---
title: "Email Universal Link Setup"
slug: "email-universal-link-setup-1"
updated: 2026-05-11T15:51:02Z
published: 2026-05-11T15:51:02Z
canonical: "academy.insiderone.com/email-universal-link-setup-1"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://academy.insiderone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Email Universal Link Setup

A universal link is a special URL type that works on web browsers and mobile apps. It ensures users have a smooth experience no matter what device they use. With universal links, businesses can make it easy for their users to directly open Android and iOS apps from delivered emails and access their content on any device via only one link that works everywhere.

Universal links work as follows:

- **Opens app or web**: If a user has the app, the link opens the app. If not, it opens the web.
- **Offers better user experience:** Users get to the right content quickly and easily.

This article explains how the universal link setup process works, provides SSL setup examples for multiple providers such as AWS, Google Cloud, etc., and the following steps to complete the setup process:

[1. DNS record setup](/docs/email-universal-link-setup#1-dns-record-setup) [2. SSL setup for your link branding domain](/docs/email-universal-link-setup#2-ssl-setup-for-your-link-branding-domain) [3. Verification](/docs/email-universal-link-setup#3-verification) [4. Handling asset files](/docs/email-universal-link-setup#4-handling-asset-files) [5. Verification](/docs/email-universal-link-setup#5-verification) [6. Application](/docs/email-universal-link-setup#6-application) [7. Link resolution](/docs/email-universal-link-setup#7-link-resolution)

> If you want to complete the universal link setup, you need to [create a ticket](https://academy.useinsider.com/docs/creating-support-tickets) for Insider's deliverability team.

## [1. DNS record setup](/docs/email-universal-link-setup#1-dns-record-setup)

Insider deliverability team will share two **CNAME** records with you to add under your domain.

Add the respective **CNAME** records in your domain hosting provider settings and inform the deliverability team.

| Type | Host | Value |
| --- | --- | --- |
| CNAME | url{rand-id-1}.{your-domain}.com | sendgrid.net |
| CNAME | {rand-id-2}.{your-domain}.com | sendgrid.net |

Or

| Type | Host | Value |
| --- | --- | --- |
| CNAME | {subdomain}.{your-domain}.com | sendgrid.net |
| CNAME | {rand-id-2}.{your-domain}.com | sendgrid.net |

Your title goes hereReplace the placeholders with your own specific URLs. *E.g. url123.acme.com, 12334.acme.com.*

> Below you can see DNS record setup tutorials for some providers:
> 
> - [Cloudflare](https://www.youtube.com/watch?v=PYSIt3fEEoI)
> - [AWS Route53](https://www.youtube.com/watch?v=dRoaj0QDjg0)
> - [Google Domains](https://www.youtube.com/watch?v=t0vbawaPUN4)
> - [GoDaddy](https://www.youtube.com/watch?v=x9ueVrFlIbM)
> - [Azure](https://www.youtube.com/watch?v=uumSt8VLM7o)

## [2. SSL setup for your link branding domain](/docs/email-universal-link-setup#2-ssl-setup-for-your-link-branding-domain)

To set up SSL for your link branding domain, you can proceed to **2.1 OR 2.2**. If you are using one of the **providers** listed in 2.1 (e.g. AWS Route53, GoDaddy, Azure, etc.), proceed to follow section **2.1.** If you're using **CDN**, proceed to follow section **2.2**.

2.1. **Follow these steps if you are using one of the providers listed below (e.g. AWS Route53, GoDaddy, etc.).**

You must set up SSL for your link branding domain (e.g. url123.yourdomain.com) as you would for the other domains.

SSL setup will vary depending on the service provider and the available certificate. You need a certificate that will be valid for the CNAME record shared with you. If you already have a wildcard (*.yourdomain.com) SSL that covers any subdomains, you can easily set it up. However, if you choose to use a double subdomain (url123.sub.yourdomain.com) as your link branding and you do not have a double wildcard certificate (*.*.yourdomain.com) or your SSL certificate is exclusive to your main domain (yourdomain.com), you will need to purchase a certificate to complete the setup.

> Below you can see SSL setup tutorials for some providers:
> 
> - [AWS Route53](https://www.youtube.com/watch?v=ookzXuMr8eY)
> - [Google Domains](https://www.youtube.com/watch?v=245ZJLm1AV4&amp;t=1477s)
> - [GoDaddy (CPanel)](https://www.youtube.com/watch?v=h-CN_JVi0kU)
> - [Azure](https://learn.microsoft.com/en-us/azure/app-service/configure-ssl-certificate?tabs=apex)
> - [NGINX](https://gist.github.com/omermevlut/23de878d274eade0580ec93efb95fcf4)

2.2. **Follow these steps if you are using CDN.**

Forward all the requests coming to url123.yourdomain.com to sendgrid.net. You can forward the request using your own CDN or Proxy.

> Below you can see the CDN/Proxies documents for some providers:
> 
> - [Cloudfront](https://support.sendgrid.com/hc/en-us/articles/4412701748891-How-to-configure-SSL-for-click-tracking-using-CloudFront)
> - [Cloudflare](https://www.twilio.com/docs/sendgrid/ui/sending-email/content-delivery-networks/using-cloudflare-as-your-content-delivery-network-cdn)
> - [Fastly](https://www.twilio.com/docs/sendgrid/ui/sending-email/content-delivery-networks/using-fastly-as-your-content-delivery-network-cdn)
> - [KeyCDN](https://www.twilio.com/docs/sendgrid/ui/sending-email/content-delivery-networks/using-keycdn-as-your-content-delivery-network-cdn)
> - [Sendgrid documentation for proxy setup](https://www.twilio.com/docs/sendgrid/ui/account-and-settings/custom-ssl-configurations)
> - [NGINX](https://gist.github.com/omermevlut/23de878d274eade0580ec93efb95fcf4)

At this point, your DNS records should look like as in the following example table:

| Type | Host | Value |
| --- | --- | --- |
| CNAME | url123.yourdomain.com | URL to your CDN or Proxy or API Gateway |
| CNAME | 123456.yourdomain.com | sendgrid.net |

Your title goes hereDo not change the CNAME for 123456.yourdomain.com, it should still point to [sendgrid.net](//sendgrid.net). 123456 is an example and it can vary in your implementation.

## [3. Verification](/docs/email-universal-link-setup#3-verification)

## 

Verification consists of two steps: verification on your end and verification on Insider. Once the previous steps are completed, you can verify if everything has been set up correctly on your end. To verify on your end, you will need to run the following command:

```custom
dig ns url{rand-id}.{yourdomain}.com
// or
dig ns {subdomain}.{yourdomain}.com
```

![](https://cdn.document360.io/c6df4583-da94-4cb2-bb8a-be0cbdd11109/Images/Documentation/unnamed%20(98)(1).png)

### Verify Certificate

To verify the certificate, run the following command:

```custom
openssl s_client -showcerts -connect url{rand-id}.{yourdomain}.com:443
// or
openssl s_client -showcerts -connect {subdomain}.{yourdomain}.com:443
```

You will receive a public certificate as a response with the following response: ![](https://cdn.document360.io/c6df4583-da94-4cb2-bb8a-be0cbdd11109/Images/Documentation/unnamed%20(99)(1).png)

Once you verify everything on your end, you can contact the Insider One team to verify your setup. Once the setup is verified on Insider's end, your next email will have your custom domain in the links instead of sendgrid.net.

## [4. Handling asset files](/docs/email-universal-link-setup#4-handling-asset-files)

For universal links to work, you need to include your application-specific asset files under the subdomain you set up on the [DNS record setup step.](/docs/email-universal-link-setup#1-dns-record-setup) Your setup must include route prioritization to re-route the incoming requests to assetlinks.json (Android) and apple-app-site-association (iOS) to your file server (or cloud storage) and everything else to sendgrid.net.

Adding the asset files under the subdomain will ensure that your app is opened instead of the web page every time it is clicked. For example, if your links are branded as https://links.acme.com, your asset files should be available as https://links.acme.com/.well-known/assetlinks.json, https://links.acme.com/.well-known/apple-app-site-association, and https://links.acme.com/apple-app-site-association (without the JSON extension).

The sample case above would look as follows:

| Precedence | Path | Origin | Protocol |
| --- | --- | --- | --- |
| 0 | apple-site-association | File Server | HTTPS Only |
| 1 | .well-known/apple-site-association | File Server | HTTPS Only |
| 2 | .well-known/assetlinks.json | File Server | HTTPS Only |
| 3 | * | sendgrid.net | HTTP and HTTPS |

Below you can see some examples of different providers.

### NGINX

```custom
server {
  listen 80;
  listen 443 ssl;
  server_name 'links.acme.com';
  ssl_certificate '/etc/pki/tls/certs/links.acme.com.crt';
  ssl_certificate_key '/etc/pki/tls/private/links.acme.com.key';
  location = /apple-app-site-association {
    root '/var/www/links.acme.com';
    default_type 'application/json';
  }
  location = /.well-known/apple-app-site-association {
    root '/var/www/links.acme.com';
    default_type 'application/json';
  }
  location = /.well-known/assetlinks.json {
    root '/var/www/links.acme.com';
    default_type 'application/json';
  }
  location / {
    proxy_pass 'https://sendgrid.net';
    proxy_set_header 'Host' 'links.example.com';
  }
}
```

### AWS Route53

Refer to the [Universal Link Setup page](https://www.twilio.com/docs/sendgrid/ui/sending-email/universal-links) to set up universal links using Route53, S3, and Cloudfront.

### GCP

For Google Cloud, you can utilize [routes](https://cloud.google.com/vpc/docs/routes) or use [URL maps](https://cloud.google.com/load-balancing/docs/url-map) on your load balancer to handle the traffic.

## [5. Verification](/docs/email-universal-link-setup#5-verification)

If you set up your asset configuration internally, you can use a bastion server. If it's a public setup, you can directly test your asset links and routing. In either case, testing will be the same.

```custom
[must return JSON response]
curl http://internal.domain/.well-known/assetlinks.json
curl http://internal.domain/.well-known/apple-app-site-association
curl http://internal.domain/apple-app-site-association

[must return 404 response from sendgrid.net]
curl http://internal.domain/test-404-response
```

## [6. Application](/docs/email-universal-link-setup#6-application)

Once you’ve verified that the asset links are set up correctly, you can replace sendgrid.net in your DNS setup with your internal setup.

```custom
links.acme.com => sendgrid.com
// will be updated as
links.acme.com => example.internal.load-balancer.com
// or
links.acme.com => cloudfront.com/12345
// or
links.acme.com => 127.0.0.1 (NGINX instance IP)
```

Your title goes hereVerify that the same files and routing work through your subdomain (links.acme.com). If the routing fails and the URLs are broken, you should avoid sending emails until you fix the issue.

## [7. Link resolution](/docs/email-universal-link-setup#7-link-resolution)

Now you can trigger your application directly from the email links. However, the links are encrypted and the destination is unavailable. **Link resolution** is required as the last step of the implementation for your application to redirect users to the relevant pages correctly within your application.

To complete this action, follow these three steps:

1. Send an HTTP request to the link the user clicks to resolve the destination link.
2. Obtain the destination from the sent request.
3. Redirect the user to the relevant page in your application.

This will ensure that the user will be redirected to the destination page and the click log is delivered to the Insider server.

Below you can see some examples:

### Android link resolution example

```java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    onNewIntent(getIntent());
}

protected void onNewIntent(Intent intent) {
    String action = intent.getAction();
    final String encodedURL = intent.getDataString();
    if (Intent.ACTION_VIEW.equals(action) && encodedURL != null) {
        Log.d("App Link", encodedURL);
        new Thread(new Runnable() {
            public void run() {
                try {
                    URL originalURL = new URL(encodedURL);
                    HttpURLConnection ucon = (HttpURLConnection) originalURL.openConnection();
                    ucon.setInstanceFollowRedirects(false);
                    URL resolvedURL = new URL(ucon.getHeaderField("Location"));
                    Log.d("App Link", resolvedURL.toString());
                }
                catch (MalformedURLException ex) {
                    Log.e("App Link",Log.getStackTraceString(ex));
                }
                catch (IOException ex) {
                    Log.e("App Link",Log.getStackTraceString(ex));
                }
            }
        }).start();
    }
}
```

### iOS link resolution examples

```swift
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
    if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
        guard let encodedURL = userActivity.webpageURL else {
            print("Unable to handle user activity: No URL provided")
            return false
        }
        let task = URLSession.shared.dataTask(with: encodedURL, completionHandler: { (data, response, error) in
            guard let resolvedURL = response?.url else {
                print("Unable to handle URL: \(encodedURL.absoluteString)")
                return
            }
            // Now you have the resolved URL that you can
            // use to navigate somewhere in the app.
            print(resolvedURL)
        })
        task.resume()
    }
    return true
}
```

```objectivec
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
    if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) {
        NSURL *encodedURL = userActivity.webpageURL;
        if (encodedURL == nil) {
            NSLog(@"Unable to handle user activity: No URL provided");
            return false;
        }
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionDataTask *task = [session dataTaskWithURL:encodedURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (response == nil || [response URL] == nil) {
                NSLog(@"Unable to handle URL: %@", encodedURL.absoluteString);
                return;
            }
            // Now you have the resolved URL that you can
            // use to navigate somewhere in the app.
            NSURL *resolvedURL = [response URL];
            NSLog(@"Original URL: %@", resolvedURL.absoluteString);
        }];
        [task resume];
    }
    return YES;
}
```

> If you’re using an external company to handle your universal links such as *branch.io*, you might want to consult with the respective vendors on how to set up the universal links. If you have a mixed setup where you use both your own links and an external company setup, you need to complete all the steps listed in this guide and contact your provider to set it up with them as well.

## Related

- [Email Link Branding Setup](/link-branding-setup.md)
