<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: WIO</title>
    <description>The latest articles on DEV Community by WIO (@xwiox).</description>
    <link>https://dev.to/xwiox</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4206624%2F1c1baa6a-2038-4590-bf1c-08b464860e98.png</url>
      <title>DEV Community: WIO</title>
      <link>https://dev.to/xwiox</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xwiox"/>
    <language>en</language>
    <item>
      <title>How to share one App Store link that works in every country</title>
      <dc:creator>WIO</dc:creator>
      <pubDate>Mon, 26 Oct 2026 00:54:50 +0000</pubDate>
      <link>https://dev.to/xwiox/how-to-share-one-app-store-link-that-works-in-every-country-3o83</link>
      <guid>https://dev.to/xwiox/how-to-share-one-app-store-link-that-works-in-every-country-3o83</guid>
      <description>&lt;p&gt;If you've ever put an App Store link in a tweet, a README, or a newsletter, you've probably copied it straight from your browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://apps.apple.com/us/app/your-app/id1232780281
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;/us/&lt;/code&gt; is the problem. App Store web links carry a two-letter country code, because Apple runs a separate store (a "storefront") for each country. Someone in Japan who taps a &lt;code&gt;/us/&lt;/code&gt; link is looking at the US store. And if your app isn't sold in their country at all, they hit Apple's "This app is not available in your country or region" message instead of your app.&lt;/p&gt;

&lt;p&gt;Making a separate link for every country isn't realistic. You'd need to know where each reader is before they click. So the link has to work it out at click time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The useful fact: the app ID never changes
&lt;/h2&gt;

&lt;p&gt;Every app has a numeric ID, the &lt;code&gt;id1232780281&lt;/code&gt; part. It's the same in every country. The only thing that changes between storefronts is the country code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apps.apple.com/us/app/id1232780281
apps.apple.com/jp/app/id1232780281
apps.apple.com/de/app/id1232780281
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So if you know the visitor's country and you know the app is sold there, you can build the right URL yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: where is the visitor?
&lt;/h2&gt;

&lt;p&gt;If your code runs on Cloudflare Workers, you get this for free. Cloudflare attaches the visitor's country to every request as &lt;code&gt;request.cf.country&lt;/code&gt;, an ISO code like &lt;code&gt;JP&lt;/code&gt;. Two values need care: &lt;code&gt;XX&lt;/code&gt; means unknown and &lt;code&gt;T1&lt;/code&gt; means Tor. Treat both as "no country".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;normalizeCountry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Za-z&lt;/span&gt;&lt;span class="se"&gt;]{2}&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cc&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;xx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;cc&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;t1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the country is unknown, the safest redirect is Apple's country-less link, &lt;code&gt;https://apps.apple.com/app/id&amp;lt;ID&amp;gt;&lt;/code&gt;, and Apple picks the store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: is the app sold there?
&lt;/h2&gt;

&lt;p&gt;Apple has a public lookup endpoint (the old iTunes Search API) that answers this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://itunes.apple.com/lookup?id=1232780281&amp;amp;country=jp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the app is in that storefront you get &lt;code&gt;resultCount: 1&lt;/code&gt; plus its name and icon. If it isn't, you get &lt;code&gt;resultCount: 0&lt;/code&gt;. No key needed.&lt;/p&gt;

&lt;p&gt;The endpoint is rate-limited, so don't call it on every click. On Workers you can cache the fetch at the edge:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cacheTtl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;cacheEverything&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A day of caching means Apple only gets asked about each app and country pair occasionally. The trade-off: when an app launches in a new country, detection can lag by up to a day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: when it isn't sold there
&lt;/h2&gt;

&lt;p&gt;Now you need a fallback. Most apps are in the US store, so check that first, then walk a list of other large storefronts (UK, Canada, Australia, Germany, France, Japan and so on) until one says yes.&lt;/p&gt;

&lt;p&gt;On a computer or Android phone, you can just redirect to that store's page. The visitor sees the app, its screenshots, its description.&lt;/p&gt;

&lt;h2&gt;
  
  
  The iPhone catch
&lt;/h2&gt;

&lt;p&gt;This is the part that surprised me. On iPhone and iPad, App Store links open in the App Store app, and the App Store app can only show the store for the account's own country. So redirecting a visitor in, say, Brazil to the &lt;code&gt;/us/&lt;/code&gt; page doesn't show them the US page. It ends in the same "not available" message you were trying to avoid.&lt;/p&gt;

&lt;p&gt;The only honest fix is to not redirect. Instead, show a small page with the app's icon and name (both come from the lookup response) and a button to the store that has it. At least the visitor sees which app you meant and where it's sold. It's less magic than a redirect, but it doesn't dead-end.&lt;/p&gt;

&lt;h2&gt;
  
  
  One more failure case
&lt;/h2&gt;

&lt;p&gt;If Apple's endpoint is down or returns an error, don't show an error page. Send the visitor to their own country's page. Most of the time the app is sold there anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together: Hither
&lt;/h2&gt;

&lt;p&gt;I wrapped all of this into a small free tool called &lt;a href="https://hither.link/" rel="noopener noreferrer"&gt;Hither&lt;/a&gt;. You paste any App Store link (from any country) or a bare app ID, and you get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hither.link/1232780281
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The link holds only the app ID, so there's no database. The same app always gets the same link, links don't expire, and nobody can change where a link goes, because everything else comes from Apple at click time. There's no account. You can even make your link before launch, using the Apple ID from your app's App Information page in App Store Connect, and put it on your landing page ahead of time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run your own copy
&lt;/h2&gt;

&lt;p&gt;The code is &lt;a href="https://github.com/xxwioxx/hither" rel="noopener noreferrer"&gt;on GitHub under the MIT license&lt;/a&gt;. It's a single Cloudflare Worker, and the Apple-specific bits live in one file (&lt;code&gt;src/apple.ts&lt;/code&gt;) while the routing decision is pure, tested logic (&lt;code&gt;src/decide.ts&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm &lt;span class="nb"&gt;test
&lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To deploy to your own domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx wrangler login
&lt;span class="nv"&gt;CLOUDFLARE_ACCOUNT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-account-id npm run deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the domain in &lt;code&gt;wrangler.toml&lt;/code&gt;, &lt;code&gt;public/robots.txt&lt;/code&gt; and &lt;code&gt;public/sitemap.xml&lt;/code&gt; first. On Cloudflare's free plan a Worker handles 100,000 requests a day, and going over makes requests fail until the daily reset instead of producing a bill.&lt;/p&gt;

&lt;p&gt;Ideas on the list: Google Play links, custom short names (with a way to prove you own the app), and a QR code per link. If you've hit other edge cases with App Store links across countries, I'd like to hear about them in the comments.&lt;/p&gt;

</description>
      <category>ios</category>
      <category>webdev</category>
      <category>cloudflare</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
