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",
]

Test your lists before you deploy them. Run your app with shopify app dev on a dev store that has the Network access testing feature preview. On that store, links to undeclared hosts show the exit prompt, and the browser console warns when a page connects to a server that your app hasn't declared.

To test your app:

  1. Create a dev store and select the Network access testing feature preview.

  2. From your app's directory, run shopify app dev --store <your-dev-store>.myshopify.com, and keep it running while you test. Shopify CLI installs your app on the store and applies the [network_access] section of your local configuration file to it. If an entry doesn't follow the rules in the network_access reference, then the command fails and shows the error.

    Don't install other apps on the store. Shopify combines the allowed_sources of every app on the store into one policy, so another app's entry can hide one that's missing from yours.

  3. Add your app's theme app extension blocks to the theme, and turn on its app embeds.

  4. Open your browser's developer console, then use each feature of your app on the online store, at the store's myshopify.com address. Don't test in a theme preview, such as the theme editor's Preview or the local 127.0.0.1 preview URL that Shopify CLI prints for theme app extensions. Shopify doesn't add the Content Security Policy to theme previews, so the console stays quiet there even when servers are missing. To confirm that the policy applies, check that the page's response has a Content-Security-Policy-Report-Only header.

  5. Check for problems:

    • Links: If a link to one of your hosts shows the exit prompt, then add the host to allowed_navigation_hosts. A link that doesn't show the prompt might still be undeclared, because Shopify also skips the prompt for its own hosts, popular social and service sites, the store's domains, and hosts that the merchant trusts.

    • Requests: If the console shows a Content Security Policy warning for one of your servers, then add the server to allowed_sources. If the server is already in the list, then look for it in the page's Content-Security-Policy-Report-Only header:

      • If the header lists the server, then check that the entry matches the URL that the request uses.
      • If the header lists none of your app's servers, then check that shopify app dev is still running for this store. If it is, then Shopify left every app's servers out of the policy for that page view. Shopify does that when the policy would be too long or when it can't read the apps' declarations, and then declared servers warn too.

      The console doesn't warn about Google Fonts. If your app uses them, then add both https://fonts.googleapis.com and https://fonts.gstatic.com to allowed_sources.

  6. After you update a list, wait for shopify app dev to update the preview, and then test again.

Caution

This feature preview is under development, and its behavior might change.


When your lists are complete, run shopify app deploy and release the new app version. Shopify checks both lists again when it creates the version, and rejects the deploy if an entry doesn't follow the rules in the network_access reference.


Was this page helpful?