Quick answer: Shopify handles more SEO for you than most guides admit. It writes canonical tags correctly, generates your sitemap, and injects hreflang if you use Markets. We verified all three on live stores on 23 September 2026. What it does not do is fill in your collection pages, stop your apps slowing the site down, or add the schema properties Google actually wants. In Malaysia there is one more thing nobody outside the market notices: Malay product terms sitting at keyword difficulty 0 with real volume behind them.
One warning on old advice: Shopify’s default robots.txt has changed. The version served by new stores in September 2026 no longer blocks /search or /policies/, opens with Allow: /, and carries an allowlist for Google’s ads crawler. Most Shopify SEO articles still describe the old file, so check yours before copying a fix from anywhere, including from us.
What Shopify decides for you, and what you decide
|
Area |
Who controls it |
What this means |
|
URL prefixes /products, /collections, /pages, /blogs |
Shopify, fixed |
You edit the handle after the prefix, nothing else |
|
Canonical tags |
Shopify, automatic |
Already correct on the duplicates people worry about |
|
sitemap.xml |
Shopify, automatic |
Generated and updated for you. Not editable |
|
robots.txt |
You, through robots.txt.liquid |
Powerful, unsupported, easy to break |
|
Titles and meta descriptions |
You, per resource |
Fully editable in admin |
|
Collection and product content |
You |
The biggest untapped area on most stores |
|
Structured data |
Theme, partly |
Product and Article ship. Breadcrumbs do not |
|
Site speed |
Shared |
Shopify serves the platform, you choose the apps |
URL structure and the duplicate content it creates
Shopify’s redirect documentation states the constraint directly: you cannot redirect URLs that use the fixed Shopify paths /products, /collections and /collections/all. The prefix is permanent. Only the handle after it is yours.
That produces the pattern every Shopify SEO article warns about: the same product sits at both /products/jam-tangan-kulit and /collections/lelaki/products/jam-tangan-kulit. Here is what we found testing it on live Shopify stores on 23 September 2026.
|
URL requested |
Canonical tag returned |
|
/products/mens-runner-nz-slip-on-mushroom |
/products/mens-runner-nz-slip-on-mushroom |
|
/collections/mens/products/mens-runner-nz-slip-on-mushroom |
/products/mens-runner-nz-slip-on-mushroom |
|
/products/mens-runner-nz-slip-on-mushroom?variant=27062780264528 |
/products/mens-runner-nz-slip-on-mushroom |
|
/collections/mens?page=2 |
/collections/mens?page=2 |
That overturns a lot of received advice. The duplicate product URL is already handled: Shopify points the canonical at the clean /products/ version by itself. You do not need an app, and you could not 301 it away anyway, because the redirect manager refuses fixed paths and refuses any URL returning a 200. Variant URLs are handled too.
Paginated collections behave differently. Page 2 self canonicalises instead of pointing back to page 1, which is correct under Google’s current pagination guidance and means page 2 is a real indexable page. So a collection running to nine pages of bare product grid puts eight thin pages in the index. Fix that by splitting the collection, not by fiddling with canonicals.
Two things are left. Link products consistently: if your menus and theme use the long /collections/…/products/… form, Google spends crawl budget on the version you told it to ignore. And set handles once. Changing one later gives you a checkbox to create a redirect, not an automatic one, and Shopify deletes that redirect if you later make a hidden resource visible again. Replatforming is a bigger job, covered in our SEO migration plan.
Collection pages: the biggest win on most Shopify stores
Collection pages carry the commercial keyword (“jam tangan lelaki”, “baju kurung moden”, “protein powder malaysia”) and on a default theme they hold a title, two lines of description and a grid. In Dawn, Shopify’s free theme, sections/main-collection-banner.liquid hard codes the H1 to the collection title, so you cannot type a separate H1 in admin. What you can do:
- Rename the collection so the title is the search term, not an internal label. “Men’s Watches” beats “New Arrivals SS26”.
- Use the description properly. It renders directly under the H1. Two hundred words on sizes, materials, delivery expectations and price bands, not marketing copy.
- Add sections below the grid. Dawn’s templates/collection.json holds only the banner and the grid, and it is an Online Store 2.0 JSON template, so you can add a buying guide, an FAQ block or related collection links after the grid in the theme editor. No app needed.
- Leave the filter and sort rules alone. The default robots.txt blocks /collections/*sort_by* and the encoded filter combinations, which are crawl traps.
One collection page built this way usually outranks the thin category pages of every competitor on page one, because almost nobody does it.
Product pages and thin variant pages
Product pages fail in Malaysia for a boring reason: the description is the supplier’s paragraph, repeated across four stores selling the same item.
- Write the title as a shopper would search it. Brand, product type, then the qualifier that matters locally: size, capacity, material, halal status.
- Replace supplier copy. If the same 80 words sit on Shopee, Lazada and three other Shopify stores, the page has nothing to rank on.
- Answer what your WhatsApp gets asked. Delivery time to East Malaysia, warranty, sizing, which plug it uses.
- Do not build a page per variant. Shopify keeps variants on one URL and strips ?variant= from the canonical. One product per colour splits your signals across near identical pages. Promote a variant to a product only when it has demand of its own, such as a model number people search for.
robots.txt.liquid: what you can and cannot change
Add a robots.txt.liquid file to your theme’s templates folder and you can override robots.txt. Shopify’s documentation lists four permitted operations: allow or disallow certain URLs, add crawl delay rules, add extra sitemap URLs, and block certain crawlers. It carries two warnings worth quoting: “This is an unsupported customization. Shopify Support can’t help with edits to the robots.txt.liquid file,” and “Incorrect use of the feature can result in loss of all traffic.”
Two traps. Uploading a theme from the admin’s Themes section does not import the file, so it vanishes on a theme upload. And it must be robots.txt.liquid, not a JSON template.
Work with the defaults, not around them. Shopify updates the default rules regularly, so loop robots.default_groups and add or remove one rule rather than replacing the file with hand written text:
{% for group in robots.default_groups %}
{{- group.user_agent }}
{%- for rule in group.rules -%}
{{ rule }}
{%- endfor -%}
{%- if group.user_agent.value == ‘*’ -%}
{{ ‘Disallow: /*?q=*’ }}
{%- endif -%}
{%- if group.sitemap != blank -%}
{{ group.sitemap }}
{%- endif -%}
{% endfor %}
To drop a default rule instead, wrap the output in an unless that tests rule.directive and rule.value.
Check your own file before acting on anyone’s advice. On 23 September 2026 we pulled robots.txt from six live Shopify stores. Four served a byte identical current default, which no longer blocks /search or /policies/, begins with Allow: /, includes an allowlist group for adsbot-google, and carries comment lines pointing at /agents.md and /.well-known/ucp for agent commerce. The two stores running customised templates still served the older rule set including Disallow: /search.
One of those files showed a bug we see often: a rule appended without a line break, so it ran onto the end of the previous rule and almost certainly does nothing. Every appended line needs its own newline, and the result belongs in Search Console’s robots.txt report before you trust it.
What you cannot do: edit sitemap.xml. Shopify builds it from your products, collections, pages and blog posts and gives you no controls. To keep something out of the index you have three documented options: a template level noindex in Liquid, the unlisted product status, or the seo.hidden metafield set to 1.
Speed, app bloat and Core Web Vitals
Core Web Vitals in 2026 are LCP, INP and CLS, measured at the 75th percentile of real user experiences. Good means LCP within 2.5 seconds, INP of 200 milliseconds or less, CLS of 0.1 or less. INP became a stable Core Web Vital on 12 March 2024 and FID was retired on 9 September 2024, so anything you read about FID is out of date.
Shopify’s speed report changed with it. It now reports Core Web Vitals field data, not a Lighthouse score out of 100: the three metrics at the 75th percentile, each ranked Good, Moderate or Poor, lagging up to 36 hours and holding only the last 90 days. If a proposal quotes you a “Shopify speed score”, it is working from a report Shopify no longer publishes.
What actually moves these numbers, from Shopify’s own performance guidance:
|
Lever |
What to do |
|
Apps |
“Remove any that aren’t essential for sales.” Every storefront app costs you INP |
|
Sections |
“Avoid adding too many sections to page templates.” Fifteen section homepages hurt LCP |
|
LCP image |
Never lazy load it. Give it fetchpriority=”high” |
|
Lazy loading |
loading=”lazy” only outside the initial viewport |
|
Images |
Use image_url and image_tag, not hand built CDN URLs |
|
Preload |
One or two late discovered resources. More competes with the browser |
|
Blocking scripts |
Defer app scripts that block parsing before anything renders |
The app audit does more than everything else on that list combined. Stores accumulate a reviews app, an upsell app, a pop-up, a currency converter, a WhatsApp widget and two abandoned trials, and each still injects script on every page. Uninstalling one does not always remove its theme code, so check theme.liquid for leftover snippets afterwards. Our guides to making a website faster and Core Web Vitals cover measurement.
Structured data: what your theme already outputs
We scanned every Liquid file in Dawn on 23 September 2026. Four output JSON-LD: main-product.liquid and featured-product.liquid emit Product through the structured_data filter, main-article.liquid emits Article, and header.liquid emits Organization plus WebSite and SearchAction on the homepage only.
That filter outputs a Product when an item has no variants and a ProductGroup when it has one or more, carrying name, description, image, brand, category, url and an offers block with price, priceCurrency and availability.
Google requires, for merchant listing rich results, name, image and offers, and within the offer either price or priceSpecification.price plus priceCurrency in three letter ISO 4217 format, with a price greater than zero. So Shopify’s default output clears Google’s required bar. What it omits is the recommended set that actually wins the rich result: sku or gtin, aggregateRating and review. There is no BreadcrumbList in Dawn, or in Shopify’s newer Horizon theme family. That leaves four jobs:
- Add BreadcrumbList yourself. Neither free theme ships it.
- Add gtin or sku, the identifiers Google uses to match your listing to the same product elsewhere.
- Watch for duplicate Product blocks. Shopify’s filter emits no rating, so review apps inject a second node, and two on one page is a common reason a rich result disappears.
- Check priceCurrency reads MYR.
Blog and content structure inside Shopify
Shopify blog URLs are /blogs/<blog-handle>/<article-handle>, you can run more than one blog, and titles and meta descriptions are editable per article.
The recurring worry is tag URLs at /blogs/news/tagged/<tag>, which plenty of articles call an index bloat disaster. What we found on 23 September 2026: tag URLs for tags that do not exist return a hard 404; they do not appear in the generated blog sitemaps, checked on two stores with 293 and 82 entries; neither Dawn nor Horizon links to them anywhere across 88 and 283 Liquid files; and the default robots.txt blocks only the multi tag form /blogs/*+*. So single tag blog URLs are crawlable but orphaned, which makes them a problem only if a theme or app starts linking them. Audit your internal links rather than panicking about the URLs.
For a Malaysian store the blog earns its place by answering what keeps people from buying: sizing, delivery to Sabah and Sarawak, halal certification, care instructions, what to buy for Raya. Each post should link to the collection it supports, as in our guide to blogging for SEO.
Ranking in Malaysia specifically
Ringgit in your structured data
Google requires priceCurrency in three letter ISO 4217 format, so MYR. Shopify pulls it from your store currency, so it is correct by default on a store set up as a Malaysian business.
Where it goes wrong: stores that started in USD “to look international” and never switched, and stores running a currency converter app that changes the displayed price in JavaScript while the markup keeps the original figure. Google reads the markup, and a price mismatch is a documented reason to drop a merchant listing.
One Malaysian constraint matters here. Shopify documents that selling in local currencies is supported only by Adyen or Shopify Payments, and that orders through other providers are processed in your store’s default currency. Shopify Payments is not available in Malaysia, so unless you are on Adyen your store has one currency and your schema should carry MYR everywhere. That is simpler for SEO, not harder. The cost effect is in our guide to Shopify in Malaysia.
Malay search terms: target the nouns, not the site
This is where most Malaysian stores waste effort. The instinct is to translate the whole site into Malay. The data says something narrower: generic Malay shopping phrases are weak, while Malay product nouns are wide open. Ahrefs Malaysia, checked 23 September 2026.
|
Search term |
Monthly searches (MY) |
Keyword difficulty |
Traffic potential |
|
jam tangan lelaki |
2,100 |
0 |
600 |
|
dropship malaysia |
1,700 |
2 |
1,700 |
|
kasut lelaki |
700 |
0 |
500 |
|
beg tangan wanita |
450 |
0 |
50 |
|
baju raya online |
80 |
53 |
4,800 |
|
tudung online |
10 |
43 |
7,400 |
|
kedai online |
50 |
31 |
10 |
|
online shopping malaysia |
500 |
55 |
135,000 |
Read the pattern. “Jam tangan lelaki”, “kasut lelaki” and “beg tangan wanita” carry real volume at difficulty 0, meaning nothing on those results has earned its position. Meanwhile “kedai online” gets 50 searches a month, and “online shopping malaysia” has a difficulty of 55 with Lazada as its parent topic, so chasing it burns a quarter for nothing.
What to do with that:
- Put the Malay product noun in the collection page where Malaysians actually use it. A watch retailer needs a collection ranking for “jam tangan lelaki”, not a Malay translation of its About page.
- Mixed language pages are normal in Malaysian results. “Jam Tangan Lelaki” as an H1 with English body copy is how many ranking Malaysian ecommerce pages read.
- Note the traffic potential on “tudung online” and “baju raya online” against their low exact volume. Those are cluster entry points, earning most of their traffic from long tail variants underneath.
- Watch the calendar. “Baju raya online” is seasonal, so index that collection three months before Ramadan, not the week before.
Google Business Profile, only if you have a shop
Google’s rule is short: you qualify if your business has a physical location customers can visit, or travels to customers. An online only store meets neither test and should not create a profile. If you have a showroom or pickup counter, our Google Business Profile checklist and local SEO page cover it.
English and Malay on one store
Shopify Markets handles the technical side if you do want a Malay version: up to 20 languages on Basic, Grow and Advanced and 30 on Plus, served from subfolders such as /ms, with hreflang injected automatically through content_for_header, self referencing canonicals per market and an x-default. We counted 86 hreflang alternates on one live multi market store.
Two limits to plan around: tags cannot be translated, and URL handles stay in the original language, so a Malay page sits at /ms/products/mens-leather-watch. Given the difficulty numbers above, most Malaysian stores do better writing a few strong Malay collection and blog pages on the main store than running a full second language.
A 90 day priority order
|
Days |
Work |
Why here |
|
1 to 10 |
Compare your live robots.txt to the current Shopify default and fix anything blocked that should not be |
A broken robots.txt caps everything else |
|
1 to 10 |
Confirm priceCurrency reads MYR and there is one Product block per page |
Either fault kills rich results |
|
10 to 25 |
App audit, clear leftover theme.liquid snippets, re-check the speed report |
Biggest speed lever, and free |
|
15 to 40 |
Rewrite the top 10 collection pages: real titles, 150 to 250 word descriptions, sections below the grid |
Where the commercial keywords live |
|
25 to 50 |
Map Malay product nouns to collections and create the two or three the data justifies |
Difficulty 0 terms, no competition |
|
40 to 60 |
Rewrite the top 30 product pages. Kill supplier copy, merge variant duplicates |
Removes thin content behind the collections |
|
50 to 70 |
Add BreadcrumbList and sku or gtin to the theme schema |
Rich result upgrade once pages deserve it |
|
60 to 90 |
Internal linking pass, and split collections running past three pages |
Consolidates what the first 60 days built |
Frequently asked questions
Is Shopify bad for SEO?
No. On the complaints people repeat, Shopify already does the right thing: canonicals, stripped variant parameters, automatic sitemaps and automatic hreflang. Its real limits are the fixed URL prefixes and the thin default collection pages, and only the second matters.
Can I remove /collections/ or /products/ from my Shopify URLs?
No. Shopify lists those as fixed paths that cannot be redirected. You can edit the handle after the prefix and nothing more, and it carries no measurable ranking cost.
Do I need a Shopify SEO app?
Not for the basics. Titles, meta descriptions, handles, redirects and sitemaps are built in. An app earns its place for bulk editing at scale or for schema your theme omits, weighed against the speed cost of another script on every page.
Should I block AI crawlers in robots.txt.liquid?
Shopify’s current default points crawlers at agent commerce endpoints rather than blocking them. The trade off is covered in AI SEO for Shopify stores.
My collection has nine pages of products. Is that a problem?
Usually yes, because each paginated page self canonicalises and counts as its own thin page. Split it into tighter collections people search for.
Why did my product rich result disappear after installing a reviews app?
Most likely two Product blocks on one page. Shopify’s filter emits Product or ProductGroup without a rating, so review apps add their own node. Test the URL in Google’s Rich Results Test and remove one.
Should I translate my whole store into Malay?
For most stores, no. Generic Malay shopping phrases are low volume and competitive while Malay product nouns sit at difficulty 0. Write those pages first, then measure.
How long does Shopify SEO take in Malaysia?
Collection and product rewrites on an established store usually show movement in 8 to 12 weeks. A store with no history should plan on 6 months before organic search is a real channel.
The bottom line
Shopify’s SEO reputation is stuck in about 2019. The platform now handles canonicals, sitemaps and hreflang correctly, and its default robots.txt has moved on from what most guides describe. What matters is the work Shopify cannot do for you: collection pages with real content, product pages that are not supplier copy, an app list that is not strangling your INP, and schema carrying breadcrumbs, identifiers and MYR. In Malaysia, add the Malay product nouns sitting at difficulty 0 that nobody has claimed. That opportunity closes as soon as someone else notices it.
If you want that done rather than described, talk to MediaPlus Digital about ecommerce SEO, request a quote, and claim a free store audit worth RM300. If the store needs rebuilding first, that is our Shopify website design and development team.





