SEO and discoverability

Every published page tells search engines and AI assistants what it is, who wrote it and where its sources are, and the site publishes the files crawlers look for. Authors get a Checks tab that scores each draft for being found and quoted.

What it is

The publish pipeline writes these with every published page. Nothing has to be switched on. The home page gets the head tags and the site's structured data too, without overwriting the ones it sets itself (see Build your home page).

PartWhat it gives
Head tagsTitle, meta description, canonical URL, Open Graph and Twitter card tags, and hreflang links when the page exists in more than one language.
Structured dataschema.org JSON-LD: the site owner (Organization or Person), the WebSite with a search action, a breadcrumb trail, and the page itself as an Article or the type its content type is mapped to. FAQ, How-to and Definition blocks in the body become their own nodes, and so do the rows of an FAQ custom field.
Authors and sourcesA byline with the author's box, a Sources list at the end, both sent to search engines as the author and citations.
Markdown twinsThe same page as Markdown, at the same address with .md (/articles/my-post.md), linked from the HTML head. AI tools read it at a fraction of the tokens.
Related itemsUp to four related pages at the end of each page, chosen from the search index by title and tags. Needs Search.
Checks tabA score out of 100 for each draft, with the one thing to fix per failing check. Advice only: it never blocks publishing.

The seo feature adds the site-wide files and the settings for them:

FileWhat it holds
/sitemap.xmlThe home page, each content type's list page, every published page in every language it exists in (with hreflang alternates), and every static page (Arc CMS's privacy and cookie policies and the app's own, see Write static pages). lastmod is the updated date, else the publish date.
/robots.txtEverything allowed except /_site/ (the site's source files: templates, header, page sources), a Disallow: / group for each AI crawler switched off, and the sitemap address.
/llms.txtA Markdown index of the site (llmstxt.org): the site name and description, then a section per content type linking its pages' Markdown twins in the default language, newest first, up to 500 pages in all, then links to the home page and the sitemap.
/llms-full.txtEvery default-language Markdown twin in one file, newest first, up to 2 MB.
/{type}/feed.xmlAn RSS 2.0 feed per content type with its 20 newest items.
/{key}.txtThe IndexNow key file, which proves the site to the engines.

After each publish, IndexNow tells Bing (which feeds ChatGPT search and Copilot), Yandex, Seznam and Naver which pages changed. Google does not take part; the sitemap covers it. For a website walkthrough, see Get found by search engines and AI.

Turn it on or off

The site-wide files are the switchable feature seo. It is on by default and needs no other feature. To turn it off, list it in src/custom/features.ts:

export const CUSTOM_FEATURES: FeatureChoice = {
    off: ['seo'],
};

When it is off, Admin, Settings, Discoverability goes, the regenerateSeoFiles function goes at the next deploy of the whole functions codebase, and neither publishing nor npm run seed writes the sitemap, robots.txt, llms.txt, llms-full.txt, feeds or IndexNow key, and nothing is pinged. Files an earlier publish or seed wrote stay on Hosting until the next website deploy: with seo off it leaves them out of the release, which removes them. So after turning it off, deploy the website once (npm run deploy -- --only hosting --project <alias>), then check that /robots.txt and /sitemap.xml no longer show the old files. Head tags, structured data, Markdown twins, authors, sources and the Checks tab are part of Content and stay. See Feature ids.

Set it up

  1. Say who owns the site. In Admin, Settings, About, fill in the site name, the production URL, whether the site is an organization or a person, the logo, a description, the Social and profile links (one URL per line), a contact email and a public phone. They become the Organization or Person node on every page (the phone as telephone, the links as sameAs), and the same details can be printed on the site itself (see Site info), and the production URL is the base of every absolute link in the files above. Empty fields are left out, never guessed.

  2. Add authors. Content, Authors. Mark one as the default and new content gets that author. See Authors and tags.

  3. Map content types (optional). Add or edit a content type and choose its Structured data type: Article (the default), Blog post, News article, Web page, Product, Service, Event, Person, Recipe or How-to guide. Web page suits pages that are not articles, such as About or a policy; the standard pages use it. Map the type's properties to your custom fields; anything unmapped is left out.

  4. Choose the crawler policy. Admin, Settings, Discoverability. Every crawler is allowed until you switch it off. Choose whether to publish /llms.txt and /llms-full.txt and whether to use IndexNow (both on by default). Press Save and apply to site to write the files now, or Save to let the next publish write them.

  5. Verify from the outside. Arc CMS keeps no crawler log, so register the site in Google Search Console and Bing Webmaster Tools (which also shows IndexNow submissions).

The crawlers on the settings page come in two groups:

GroupCrawlersSwitching one off
Search and answer botsOAI-SearchBot, ChatGPT-User, Claude-SearchBot, Claude-User, PerplexityBot, Perplexity-User, Bingbot, Googlebot, DuckAssistBotRemoves the site from those answers and results.
Training botsGPTBot, ClaudeBot, Google-Extended, Applebot-Extended, CCBot, meta-externalagent, Bytespider, AmazonbotA policy choice. It does not change whether assistants cite the site.

Use it

When the files change

Each publish or unpublish of a content type with public pages rewrites the sitemap, the feeds, robots.txt, llms.txt and (with IndexNow on) the IndexNow key file, and releases them to Hosting together with the pages. IndexNow is pinged only after that release succeeds. Save and apply to site rewrites robots.txt, llms.txt and the key file at once, without a publish.

In the editor

WhereWhat
Basic tabAuthor: the byline and the schema.org author. Cover image: the social and search thumbnail.
SEO tabSEO title, meta description, Sources (the pages this piece draws on), Mark as updated today, which sets the "Last updated" date shown on the page and sent as the modified date, and Canonical URL, empty unless the original is on another page. An empty canonical, and one on a local address such as localhost, is published as the page's own address on the Production URL.
Body toolbarThe block menu inserts an FAQ, Key takeaways, How-to steps or Definition block; typing / offers them too. Each becomes structured data. Key takeaways become the page's abstract.
Checks tabThe score, each check, and pages on this site the body does not link to yet, each with Insert link.

A content type with an FAQ custom field (see Field types reference) adds a FAQPage node from the rows of its first FAQ field that have both a question and an answer. Since 2023 Google shows FAQ results only for some government and health sites, so do not expect them in search; the node is still valid, and AI assistants read it.

What the Checks tab scores

High checks weigh 3, medium 2, low 1. The score is the weight passed out of the weight checked. It reads the default language.

CheckPasses whenWeight
Answer firstThe first paragraph has 15 to 70 words.High
Question headingAt least one h2, h3 or h4 ends with a question mark.High
BlocksThe body has an FAQ or Key takeaways block.High
Title lengthThe title has 1 to 70 characters.Medium
Meta description50 to 160 characters.Medium
List or tableThe body has one.Medium
Internal linksThree or more links to this site.Medium
AuthorAn author is picked.Medium
SourcesAt least one source with an http or https address.Medium
Length300 words or more.Low
Cover imageOne is set.Low
TagsTwo or more.Low
FreshnessPublished or marked updated in the last 12 months.Low

Write for Google and AI assistants

Assistants quote passages, not pages. A passage gets quoted when it answers a question on its own, sits under a heading that matches how people ask, holds something checkable, and comes from a page with a named author, a date and sources. Google rewards the same things. Share this with the people who write:

  1. Answer first. The first paragraph under the title, and under each heading, answers in 15 to 70 words. Detail comes after.
  2. Write headings as questions. "How much does it cost in India?" beats "Pricing".
  3. Use facts, not adjectives. "Supports 12 languages including Hindi and Tamil" can be quoted; "world-class support" cannot.
  4. Give it structure. A list for steps, a table for comparisons, an FAQ block for the questions you keep getting.
  5. Say who wrote it and when. Pick an author. Press Mark as updated today only after a real revision, never for a typo fix.
  6. Cite sources. Add the pages you drew on under Sources.
  7. Link to your own pages. Three or more; use the suggestions in the Checks tab.

Do not stuff keywords, rewrite what is already everywhere on the web, mark a page updated without changing it, or write an FAQ of questions nobody asks. Aim for a score above 80, then publish. A day later, test the page with Google's Rich Results Test, and ask an assistant your heading's question to see whether it quotes you. If it does not, sharpen the opening paragraph.

AI assistant visits

When Google Analytics is connected, the dashboard counts visits referred by AI assistants such as ChatGPT and Perplexity. See Analytics.

Extend it

An app extends discoverability through its own content and templates:

New schema.org types, content blocks, crawlers, AI referrers and checks are changes to Arc CMS itself, not to an app (see Keep core generic). Types, blocks and crawlers live in a functions file with a browser copy; change both, since a static page and its in-app twin must agree (a test compares the two lists for types and crawlers):

To addChange
A schema.org typefunctions/src/shared/schema-types.ts and its copy src/shared/constants/schema-types.ts; build the node in functions/src/shared/schema-mapping.ts and src/shared/utils/schema-mapping.ts. The type editor lists it by itself.
A content blockARC_BLOCKS in src/shared/components/tiptap-editor/service/arc-block-extension.ts; read it in functions/src/shared/content-blocks.ts and src/shared/utils/content-blocks.ts; style it in the default template.
A crawlerCRAWLERS in functions/src/shared/crawlers.ts and src/shared/constants/crawlers.ts, in the search or training group. Existing sites get it allowed.
An AI referrerAI_REFERRERS in functions/src/shared/ai-referrers.ts.
A checkA rule() in src/shared/utils/discoverability-checklist.ts, and the keys admin.contents.checklist.{id} and admin.contents.checklist.{id}_fix in every admin translation file, then npm run i18n:keys.

The pipeline never invents a value: a property that cannot be filled truthfully is left out. Keep new types and blocks that way.

What it stores

WhereWhat
Settings/discoverabilityThe default author id, the crawler policy (allowed or not, by crawler id), whether llms.txt is on, and IndexNow on or off with its key (made on first use).
Settings/aboutThe site's identity for the Organization or Person node.
AuthorsName, bio, photo, job title and profile links. Nothing private.
Each content documentauthorId, references, updatedOn, seoTitle, metaDescription; the content type's schema mapping.
Firebase HostingThe files in What it is and each page's .md twin, written by the functions. Nothing is kept in Storage.

Cloud Functions: the regenerateSeoFiles callable (the seo feature). The rest runs inside the publish queue. See Cloud Functions.

Who can do what

ActionWho
Read and change Discoverability settings, run regenerateSeoFilesAdmins.
Change About settings, add and edit authorsAdmins.
Read authors and AboutAnyone: the same details are printed on every public page.
Set author, sources and dates on content; see the Checks tabAdmins: the content screens are admin only (see Users and roles).
Read the sitemap, robots.txt, llms.txt, feeds and twinsAnyone: they are public files on Hosting.

Troubleshooting

Settings changed but robots.txt did not. Save applies on the next publish; the functions also cache the settings for five minutes. Press Save and apply to site.

Pages and files vanish after a plain firebase deploy --only hosting, which releases only what the CLI uploaded. npm run deploy keeps every file the functions wrote. If they did vanish, rebuild them with npm run seed:dev (or npm run seed:prod). See Deploy.

SymptomFix
Absolute links point at the wrong hostWithout a production URL in Admin, Settings, About, the Hosting default address is used. Set it, then publish.
IndexNow sends nothingCheck it is on in Discoverability. Each submission, and each refusal with its status, is in the function log under "IndexNow". A 4xx means the key file is not served at the production URL: press Save and apply to site.
A structured data property is missingIts source field is empty or unmapped. Nothing is invented.
No Related items or link suggestionsSearch is off, or the index is empty: rebuild it in Admin, Settings, Search.
A page is missing from llms.txtIt has no URL slug or title, its type has no public pages, or the 500-page limit was reached before it.