Content and SEO
Per-page titles and descriptions, redirects that keep an old address working, and posts published on your store with an RSS feed.
Written 2026-09-16, updated 2026-09-29. Three things that decide how a store is found and how much of it there is to find: per-page SEO (a title, description, canonical, share image and noindex on one product, collection, page or post), redirects (so an address you change keeps working), and posts (your own writing, published on your store with a feed). Sections 6 to 8 say what your store writes for search engines on its own: structured data, the sitemap and robots.txt, and the proof a search engine asks for. Section 9 is the store's own health check.
Everything here is live data: a change is on the storefront as soon as it is saved. None of it goes through the draft-and-publish loop the theme uses (agents.md).
1. Per-page SEO
Your store already writes a title and description for every page. An override replaces one of those tags on one page and leaves the rest alone.
An override is addressed by two things:
| Part | What it is |
|---|---|
ownerType | product, collection, page or post |
ownerId | that object's public handle: the product slug, the collection handle, the page slug, the post slug |
Handles, not internal ids: the address the shopper sees is the address you write SEO for. If you rename the handle, the override moves with it (§2).
| Field | Limit | What it does |
|---|---|---|
title | 60 | Replaces <title> and the og:title |
metaDescription | 160 | Replaces the meta description and the og:description |
canonical | https URL | Replaces <link rel="canonical"> and the og:url |
ogImageUrl | https URL | Adds an og:image, the picture that shows when the page is shared |
noindex | true / false | Adds noindex,nofollow for this page only |
An empty field keeps the generated tag. Set only what you want to change. An override whose fields are all empty is removed, and the page goes back to what your store writes for it. noindex can hide one page; it can never un-hide a store you hid store-wide.
Dashboard. Growth → SEO, under the store-wide settings: pick a page, type, save.
Routes. GET /api/seo?ownerType=&ownerId= reads one override, PUT with the same query saves it, DELETE removes it, and GET /api/seo?list=1 lists every override the store has. Without ownerType the route is the store-wide setting it has always been (storefront:read / storefront:write).
Tools. get_seo { ownerType?, ownerId? }, set_seo { ownerType?, ownerId?, title?, metaDescription?, canonical?, ogImageUrl?, noindex? } and list_seo_overrides.
{ "ownerType": "product", "ownerId": "blue-mug",
"title": "Blue mug, hand thrown", "metaDescription": "A 300 ml stoneware mug, thrown and glazed in our studio.", "noindex": false }
What your store writes by default
| Tag | Rule |
|---|---|
| Title | <name> · <store name> in at most 60 characters, cut at a word. When the store name leaves no room, the name stands alone. The home page uses your store-wide title, else your store name and its description when both fit. |
| Description | Plain text from the thing's own words, at most 160 characters, cut at a word. On the home page, collections, pages, posts and the blog list, text under 70 characters is followed by a short sentence built from real names (the page, your store, and on a list the things it lists) when that still fits. A product's own short text is used as written. |
| Canonical | Your primary address, https, with no tracking parameters. A paged list keeps ?page=N. A filtered or sorted list points to the plain list. |
| Share image | Your override, else the page's own first picture, else your home page picture, else your logo. With none, the page carries no image tag. |
| Language | lang="en". A store has one language, so pages carry no hreflang. |
Never indexed. These pages say noindex: the cart, checkout, account pages, order tracking, search results, previews, the editor, not found pages, filtered or sorted lists, and page 2 and later of a collection. Page 2 of the blog list stays indexable.
One address per page. Your free store address, a www. variant and every other domain of your store answer one permanent redirect to the same page on your primary address, path and query kept.
2. Redirects
A redirect sends one address to another. Two things write them:
- Automatically. Change a product slug, a collection handle, a content page slug or a post slug and a 301 from the old address to the new one is written for you, and that object's SEO override moves across. Nothing you had linked to breaks.
- By hand. For a page you removed, a campaign address you want to keep, or a move from a shop you came from.
| Rule | |
|---|---|
fromPath | a store path: starts with /, no spaces |
toPath | a store path, or an https URL to send people off-site |
status | 301 (permanent, the default) or 302 (temporary) |
| Loops | a redirect that would send a shopper back where they started is refused |
| Limit | 500 a store |
Trailing slashes and query strings never distinguish two redirects: /old-page/, /old-page and /old-page?utm_source=x all match the same row, and a store-path target keeps the query string it arrived with. Only the proof files of a search engine (§8) answer before redirects; after them the storefront checks redirects before anything else it serves, so a redirect can also shadow a page that still exists. Each row counts how many times it has been followed.
What the storefront answers on its own:
| You ask for | Your store answers |
|---|---|
| An address renamed once or several times | one 301 to the newest address |
| A redirect that leads to another redirect | one 301 to the end of the chain (302 if any step is temporary) |
/Products/Blue-Mug, //products//blue-mug, /products/blue-mug/ | one 301 to /products/blue-mug, query kept |
/pages/Shipping | 404: a content page's handle keeps its case (/Pages/shipping still folds) |
| A product, collection or page you deleted in the last 90 days | 410 (gone) |
| A deleted post, a draft, an archived or hidden product or collection, an unknown address | 404 |
When you rename something, redirects that pointed at the old address, including ones you wrote, now point at the new one; their status and hit count are kept. Renaming back to an earlier address removes the automatic redirect away from it. A redirect you wrote away from that address is kept.
Media, feeds, sitemaps, account pages, order tracking and download links are never respelled.
Dashboard. Online store → Pages, under the page list.
Routes. GET /api/redirects, PUT /api/redirects with { fromPath, toPath, status? }, DELETE /api/redirects?id= or ?fromPath= (storefront:read / storefront:write).
Tools. list_redirects, set_redirect { fromPath, toPath, status? }, delete_redirect { id | fromPath }.
3. Posts
A post is a piece of writing that lives on your store. Same Markdown as content pages, same theme, same header and footer.
| Field | Limit |
|---|---|
slug | lowercase words joined by hyphens, 100 |
title | 160 |
excerpt | 300; what the list, the feed and the description show |
bodyMarkdown | 50,000 |
coverImageUrl | an https URL, usually one your store already holds |
author | 80 |
tags | 10 tags, 40 characters each |
status | draft or published |
Only a published post is visible. Publishing stamps publishedAt once: unpublishing and republishing keeps the original date, so a post does not jump to the top of the feed because it was edited. Up to 500 posts a store.
Where they appear:
| Address | What it is |
|---|---|
/blog | the list, newest first, 12 a page (?page=2) |
/blog/<slug> | the post, with Article structured data |
/blog/feed.xml | RSS 2.0, the 50 newest posts |
/sitemap.xml | /blog and every published post |
Dashboard. Online store → Pages: the post list, a Markdown editor, a cover upload and a Published switch. Add /blog to your header or footer from the same screen to give readers a way in.
Routes. GET /api/posts (all, drafts included), GET /api/posts?id= or ?slug=, PUT /api/posts, DELETE /api/posts?id= (storefront:read / storefront:write).
Tools. list_posts, upsert_post, set_post_image { postId, url | dataBase64 + contentType } and delete_post.
4. Reading posts from your own front end
The storefront read API is public, cached and CORS-open, like the rest of it (headless.md):
| Endpoint | Answers |
|---|---|
GET /api/storefront/posts | { posts: [{ slug, title, excerpt, coverImageUrl, author, tags, publishedAt, updatedAt, url }] } |
GET /api/storefront/posts/<slug> | { post: { …, bodyHtml } }, the Markdown already rendered |
Drafts are never returned: an unpublished slug is a 404, the same as an unknown one.
5. Events
Every change here is in the store's event log (events.md): post.created / post.updated / post.deleted, redirect.created / redirect.deleted (the resource id is the redirect id) and seo_override.created / seo_override.updated / seo_override.deleted, whose resource id is <ownerType>:<handle>. Subscribe to post.* to rebuild a cache when writing is published, or to redirect.* to mirror moves into a front end of your own.
6. Structured data
Every indexable page carries one block of structured data that search engines read for rich results:
| Page | What is written |
|---|---|
| Home | Organization (name, logo, contact details, address and social links you entered) and WebSite with a search box that opens your store's /search |
| Product | Product with its offers: the price in the product's currency with that currency's own decimals (two for dollars, none for yen), availability, every image, SKU and barcode when you entered them, the brand, and a breadcrumb |
| Collection | a breadcrumb and an item list of the products on that page, numbered on across pages; a filtered or sorted list carries the breadcrumb only |
| Page | a breadcrumb, and an FAQ when a section on the page holds questions you saved |
| Post | Article and a breadcrumb |
| Blog list | a breadcrumb |
- Brand is the product's vendor, else your store's name.
- Availability is in stock, out of stock, or back order when the product takes orders past zero. A gift card is always in stock.
- Variants at different prices make a price range of up to 50 offers.
- Breadcrumb of a product: Home, the first collection that lists it, the product.
- FAQ also appears on the home page and product pages when a section placed there holds saved questions. Sample questions a section shows before you edit it do not count.
Cart, search and not found pages carry none. Ratings and reviews are not written today. The Reviews app loads its reviews in the shopper's browser after the page arrives, so the page itself has no reviews to describe, and your store never writes a rating it cannot show.
7. Sitemap and robots.txt
/sitemap.xml lists the addresses search engines should index, all on your primary address: the home page, /collections/all, active products, visible collections, published pages and posts, and /blog once a post is published. Drafts, archived products, hidden collections and anything whose override says noindex or names another canonical address are left out.
- Up to 1,000 addresses it is one file. Beyond that
/sitemap.xmlis an index of parts:/sitemap-pages-1.xml(home,/collections/all, pages and/blog),/sitemap-collections-1.xml,/sitemap-products-1.xmland/sitemap-posts-1.xml, then-2and on, 1,000 addresses each. - **
lastmod** is when the thing last changed. For a product that is the time its content was last saved (created, saved, or archived or activated in bulk), or its latest recorded change in the last 90 days if that is later. A sale changes stock, not content: it is a recorded change, not a save. - Images: each product lists up to 20 of its pictures.
What a closed or hidden store answers:
| Your store | robots.txt | Sitemap |
|---|---|---|
| Open | the lines below | every address |
| Hidden from search | Disallow: / for every crawler | empty |
| Coming soon | Disallow: / for every crawler | empty |
| Password | Disallow: / for every crawler | the home address only |
Every sitemap file of a closed or hidden store carries a noindex header, and every part is empty. An open store's robots.txt:
User-agent: *
Allow: /
Disallow: /cart
Disallow: /checkout
Disallow: /account
Disallow: /search
Disallow: /orders/
Disallow: /email/unsubscribe
Disallow: /newsletter/
Disallow: /downloads/
Disallow: /l/
Disallow: /__fh/
Disallow: /api/storefront/orders
Disallow: /api/storefront/account
Disallow: /*?fh_edit=
Disallow: /*&fh_edit=
Disallow: /*?fh_preview=
Disallow: /*&fh_preview=
Disallow: /*?sort=
Disallow: /*&sort=
Disallow: /*?tag=
Disallow: /*&tag=
Disallow: /*?vendor=
Disallow: /*&vendor=
Disallow: /*?type=
Disallow: /*&type=
Disallow: /*?availability=
Disallow: /*&availability=
Disallow: /*?price
Disallow: /*&price
Disallow: /*?opt.
Disallow: /*&opt.
User-agent: GPTBot
User-agent: OAI-SearchBot
User-agent: ChatGPT-User
User-agent: ClaudeBot
User-agent: anthropic-ai
User-agent: PerplexityBot
User-agent: Google-Extended
User-agent: Applebot-Extended
User-agent: CCBot
User-agent: Bytespider
Allow: /
Disallow: /cart
Disallow: /checkout
Disallow: /account
Disallow: /search
Disallow: /orders/
Disallow: /email/unsubscribe
Disallow: /newsletter/
Disallow: /downloads/
Disallow: /l/
Disallow: /__fh/
Disallow: /api/storefront/orders
Disallow: /api/storefront/account
Disallow: /*?fh_edit=
Disallow: /*&fh_edit=
Disallow: /*?fh_preview=
Disallow: /*&fh_preview=
Disallow: /*?sort=
Disallow: /*&sort=
Disallow: /*?tag=
Disallow: /*&tag=
Disallow: /*?vendor=
Disallow: /*&vendor=
Disallow: /*?type=
Disallow: /*&type=
Disallow: /*?availability=
Disallow: /*&availability=
Disallow: /*?price
Disallow: /*&price
Disallow: /*?opt.
Disallow: /*&opt.
Sitemap: https://shop.example.com/sitemap.xml
shop.example.com stands for your primary address.
AI assistants
Let AI assistants read my store (Search & sharing, on by default) decides whether assistants such as ChatGPT and Claude may read your pages, quote them and link to them. On, robots.txt gets one more group naming the AI crawlers (GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, anthropic-ai, PerplexityBot, Google-Extended, Applebot-Extended, CCBot, Bytespider) with the same rules as everyone: your pages open, your cart, checkout, account and filtered views closed. The rules are repeated because a crawler named in its own group follows only that group. Off, that group says Disallow: / and /llms.txt answers "not found". Search engines are not affected either way: Googlebot and Bingbot keep the rules above. Google-Extended and Applebot-Extended do not fetch pages; they tell Google and Apple whether pages their search crawlers read may be used for their AI answers.
It is one switch on purpose: there is no separate setting for assistants that learn from pages and assistants that answer with them. A hidden or closed store refuses every crawler already. Agents change it with set_seo { aiAssistants: false }.
8. Search engines
When you connect a search engine, it asks for proof that the store is yours. Your store answers the proof file on your primary address, also while the store is closed, before any redirect. The ownership tag goes on your home page only, and never on the editor's or a preview's copy of it. Connecting is described in Search engines.
9. Health check
Your store checks itself for search problems and says how to fix each one. Nothing changes until you fix it.
| Check | Severity |
|---|---|
| The store is closed, or hidden from search | high |
| A page has no title | high |
| Redirects send visitors in a circle | high |
| A page has no search description | medium |
| The same title or description on several pages | medium |
| A page has no main heading | medium |
| A product has no image | medium |
| A link goes to a page that does not exist | medium |
| A redirect leads to another redirect | medium |
| A title over 60 or under 15 characters | low |
| A description over 160 or under 70 characters | low |
| A page has several main headings | low |
| A product image has no description | low |
| A product description under 50 words | low |
| A product image over 500 KB | low |
| A link goes through a redirect | low |
Links are checked in your pages, posts and menus. Up to 60 pages are checked at a time, and a report lists up to 200 findings, the most serious first. A closed store is checked on its data only; its pages are checked once it is open.
Dashboard. Online Store → Search & sharing → Health: each finding has a Fix link to the screen that fixes it.
Route. GET /api/seo-audit (storefront:read).
Tool. get_seo_audit { limit?, severity? }: limit from 1 to 200 (default 50), severity high, medium or low. Read-only.
Insights. For what to do next, get_seo_insights and the dashboard's Online Store → SEO insights screen propose topics to write about, titles to improve, pages that compete for the same search, pages to remove and whether assistants can read the store, each with its evidence. Titles and pages to remove need a connected search engine. Both only propose: you apply a step with set_seo, set_redirect, upsert_post or set_content_pages.
See also
- agents.md: the twelve tools here, their scopes, and what still needs a publish.
- analytics.md: your store's search performance, and how it shows which titles to rewrite.
- product-kinds.md: a digital or service product's page is indexed like any other.
- headless.md: reading posts, and following the redirect table, from a front end of your own.