SEO Toolkit
The SEO toolkit helps your pages rank and share well. Set metadata per page, and Aglyn emits the right tags, sitemap, and structured data automatically.
Free for core SEO fields; richer structured data ships for published sites.
Per-page SEO
Every page's detail view has an SEO card with four fields:
- Search title — up to 60 characters, published exactly as you type it.
- Search description — up to 155 characters, the meta description.
- Breadcrumb label — up to 40 characters, the page's short name in a breadcrumb trail.
- Social image — the picture shown when the page is shared. Press Choose image to pick one from your media library (or the organization's shared library); Clear puts the site default back.
Fill them in and press Save SEO — it saves independently of the canvas, so you can update metadata without touching the design. The published site emits these into the page head, deduping descriptions so you never get conflicting tags.
The social image is a media pick, not a URL you type. That is deliberate: a picked asset is stored by identity, so moving it into a folder or replacing it with a new version keeps every card that references it working.
Anything you leave blank falls back sensibly: the description falls back to the page's own description and then the site's, and the social image to the site-wide one.
How a page title is built
A search title you write is the whole title — nothing is appended to it. That is what lets you keep every page inside the ~60 characters a search result shows, and say the brand once rather than twice.
A page with no search title of its own is titled from its name — the page's display name, a blog post's headline, a collection's name — joined to the site Title with your Separator:
| Page | Rendered title |
|---|---|
Search title About Aglyn — one platform for the open web | About Aglyn — one platform for the open web |
Page named Contact, no search title | Contact – Acme Widgets |
| Neither | Acme Widgets |
Your brand still travels with every share regardless: og:site_name is published on
every page, from your site Title, then your Entity name, then the site's own
name — so a site that has filled in any one of the three is named on every card.
Variables, so a title is not a copy of your site name
A title can name the things it depends on instead of repeating them:
| Variable | What it stands for |
|---|---|
{{page.name}} | What this page is called — its display name |
{{site.separator}} | Your Separator from Setup → SEO |
{{site.name}} | Your site Title from Setup → SEO |
Type {{ in a title and the list appears, filtered as you keep typing; the +
beside the field offers the same list. Either way what is stored is the text you
see — {{site.name}} — and the line under the field shows you the title as the
live page will render it, counted against the ~60 characters a search result shows.
That count is the resolved one, because that is the title people read.
Write Plans and pricing {{site.separator}} {{site.name}} once, and renaming your
site in Setup → SEO renames it on that page too. A title that names no variables is
published exactly as it always was, so nothing you have already written changes.
Title pattern, in Setup → SEO, is the same idea for every page that has no search title of its own. It defaults to the composition in the table above:
{{page.name}} {{site.separator}} {{site.name}}
You can reorder it — {{site.name}} {{site.separator}} {{page.name}} — or drop the
site name from it entirely with {{page.name}}. Clear the field to go back to the
default.
A separator with nothing rendered on one side of it is left out, so a page that is missing one of the pieces never renders a title that starts or ends with a dash.
Site-wide defaults
Setup → SEO holds the site-level fields every page inherits: the site Title
and Description, the Separator used to join a page's name to the site title
when that page has no search title of its own (default –), the Title pattern
that decides how those pieces are put together, the Favicon, an
App icon, a Social image, and an Entity block (Organization or Person,
with a name and logo) that feeds the site's structured data.
The App icon is the square mark someone installs to a phone or desktop home screen, and it is a third picture rather than a reuse of the other two on purpose: a favicon is a 16–32px glyph drawn for a browser tab, and a site logo is usually a wordmark, which comes out as a thin strip in a square tile. Leave it unset and installing your site uses the favicon, then the logo. Bring one square PNG or SVG at 512×512 or larger.
Every icon size is generated for you
You upload one file for the favicon and one for the app icon. Your site generates every size browsers, phones and installers ask for from it, so you never resize or export anything:
| Where it shows | What your site serves |
|---|---|
| Browser tabs and bookmarks | 16, 32 and 48px PNGs, each declared with its real size |
yoursite.com/favicon.ico | One .ico file holding the 16, 32 and 48px icons |
| iPhone and iPad home screens | 180, 167 and 152px touch icons |
| Installing your site (Android, desktop) | 48, 72, 96, 128, 144, 152, 192, 256, 384 and 512px icons |
| Android's shaped icons | 192 and 512px maskable icons |
A few things happen along the way so each one looks right where it lands:
- Nothing is stretched. An image that is not square is centered on a square with transparent space around it, so a wide mark gets smaller rather than squashed.
- Home-screen icons sit on your background. iPhones paint transparent areas black, so the touch icons are placed on your theme's light background color. Android's maskable icons are too, with your mark kept inside the central safe zone so whatever shape the phone cuts — a circle, a squircle — never clips it.
- An SVG stays sharp. Each size is drawn from the vector, and browsers that show SVG favicons get the SVG itself as well.
- An
.icois used as you uploaded it. It cannot be redrawn, so for the full set upload a PNG, SVG or JPG instead. - A replaced image updates everywhere. The generated sizes are made once and cached for a long time, keyed to the exact file. Use Replace in the media library and the new image gets fresh icons; pages pick them up as they refresh.
The favicon and app icon come from your media library. A URL you paste in instead is used as it is, at the one size the file has.
Your site's install details (the web app manifest)
When someone installs your site, their device reads a small description of it, the web app manifest. Your site writes it for you from settings you already have — there is nothing extra to fill in:
- Name — your site's Title on this card, or its name when that is empty.
- Short name — the name cut at a whole word to 12 characters, which is what a home screen has room for under the icon.
- Description — your site's Description on this card.
- Language — from the Languages card, the same way the page language below is.
- Colors — your theme's light primary color for the title bar, and its light background for the splash screen.
- Icons — the full set above, from your app icon (or favicon, or logo).
The Social image card is the default card for the whole site — every page that sets none of its own uses it, including collection lists and blog entries with no cover image. Set one and no page of your site ever shares as a bare, image-less link again.
What language your site says it is in
Every page your site serves declares a language in its markup, as
<html lang="…">. Browsers offer to translate from it, screen readers pick a
voice with it, and search engines use it to decide who to show the page to — so
a site written in Spanish that declares English is mis-served to everybody.
It comes from your Languages card in site setup, and nothing else:
- the site's default language, if you set one;
- otherwise the first language you added;
- otherwise English, for a site that has not been set up for more than one language.
You do not set this per page. It describes the site, so it is the same on every page of it, and it updates everywhere the moment you change the card.
A locale variant — the same page written in another language — still
advertises its own language where it counts for sharing and search: og:locale
and the inLanguage in its structured data name the variant's language, and its
hreflang links point search engines at it. The <html lang> on the variant
names the site it belongs to.
See Multilingual for adding a language and a language switcher.
SEO check
Setup → SEO starts with an SEO check card. Press Run the check and it reads every page your sitemap lists — every published page whose visibility is Public, without template pages or error pages — and lists what a search result or a crawler would find wrong. The check is part of every plan and needs no add-on. It changes nothing on your site: fix what it finds in each page's SEO card or in the designer, publish, and run it again.
Each page is checked as it is published: the headings, text, images and links of the reusable components it places count as the page's own, with the values that placement gives them, and so do the rows a repeated element shows. A finding about something a component draws points at the component on the page; fix it in the component or in the values the page gives it.
A very large site is checked on its first 150 pages, and the card says how many it left out. For each page it checks:
| Check | What counts as a finding |
|---|---|
| Search title | Missing, over 60 characters, or the same as another page's. A title with variables is checked as it renders. |
| Search description | Missing, over 155 characters, or the same as another page's. |
| Main heading | No h1, more than one, or one that says little (Home, Welcome, a single short word). |
| Images | An image with no description. |
| Links | No other page or shared layout links to the page. The home page never counts. |
| Target keywords | A keyword you named that the page never says. |
It also checks the site as a whole: whether search engines are asked to stay away,
whether your structured data names and describes who publishes the
site, and whether your /llms.txt carries guidance of your own.
Each page gets a score out of 100 — a finding takes off more the more it matters — and the site's score is the average. Each page in the list links to its detail page, where you fix it.
Target keywords
The optional Target keywords by page box takes one line per page:
/pricing: pricing, plans
/lamps: brass desk lamps, dimmable
Each page is checked for at most five keywords. Two lines for the same page count as one list, and the card names any keyword past the first five that it did not check.
The check reports where each page already says its keywords, and names a keyword a page never says. Text a component on the page shows counts. Use a keyword only where the page is about it. A line for an address the check did not cover is reported, not silently dropped.
Check one page
A page's SEO card has a Check this page button. It runs the same check and lists that page's findings, as the page is published. It checks the whole site to answer, because a title another page also uses, or a page nothing links to, shows only from the site as a whole.
With the AI add-on, AI SEO proposes a fix for each finding.
Search engine visibility
You decide what search engines are allowed to index, at two levels.
The whole site
Setup → SEO → Search engines has a Discourage search engines from indexing this site switch. Turn it on while you stage a launch — the site stays live and anyone with a link can still reach it, but:
robots.txtrefuses every crawler (Disallow: /) and names no sitemapsitemap.xmlis served empty (valid but with no URLs, so search consoles read "nothing here" rather than an error they retry)- every page carries a
noindexrobots meta tag — links are still followed, but the page is not listed
While it is on, a warning banner follows you through the console on every page of that site. That is deliberate — a site missing from Google weeks after launch is almost always this switch, left on and forgotten.
Turning it off restores everything within about a minute. Search engines take longer: re-indexing a site is their schedule, not yours, so allow days.
robots.txt and noindex answer different questions. Disallow asks a crawler not to
fetch a page; noindex asks it not to list one. Aglyn sends both, because a page
linked from somewhere else can be listed without ever being fetched — and a crawler that
obeys the disallow never sees the noindex that would have stopped it.
A single page
Use the page's own Visibility, in Page Access on the page's detail view. Only Public pages are offered to search engines. Unlisted, Password protected and Members only pages are all kept out of search results and out of the sitemap, while staying reachable to whoever has the link or the credentials.
Use the site-wide switch while nothing is ready, and per-page Unlisted once you're launching page by page — the switch also covers pages you haven't created yet, which per-page visibility cannot.
noindex is a request, not access controlBoth controls ask search engines not to list a page. Well-behaved crawlers honor that; nothing stops a person with the URL. If a page must be genuinely inaccessible, use site protection instead.
Walking through a staged launch end to end — coming-soon page, hidden site, signup collection, and the reversal on launch day — is covered in Launch a coming-soon page.
Verify your site with Google Search Console
Search Console and Bing Webmaster Tools want proof the site is yours before they show
you its search data. A site on an aglyn.app address has no DNS of its own to add a
record to, so use the HTML tag method — it works on every site, custom domain or not.
- In Google Search Console, choose Add property → URL prefix, enter your site's full address, and pick HTML tag under Other verification methods.
- Copy the tag it shows you. You can paste the whole
<meta name="google-site-verification" content="…" />tag or just the code insidecontent— Aglyn keeps only the code. - In Setup → SEO → Search engine verification, paste it into Google Search Console and press Update.
- Back in Search Console, press Verify. Allow a minute for the published site to pick up the change if the first attempt fails.
Bing works the same way: in Bing Webmaster Tools,
choose Add site, pick HTML Meta Tag, and paste the msvalidate.01 tag into
Bing Webmaster Tools.
Keep the code in place after verifying — both tools re-check it from time to time, and removing it un-verifies the site. Clearing a field and pressing Update removes that tag from every page.
Sitemap & robots
Aglyn generates sitemap.xml and robots.txt for your site automatically, so
search engines can crawl your site correctly. The sitemap always names your site by its
real address (your custom domain when you have one), and includes:
- every published page whose visibility is Public — template pages (blog list/ entry templates, product page templates) are excluded, since their real URLs are the entries and products they render;
- your product and catalog collection URLs, once a product-page or collection-page template is set;
- your content collections — each collection's list URL and its published entries. A scheduled entry joins the sitemap once its publish time passes, and a collection with nothing published yet is left out until its first entry is.
One index, one file per section
sitemap.xml is a sitemap index: instead of listing your URLs directly, it names a
child sitemap for each part of your site, and each child holds that part's URLs.
https://your-site/sitemap.xml
├─ /sitemaps/pages/1.xml your pages
├─ /sitemaps/products/1.xml your products
├─ /sitemaps/catalog/1.xml your catalog collections
├─ /sitemaps/content-blog/1.xml the blog, and its published entries
└─ /sitemaps/content-news/1.xml … one per content collection
Submit sitemap.xml and nothing else — every search engine follows an index to its
children on its own. You never write these child URLs yourself, and the index adds a
collection's file with its first published entry and drops it once nothing is published.
The split is what keeps a growing site correct. A single sitemap may hold at most
50,000 URLs, and anything past that is not submitted at all — silently. A section
that outgrows one file simply continues into a second (/sitemaps/content-blog/2.xml),
and the index names both.
Every URL with a known date carries a last-modified date (lastmod) — the day a
page was last published, the day an entry, product or catalog collection last
changed, and for a listing the day of its newest entry — so a crawler can skip what
has not moved since its last visit. A URL whose date is not known simply has none;
the sitemap never invents one.
The sitemap is cached for a few minutes, and every publish refreshes it immediately — so a freshly published page never waits on the cache.
Social cards
Every published page emits Open Graph and Twitter metadata: title,
description, canonical URL, site name, the page's language, and — once an image
is set — og:image with its og:image:width, og:image:height and
og:image:alt.
What each kind of page emits
Some properties are the same on every page of your site; others describe the one page you are looking at. Both sets are listed here so you can check a page against them.
On every page, whatever kind it is:
| Property | Where it comes from |
|---|---|
og:title, og:description | The page's search title and description |
og:url | The page's canonical address, always absolute |
og:site_name | Setup → SEO Title, then the Entity name, then the site's name |
og:locale | The page's language — its own, then the site's default |
og:image + width, height, alt | The winning card image, below |
twitter:card, title, description, image | The same values as the card above |
twitter:site | The X profile in Setup → Details → Social links |
And per kind of page:
| Page | og:type | What it adds |
|---|---|---|
| Home, and any other page | website | og:locale:alternate for each language this page is translated into |
| A collection listing, and a category listing | website | — |
| A blog or collection entry | article | article:published_time, article:modified_time, article:author (their author page), article:section (its category), article:tag, and twitter:creator |
| An author page | profile | profile:username and twitter:creator, from the author's own X profile |
Anything with nothing behind it is left out rather than filled in. A site that
has set no name at all publishes no og:site_name; an entry that was never
published publishes no article:published_time; an author with no X profile is
still shown on a card, just without a byline credit on it. An empty tag is worse
than a missing one — it is a claim that the answer is "nothing".
Which image a page uses is decided in this order:
- the entry's cover image, for a blog or collection entry;
- the page's own social image, from its SEO panel;
- the site default, from Setup → SEO;
- nothing.
The first three make the page share as a large card
(twitter:card: summary_large_image). Only the fourth falls back to the small
image-less summary card, which is why setting a site default is worth doing once.
Size. 1200×630 is the size every network crops well. Aglyn reads the real dimensions off the media record and publishes them, so previews reserve the right shape before the image finishes loading — it does not assume a size, so a different ratio is described honestly rather than stretched.
Addresses. Card images are always published as absolute URLs on your site's own domain (your custom domain when you have one). A crawler fetches the image without a page to resolve a relative path against, so a relative address is a blank card.
Structured data
Published sites emit JSON-LD, giving search engines and AI assistants rich context about your content.
Every page carries two site-wide nodes:
- a top-level
Organization(orPerson, or a local business type such asPlumber) describing who publishes the site — the entity an assistant reads when someone asks who you are or how to reach you. It is filled in from Setup → SEO → Entity, and it falls back to your site name and description, so a site that has never touched that form still publishes a named, described entity; - a
WebSitenaming the site and its address, with the language it is published in (inLanguage).
And per kind of page:
| Page | What it publishes |
|---|---|
| A collection listing, and a category listing | ItemList of the entries on that page, in their real positions |
| A blog or collection entry | The article type its collection declares — BlogPosting, NewsArticle, Article — with its headline, cover, dates, byline, section and language |
| An author page | ProfilePage wrapping the author as a Person or Organization |
| A product page | Product with its Offer, availability and — where you have reviews — an AggregateRating |
| Any page more than one level deep | BreadcrumbList naming the trail to it |
A page with a FAQ section — a Section whose accessible label names it (FAQ, Frequently asked questions) — holding two or more question-and-answer pairs, as two-line items or accordions | FAQPage listing each question with its answer, the block AI search quotes and FAQ rich results read |
Your social links, from Setup → Details → Business details, are published as
the entity's sameAs — the property that tells a search engine your site, your
LinkedIn page and your X account are all the same organization. You do not have to
retype them anywhere; the list the site already renders is the list it publishes.
Two fields are worth adding by hand, because nothing can guess them:
- Contact email / phone, published as
contactPoint; - Address, published as a
PostalAddress. Partial is fine — a city and a country are still a real answer.
Local businesses
If customers visit you, or you go to them, pick a Business type on the
Local business card in Setup → SEO — Plumber, Restaurant, Dentist, or plain
LocalBusiness when nothing narrower fits. The site entity is then published as
that type instead of Organization, and four more fields appear:
-
Areas served — one city or area per line, published as
areaServed. This is how a business with no storefront says where it works; it does not need a street address, and a region and a country on the Address card are enough. -
Hours — one line per set of days, in 24-hour time:
Mo-Fr 08:00-17:00Sa 09:00-13:00Days can be written
Mo,MonorMonday, joined with-for a range or,for a list (Mo,We,Fr 07:00-15:00). For open all day, use00:00-23:59. Each line becomes anopeningHoursSpecification; a line that does not read is left out rather than guessed at, and the card tells you which one before you save. -
Price range — free text such as
$$or$150–$400, published aspriceRange. -
Payment accepted — free text such as
Cash, credit card, Zelle, published aspaymentAccepted.
Your Contact phone is published on the business itself as well as on its
contactPoint, which is where a local search result reads it from. A business type
has no effect while the entity Type is Person.
AI agents
Agents read a site differently from a browser. Aglyn publishes four things for them, on every site, with nothing to switch on.
Markdown for any page
Every page serves a Markdown version of itself with the navigation, styling and scripts removed. Two ways to ask:
curl -H "Accept: text/markdown" https://your-site/pricing
curl https://your-site/pricing.md
Both return the same document: the page title, its summary, the content region, and a
Source: line carrying the canonical URL. Site chrome is left out — that is the point.
The HTML page links to its own Markdown with
<link rel="alternate" type="text/markdown">, so an agent reading the HTML can find it.
A request that accepts neither HTML nor Markdown — Accept: application/pdf, say — is
answered 406 Not Acceptable with a plain-text list of what IS available. Ordinary
browser requests are never affected.
The search page is the one exception: its results are computed in the browser, so there is no Markdown version to serve and it stays HTML.
/llms.txt
A short guide at https://your-site/llms.txt telling an agent what the site is for and
which addresses answer what — your collections that have published entries and their
entry counts, search, the sitemap, the API description, and a contact route when you
publish one.
It also lists your pages in the order your site is built: the home page, then the
top-level pages as your screens list arranges them, then the pages under each of those.
A page you have hidden from search — unlisted, password-protected, members-only — is
never listed, so the guide agrees with your robots meta about which pages exist. A very
large site lists its first two hundred in that order and leaves the rest to the sitemap.
You can lead it with your own words. Setup → SEO → AI agents has two boxes:
- When to use this site — the questions you are the best source for. Be specific; an agent discounts a claim it cannot check, and generic marketing copy does not read as guidance.
- How an agent should call you — anything worth knowing before it fetches.
Both are optional. The rest of the file is derived from what your site actually publishes, so it is never empty and never out of date.
/openapi.json
A machine-readable OpenAPI 3.1 description of everything your site serves, generated
per site so it always names your own domain. Every operation carries a unique
operationId, a description, typed parameters and a response schema — the shape an AI
tool-calling integration reads to turn your site into callable functions.
It describes reads only. Your forms still work exactly as they did; they are simply not listed as an endpoint for every crawler on the internet to call.
/.well-known/api-catalog
Two APIs answer for your site's data, and they are not interchangeable. The
/openapi.json above describes what the site serves — your pages, feeds and
sitemap — and it is anonymous and read-only. The Aglyn platform API describes the
same records as typed JSON, takes an API key, and writes.
An agent that finds only the first concludes there is no write API. The catalog is the
one document that names both, published at /.well-known/api-catalog in the format
RFC 9727 reserves for exactly this
question. Each entry carries that API's OpenAPI description, its documentation and,
where there is one, its health endpoint.
Nothing to configure — every site publishes it.
Crawler access
robots.txt names the major AI crawlers and assistants explicitly — GPTBot,
ClaudeBot, Google-Extended, PerplexityBot and the rest — and allows them, alongside the
usual wildcard rule.
Turning on Discourage search engines reverses all of it in one switch: robots.txt
refuses everything including those named agents, and /llms.txt, /openapi.json and
/.well-known/api-catalog stop being served at all. A site that has asked not to be
found does not publish an index of its own endpoints.
Analytics integration
Add your Google Analytics ID to track traffic alongside Aglyn's built-in analytics.