Skip to main content

Declare your app's network access

Declare the external hosts and servers that your app uses in the network_access section of your app configuration file.


Your app's code runs on pages that merchants and customers trust: storefronts, checkout, and the Shopify admin. When you declare the hosts and servers that your app uses, Shopify knows what your app's links and requests should look like. We use the list to protect your app and monitor storefronts for malicious requests.

Declaring the hosts that your app links to also improves the customer experience. Shopify skips the exit interstitial for links from an online store to those hosts.


  • List every external host that your app links customers to from the online store in allowed_navigation_hosts. Only list hosts that you control, such as your app's own domain.
  • List every external server that your app's code loads from or sends requests to in allowed_sources. This covers your extensions and the pages that your app serves, such as your app home and HTML or Liquid app proxy pages.
  • Leave out anything that Shopify serves, such as the files in a theme app extension's assets folder. Leave out your app proxy URL too: the store serves app proxy pages from its own domain.

The following examples show code from three parts of an app, and the network_access entries that each one needs. The code is on the left, and the entries that it adds are on the right.

This app block links to the app's review page, loads a widget from the app's CDN, and fetches reviews from the app's API. The block's own reviews.js file is in the extension's assets folder, so Shopify serves it and it doesn't need an entry.

extensions/reviews/blocks/reviews.liquid

<div id="reviews" data-product-id="{{ product.id }}"></div>

<a href="https://app.example.com/reviews/{{ product.handle }}">
Read all reviews
</a>

<script src="https://cdn.app.example.com/widget.js" defer></script>
<script src="{{ 'reviews.js' | asset_url }}" defer></script>

{% schema %}
{
"name": "Product reviews",
"target": "section"
}
{% endschema %}

extensions/reviews/assets/reviews.js

const container = document.getElementById('reviews');

fetch(`https://api.app.example.com/reviews?product_id=${container.dataset.productId}`)
.then((response) => response.json())
.then((reviews) => renderReviews(container, reviews));

shopify.app.toml

[network_access]
# The "Read all reviews" link
allowed_navigation_hosts = ["app.example.com"]
allowed_sources = [
# The widget script
"https://cdn.app.example.com",
# The fetch in reviews.js
"https://api.app.example.com",
]

This app proxy route renders a Liquid page in the store's theme. The page links to the app's help center and loads the same widget. The store serves the page from its own domain, so the app proxy URL itself doesn't need an entry.

app/routes/apps.reviews.tsx

import {authenticate} from '~/shopify.server';

export async function loader({request}) {
const {liquid} = await authenticate.public.appProxy(request);

return liquid(`
<h1>Reviews for {{ shop.name }}</h1>
<div id="reviews"></div>
<a href="https://help.app.example.com/reviews">How reviews work</a>
<script src="https://cdn.app.example.com/widget.js" defer></script>
`);
}

shopify.app.toml

[network_access]
# The "How reviews work" link
allowed_navigation_hosts = ["help.app.example.com"]
# The widget script
allowed_sources = ["https://cdn.app.example.com"]

Anchor to Checkout UI extensionCheckout UI extension

This checkout UI extension fetches the customer's loyalty points from the app's API. To call fetch(), a checkout UI extension also needs the network_access capability in its own shopify.extension.toml file. That capability is separate from the app's [network_access] section.

extensions/loyalty/src/Checkout.jsx

import '@shopify/ui-extensions/preact';
import {render} from 'preact';
import {useEffect, useState} from 'preact/hooks';

export default function extension() {
render(<Extension />, document.body);
}

function Extension() {
const [points, setPoints] = useState(null);

useEffect(() => {
async function loadPoints() {
const token = await shopify.sessionToken.get();
const response = await fetch('https://api.app.example.com/loyalty', {
headers: {Authorization: `Bearer ${token}`},
});
const data = await response.json();
setPoints(data.points);
}

loadPoints();
}, []);

if (points === null) return null;

return <s-banner>You have {points} loyalty points.</s-banner>;
}

shopify.app.toml

[network_access]
# The fetch to the loyalty API
allowed_sources = ["https://api.app.example.com"]

Anchor to Complete configurationComplete configuration

List each host and source once, however many parts of your app use it. Together, the three examples need this configuration:

shopify.app.toml

[network_access]
allowed_navigation_hosts = [
"app.example.com",
"help.app.example.com",
]
allowed_sources = [
"https://api.app.example.com",
"https://cdn.app.example.com",
]

Run shopify app deploy. Shopify checks both lists when it creates the app version, and rejects the deploy if an entry doesn't follow the rules in the network_access reference.


Was this page helpful?