Blog Content
August 12, 2026

How to Set Up Hreflang Between Two Separate Shopify Stores? A 9,091-Product Case Study

How to set up hreflang when Shopify Markets doesn't work? A real-world case study where we connected two separate Shopify stores with SKU matching across 9,091 products.

In an internationalization project we managed, one of Turkey's long-established carpet brands came to us with this question: Our Turkish store has been serving the Turkish market seamlessly for years, and we have set up a separate Shopify store for the international market, but Google sees them as two independent sites unaware of each other. When we stepped in, the English store was selling on its own independent domain; as we began the project, we redirected this domain to the existing subdomain via CNAME. However, solving the domain side was not enough for Google to recognize the two stores as a single site; the problem was familiar, but the solution was not standard. Shopify's own documentation and most guides describe hreflang as a feature automatically set up via Shopify Markets in a single Shopify account; the situation we faced was two completely separate accounts, two separate databases, and two separate product catalogs. In this article, we explain step-by-step how we established the hreflang infrastructure between these two stores by matching 9,091 products via SKU codes, why we made certain technical decisions, and how a store owner facing the same problem can adapt this method to their own project.

What is Hreflang and How Does It Work in a Standard Shopify Scenario?

Hreflang is an HTML tag that informs search engines that different language or regional versions of a page are related to each other; without this information, Google treats the Turkish and English versions of the same product as two separate pieces of content competing against each other and gives both a lower ranking. In a standard Shopify setup (single account, multi-language managed via Shopify Markets), these tags are generated automatically; you add the language, connect it to Markets, and Shopify writes the rest into the <head> section of the page itself. We have already explained this standard process step-by-step in our Shopify multi-language store setup guide; and on the translation management side, our article on store translations and multi-language support covers the use of the Translate & Adapt app.

The subject of this article is not that standard scenario. Shopify Markets assumes that you define multiple markets within a single Shopify account; if you already have two independent Shopify accounts (for example, a local store established years ago and an international store opened later), the automatic hreflang mechanism of Markets cannot be activated because, from Shopify's perspective, these are two different sites with no relationship to each other. In this scenario, you need to set up hreflang manually via a common data point that will link the two stores; this is exactly what this case study is about.

Critical Note: If you have two separate Shopify accounts and both are selling under the same brand name, these two sites are effectively competitors in the eyes of Google; until hreflang is set up, your organic traffic is split between the two stores, and neither can reach its full potential. This is a risk specific to multi-store growth strategies that is never encountered in single-store setups.

Problem: Why Two Separate Shopify Stores Are a Different Issue

The difficulty of setting up hreflang between two separate Shopify accounts was not technical, but structural: there was no connection between the two stores that Google could understand, and even the product URLs did not match. While a rug product's handle (the product identifier in the URL) in the Turkish store was saved with a completely different naming convention, the handle for the same product in the English store had a totally different structure; both sides had grown independently over the years, and no one had configured product naming according to a common standard.

The tangible result of this situation was measurable: Turkish pages would occasionally appear in English search queries, and the English store would compete with the local store in Turkey-based searches. Even though the two stores sold the same product from the same brand, in Google's eyes, they were two separate competitors stealing each other's traffic. This is different from the classic "duplicate content" issue; the content is not word-for-word the same (the languages are different), but the search engine does not know at the entity level that these are the same product.

Background: The Domain History of the International Store

Before the hreflang project began, the English store was already selling on its own independent domain; as part of the project, we redirected this domain to the existing en.yourdomain.com subdomain via a CNAME record. Visitors coming to the old domain are automatically redirected to the new address; we defined separate redirection rules for both the root domain and the www version to ensure that no single rule was missed, which could have caused a portion of the traffic to be lost.

This migration had two risks from an SEO perspective: the loss of the backlink profile the old domain possessed and a drop in traffic during the transition. Neither happened; the old domain's backlink profile was transferred to the new address, and no traffic drop was observed in Google Search Console after the migration. This meant that when the hreflang project started, there was no remaining problem to solve on the domain side; the hreflang work was built on a single domain that was already correctly redirected, and there was no need to manage a separate hreflang-domain complexity between two different domains.

According to Nodus Works data: The most common cause of traffic loss in domain migrations is redirecting only the root domain and skipping the www version (or vice versa). Including both variants in separate redirection rules prevents many "we migrated the domain and traffic dropped" scenarios on its own.

Why Didn't Classic Solutions Work?

In a classic multi-language Shopify setup, hreflang is managed within a single account using a subdirectory (`/en/`) or subdomain (`en.site.com`) structure, and Shopify generates this automatically. Here, we were dealing with two completely separate Shopify accounts, two separate databases, and two separate product catalogs; Shopify's Markets infrastructure cannot know that these two accounts are "part of the same brand" because there is technically no mechanism for it to know. Third-party multi-language SEO apps did not directly support this specific scenario (two independent Shopify accounts) either; therefore, it was necessary to set up the solution manually from the stores' own data layers via the Admin API.

Solution Logic: Using the SKU as a Common Key

The only constant value that could link the two stores was the SKU code; even though product names, URLs, and titles differed between the stores, each product's SKU remained identical in both catalogs. This is a detail often overlooked in internationalization projects: brands typically adapt product names, descriptions, and even images for the local market, but the SKU code remains unchanged as it serves as an operational identifier. This is exactly what happened in this case study; the SKU was the only reliable bridge to build between two independent databases.

The solution logic built upon this single common point consisted of three steps: first, fetching the SKUs of all products in both stores; second, finding the intersection of the two sets (which SKUs exist in both stores); and finally, writing a metafield to each matching product pair that points to its counterpart. This third step is critical: instead of writing the hreflang tag statically, having each product carry the handle of its counterpart in the other language within its own metafield ensures the system stays self-updating if the catalog changes in the future (e.g., adding new products or removing existing ones).

Note: If the SKU matching is working, the hreflang mechanism does not look at the product name or URL structure at all; it only looks at the operational identifier (SKU). Therefore, no matter how inconsistent the product naming is between the stores, the solution method remains robust.

Technical Implementation Step 1: Product Matching via Admin API

Putting SKU matching into practice involved four sub-steps: fetching products, extracting SKUs, finding the intersection, and writing the result as a metafield. The Shopify Admin API's /products.json endpoint returns a maximum of 250 products per request and uses cursor-based pagination rather than numbered pages: by passing the page_info value from each response header to the next request, the entire catalog was fetched in over 35 requests; in this case, 8,733 products were obtained from the TR store and 8,766 from the EN store.

  1. All products from both stores, including their variants, were fetched via the Admin API.
  2. The SKUs from all variants of each product were collected into a separate set; since a product has multiple variants (e.g., different size options), the SKU → product ID/handle mapping was processed at the variant level.
  3. The intersection of the two sets was taken: 9,091 SKUs were found in both stores. 5,403 products were available only in the TR store, and 1,492 products were available only in the EN store; these products were excluded from the hreflang scope because they had no real counterpart in the other language.
  4. A cross-metafield was written for each matching product pair: custom.en_handle for the TR product and custom.tr_handle for the EN product.

tr_skus = {sku for p in tr_products for v in p["variants"]

           if (sku := v.get("sku", "").strip())}

en_skus = {sku for p in en_products for v in p["variants"]

           if (sku := v.get("sku", "").strip())}

matched = tr_skus & en_skus  # -> 9.091


write_meta(TR_STORE, product_id, "en_handle", en_handle)


write_meta(EN_STORE, product_id, "tr_handle", tr_handle)

This process required a total of 18,182 API calls (one write operation for each direction, 9,091 pairs × 2) and took approximately 3-4 hours. In long-running bulk API operations, connection drops and timeout errors are inevitable; to mitigate this risk, an exponential backoff logic and a progress file were added, allowing the script to resume from where it left off if interrupted by an error, rather than starting from scratch.

Risk to Avoid: Do not run a script for bulk API operations on thousands of products without progress logging. The Shopify Admin API's rate limit and network interruptions can cause the script to stop in the middle of a 3-4 hour process; without a progress file, this means repeating hours of work from the beginning.

Technical Implementation Step 2: Conditional Hreflang Injection with Liquid

After the metafields were written, a conditional hreflang block was added to the theme code of both stores; the logic was simple but contained a critical rule that determined the outcome: if the metafield is empty, the hreflang tag is not rendered at all. Without this rule, the 5,403 TR products that had no real counterpart in the other language would have produced an incorrect or empty hreflang tag; this would be like telling Google "look here, but there is nothing there," making the hreflang itself unreliable from a technical SEO perspective.

{% if product and product.metafields.custom.en_handle != blank %}

  <link rel="alternate" hreflang="tr"

        href="https://www.siteniz.com/products/{{ product.handle }}" />

  <link rel="alternate" hreflang="en"

        href="https://en.siteniz.com/products/{{ product.metafields.custom.en_handle }}" />

  <link rel="alternate" hreflang="x-default"

        href="https://www.siteniz.com/products/{{ product.handle }}" />

{% endif %}

A detail to note in this code is the x-default value: this is not a language code specific to Turkish or English, but a redirection signal meaning "show this if the user's language or region does not match any in this list." In this case, the default (Turkish) store was marked as x-default because the brand identity and stock depth were primarily based there; this decision may not be the same for every brand, and sometimes the English version is a more appropriate choice for x-default.

Tip: When deciding which store to assign as x-default, ask yourself "which version is in the brand's best interest to show to a new visitor with an ambiguous language" rather than "which language do we get more traffic in." This is a strategic decision independent of current traffic distribution.

Hreflang at the Collection and Page Level

The same conditional logic was applied beyond individual products to collection and static page levels, as hreflang carries the same risk of duplicate content not only on product pages but also on category (collection) pages and static pages like "About Us." The number of collections already differed between the two stores, which resulted in a lower matching rate than that of the products.

Scope TR Store EN Store Hreflang Linked Pairs
Products 8,733 8,766 9,091
Collections 215 165 150
Static Pages 11 9 7

The difference in the table is striking: while the TR store had 215 collections, the EN store had only 165, and only 150 of these had a true equivalent. This was not just a technical matching issue but also a signal of a content gap; collections that existed in the TR store but lacked an equivalent in the EN store pointed to a product group that had not yet been categorized for the English market. This gap became the starting point for the content work addressed in the next step.

Side Effect Fixes: Duplicate URL, Pagination, and Broken Links

While the hreflang infrastructure was being set up, three separate technical issues emerged around the main problem; none of these were hreflang itself, but if left uncorrected, they could have largely invalidated the benefits provided by hreflang.

Issue 1: /en/ URLs Creating Duplicate Content

Due to Shopify language settings in the TR store, URLs with the /en/ prefix were already active and receiving traffic in Google Search Console; these pages created a classic duplicate content issue as they contained the exact same content as the separate EN store. The solution was a metafield-controlled JavaScript redirect: products with an EN equivalent were redirected directly to the product page in the actual EN store, while products without an equivalent were sent to the EN store's homepage instead of a 404 error. This distinction was important; blindly sending everyone to the homepage would have led to unnecessary click loss for matching products.

Issue 2: Indexing of Pagination Pages

Collection pagination URLs (such as ?page=2, ?page=3) were being indexed by Google; since these pages do not carry independent search value, they were unnecessarily consuming crawl budget (the limited resources Google allocates for crawling a site). By adding a noindex, follow tag to these pages, Google was stopped from indexing them while still maintaining the ability to follow the links (and thus reach the products).

{% if paginate.current_page > 1 %}

  <meta name="robots" content="noindex, follow">

{% endif %}

Issue 3: Bulk Broken Internal Links

A site crawl performed with Semrush detected 6,856 broken internal links; at first glance, this looked like a task that would take weeks to fix individually. However, closer inspection revealed that 92% of these links (6,289 of them) pointed to a single URL, namely a single page that had been deleted previously. A single redirect rule resolved the vast majority of the broken links at once; the remaining few links were handled individually.

Note: When you see a report of thousands of broken links, your first reflex is "this will take weeks"; but do not estimate this time without grouping the broken links by target URL. Often, a small number of source pages are responsible for a disproportionate number of broken links.

Results: Before and After

In this case, we are not sharing organic traffic growth as an isolated metric because the hreflang setup was not a standalone change; it was implemented in conjunction with all the technical fixes described in this article (duplicate content, pagination, broken links). Therefore, the table below shows the structural status before and after, not traffic. Structural improvement is a prerequisite for traffic improvement but not proof of it on its own; leaving this distinction clear for the reader is the only way to convey the results without exaggeration.

Status / Metric Before After
Hreflang Linked Products 0 9,091 pairs
Hreflang Linked Collections 0 150 pairs
/en/ URL Duplicate Content Active, receiving traffic in GSC Resolved via metafield-controlled redirects
Pagination Index Status All pages being indexed Crawl budget protected with noindex, follow
Broken Internal Links 6,856 92% (6,289) resolved with a single redirect rule

The real structural gain here is this: Google now knows directly that the two stores are part of the same brand and which product is the equivalent of which. Before this information, the two stores were competitors; now, they are marked as different language versions of the same entity (brand/product entity) that complement each other.

How to Implement This in Your Own Store?

Before adapting this method to your own project, you need to clarify whether you are truly in this scenario; because if you have a single Shopify account, a much simpler solution (Shopify Markets) is already available, and the method in this article would be unnecessary complexity.

Check First

  • Do you have two (or more) completely separate Shopify accounts, or are you managing different languages within a single account? If it is the latter, our Shopify Markets guide is sufficient for you, the manual method in this article is not required.
  • Do your products have consistent SKUs in both stores? If SKUs are inconsistent (or not used at all), you must first establish a common identifier strategy for matching; a barcode, product code, or similar field can also be used instead of an SKU.
  • Do you have access to the Admin API and a technical resource (your own team or an agency) capable of processing bulk data? This task requires data processing too extensive to be performed manually from the Shopify admin panel.

Implementation Sequence

  • Fetch all products and variant SKUs from both stores using the Admin API.
  • Find the SKU intersection and keep non-matching products (those present in only one store) in a separate list; these will be outside the scope of hreflang.
  • Write a reciprocal metafield for each matching pair (a custom field that stores the handle of the counterpart store).
  • Add a conditional block to your theme code that does not generate hreflang if the metafield is empty.
  • Strategically decide which store to assign the x-default value to.
  • Extend the same logic to collections and static pages.
  • Also scan for side effects (duplicate URLs, pagination indexing, broken links); a hreflang setup does not automatically resolve these issues.

The riskiest part of this process is performing bulk API operations on thousands of products; rate limits, partial errors, and data consistency require an experienced technical team. Our Shopify integration and custom app development service, sets up such bulk data matching and API integration projects from end to end; on the technical SEO side, our technical SEO analysis and optimization service identifies such structural issues, including hreflang, at the beginning of the project.

Frequently Asked Questions

How is hreflang set up between two separate Shopify stores?

You match the products in both stores via a common identifier (usually the SKU) and write a metafield for the matching product pairs that points to their counterpart in the other store. You then add a conditional block to your theme code that generates a hreflang tag if this metafield is populated, and produces nothing if it is empty.

Why is manual hreflang necessary when Shopify Markets exists?

Shopify Markets manages markets within a single Shopify account and generates hreflang tags automatically. If you have two completely separate Shopify accounts (two separate subscriptions, two separate admin panels), Markets cannot know that these two accounts are related; in this case, hreflang must be set up manually.

What should I do if my product SKUs are inconsistent across the two stores?

If SKUs are inconsistent, you first need to determine a common identifier strategy; a barcode, supplier code, or a custom field for product codes can be used instead of the SKU. If there is no common field, you will need to start the matching process with a manual mapping table and fix SKU consistency as a future process.

Which store should I assign the x-default value to?

Not to the store where the majority of your current traffic comes from, but to the store you want to show to a new visitor whose language is undefined. The store that holds your primary brand identity and stock depth is usually a more accurate candidate for x-default.

From how many products onwards does this method make sense?

For a catalog of a few hundred products, a manual matching table may suffice; for thousands of products, an automated script via the Admin API is essential. Regardless of catalog size, the real deciding factor is whether the two stores are truly separate Shopify accounts.

Conclusion: Solving the Right Problem at the Right Layer

The main lesson of this case study was not just about hreflang itself, but about which layer the solution was sought in; since Shopify's own automated mechanism does not cover this scenario, the solution had to be built from the data layer of the stores (SKU matching and metafields). If you are a brand with the same structural issue, first verify if you are truly in this scenario; if it is a single Shopify account, Markets is sufficient, but if it is two separate accounts, the method here offers a viable roadmap.

If you would like to review the basic steps of a multilingual Shopify setup to our Shopify multilingual store setup guide, or if you would like to examine the translation management side to our store translations article you can take a look. If you would like to set up this matching for your own two-store structure you can contact Nodus Works.