# SEO

## Principles

1. **One canonical URL per page.** A question lives at `/q/{slug}`; legacy paths permanently redirect to it.
2. **Meaningful pagination self-canonicalises.** Page 2 canonicalises to page 2, never to page 1.
3. **Transient state never enters a canonical.** `created`, `voted`, `followed`, `reported`, `answer`, `commented` and `utm_*` are stripped.
4. **Only public pages are indexable.** Admin, auth, account, API and search results are excluded and `noindex`.
5. **Thin pages stay out of the index.** Empty categories, empty tags and empty profiles are not indexable.

## Settings

| Key | Meaning |
|---|---|
| `seo.default_title` | Fallback title |
| `seo.robots_mode` | Site-wide robots policy |
| `seo.sitemap_enabled` | Serve sitemaps |
| `seo.question_permalink` / `seo.question_prefix` / `seo.slug_style` | Question URL shape |
| `seo.og_image` | Default social image |
| `seo.twitter_site` | Twitter/X site handle |
| `seo.google_verification` / `seo.bing_verification` | Search-console verification tokens |

Per-question, per-category and per-tag SEO title, description, index policy and same-site canonical override are edited on the record itself.

## Metadata rendered

Title, meta description, canonical link, robots directives, Open Graph and Twitter card tags, and `hreflang` where multiple locales exist for the same content.

## Structured data

| Page | Schema |
|---|---|
| Question | `QAPage` with `Question` and `Answer` |
| Topic (member-authored) | `DiscussionForumPosting` with the body in `text` |
| Topic (editorial/system) | Ordinary page schema |
| Profile | `ProfilePage` (indexable profiles only) |
| Everywhere | `BreadcrumbList` |

`answerCount` reports the **total** published answers across all pages, not the number rendered on the current page. A question with 130 answers paginated at 50 emits `answerCount: 130` on every page.

Plugins can adjust metadata and JSON-LD through the `seo.meta` and `seo.structured_data` filters.

## Sitemaps

`/sitemap.xml` is a sitemap **index**. Children are generated per content type and paged:

```
/sitemaps/questions-1.xml
/sitemaps/topics-1.xml
/sitemaps/categories.xml
/sitemaps/tags.xml
/sitemaps/profiles-1.xml
```

Every child contains only canonical, indexable URLs. Admin, auth, search and API URLs never appear. See [Sitemap pipeline](../07-internals/SITEMAP-PIPELINE.md).

## Robots

`/robots.txt` is generated and respects `seo.robots_mode`. Keep a site `noindex` while seeding content, then switch — it is much cheaper than removing thin pages from an index later.

## Practical checklist

- [ ] `APP_URL` matches the domain you actually serve
- [ ] Robots mode is correct for the site's stage
- [ ] `/sitemap.xml` loads and lists only public content
- [ ] Paginated pages canonicalise to themselves
- [ ] Empty categories, tags and profiles are not in the sitemap
- [ ] Structured data validates
