Skip to content

SEO: fix OG tags, canonicals, titles & meta descriptions (Ahrefs May 2026 audit) #23

Description

@coderdan

Source: Ahrefs full-site crawl of cipherstash.com, 31 May 2026. This issue covers everything the audit attributed to /docs/*, which lives in this repo. Current-state <head> tags below were verified live on 31 May.

TL;DR

The docs site is a Next.js App Router app. Almost all of the audit's volume comes from /docs/* and is fixable with three template-level changes plus one content/generator pass:

  1. Add og:url + og:type to the metadata template → clears 172 pages.
  2. Add a self-referencing canonical link (currently none exist on any page) → clears the duplicate-pages error + hardens trailing-slash dupes.
  3. Add a global title.template suffix and fix broken section-index titles → clears most of 76 short titles.
  4. Enrich the API-reference generator's title/description templates → clears most of 122 short descriptions.

Verified current <head> on /docs/stack/cipherstash/encryption:

<html lang="en">                            <!-- ✅ lang present -->
<title>/ENCRYPTION</title>                 <!-- ❌ broken title -->
<meta name="description" content="Searchable field-level encryption. ...">
<meta property="og:title" content="/ENCRYPTION">
<meta property="og:description" content="...">
<meta property="og:image" content="https://docs.cipherstash.com/og/stack/cipherstash/encryption/image.png">
<meta name="twitter:card" content="summary_large_image">   <!-- twitter:* all present -->
<!-- ❌ NO og:url -->
<!-- ❌ NO og:type -->
<!-- ❌ NO <link rel="canonical"> -->

Priority summary

# Issue Pages Root cause Fix
1 Open Graph incomplete 172 og:url + og:type never emitted Add both to metadata template
2 Duplicate pages, no canonical 4 No <link rel=canonical> anywhere Self-canonical + trailing-slash policy
3 Title too short / broken 76 No title suffix; /SECTION index bug; generic "Overview"; bare API symbols title.template + fix index titles + enrich API-ref generator
4 Meta description too short 122 Auto-gen "API reference for X" (~25-45 chars) + thin content Enrich API-ref generator + content pass
5 /docs index missing title 1 Root page emits no <title> Add a title
6 Slow page 1 /docs/stack/cipherstash/proxy/encrypt-tool TTFB ~6.4s Profile SSR/data fetch
7 Trailing-slash redirect inlinks ~3 /encryption/, /kms/, /proxy/ 308 + redirect-only inlinks Folds into #2

Not this repo: the dashboard.cipherstash.com root 404 belongs in apps/dashboard; status./trust./clerk. warnings (HTML-lang, large CSS, not-compressed, JS redirect) are third-party vendor pages.

1. Open Graph — add og:url + og:type (172) — P1

Every page has og:title/description/image + twitter:*; only og:url and og:type are missing. Next.js App Router does not inherit a parent openGraph into a child that defines its own, so set these where each page's openGraph is built:

openGraph: {
  type: 'article',      // 'website' for section index / landing pages
  url: pageUrl,         // <-- ADD: absolute URL of this page
  title, description, images: [ogImageUrl],
},

2. Canonical — add it (none exist) — P1

No <link rel="canonical"> on any docs page. Root of the duplicate error (/reference/stack/latest vs /reference/stack/latest/). Fix via the Metadata API and enforce one trailing-slash form:

alternates: { canonical: pageUrl },   // + set `trailingSlash` in next.config and canonicalize to it

Compute pageUrl once and reuse for both #1 and #2.

3. Titles too short / broken (76) — P1/P2

(a) Broken section-index titles — render as uppercased slug with a leading slash:

URL Current Should be
/docs/stack/cipherstash/encryption /ENCRYPTION Encryption
/docs/stack/cipherstash/proxy /PROXY Proxy

Looks like a fallback doing '/' + segment.toUpperCase() when an index page has no explicit title. Give these real titles and fix the fallback.

(b) Generic "Overview"/reference/use-cases and /reference/comparisons both title as Overview. Make them page-specific.

(c) Short single tokens (bulk) — 46 are auto-generated API-reference pages (/reference/stack/latest/packages/...) titled with the bare symbol (SearchTerm, Client, ...); the rest are content pages (Keysets, ZeroKMS, ...). Add a global template in the docs root layout:

title: { template: '%s | CipherStash Docs', default: 'CipherStash Docs' }

For the API-ref generator prefer a kind-aware title, e.g. SearchTerm — @cipherstash/stack API.

4. Meta descriptions too short (122) — P2

Shortest are all auto-generated API reference for X (25-45 chars). Fix in the API-ref generator by building from the symbol's TSDoc summary:

{KIND} {Name} in @cipherstash/stack — {first sentence of TSDoc}. Parameters, return type, and examples.

For hand-authored pages, editorial pass to bring description into the 110-160 char range. (Meta-description too long is a marketing-site issue — 30 of 32; ignore here.)

5-7 (P2/P3)

  • /docs index: add a <title>.
  • /docs/stack/cipherstash/proxy/encrypt-tool: TTFB ~6.4s (others <1s) — profile SSR/data fetch.
  • Trailing-slash variants (/encryption/, /kms/, /proxy/) resolve with the chore(deps): bump next from 16.1.6 to 16.2.3 #2 policy.

Suggested sequencing

  1. Metadata PR: OG url+type (chore(deps): bump next from 16.1.6 to 16.1.7 #1) + canonical (chore(deps): bump next from 16.1.6 to 16.2.3 #2) + title.template & index-title fix (#3a/b) — ~180 page-issues.
  2. Generator PR: API-ref title + description templates (#3c, docs: add CREATE INDEX guide for encrypted columns (self-hosted vs Supabase paths differ) #4) — ~46 titles + ~100 descriptions.
  3. Content pass: ~25 hand-authored content pages.
  4. Spot fixes: docs: per-query-type pages for searchable encryption (Equality / Match / Range) #5, docs: standalone Bulk operations page for the Encryption SDK #6.

Appendix — affected-URL examples

The complete 76-title list (grouped by fix path) and the meta-description list live in the marketing team's audit handoff doc (cipherstash-docs-seo-handoff.md); ping for the raw Ahrefs CSVs if useful. Key examples:

  • Broken: /ENCRYPTION (/cipherstash/encryption), /PROXY (/cipherstash/proxy)
  • Generic: Overview (/reference/use-cases, /reference/comparisons)
  • Content (sample): ZeroKMS, Keysets, Supabase, DynamoDB, Drizzle ORM, SST, Deploy, Billing, Glossary, Members
  • API-ref (46, sample): SearchTerm, Client, CastAs, EncryptOptions, LockContext, encryptedTable, queryTypes

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions