Services

Apps Docs


SEO review: apps/docs

Date: 2026-07-01

Scope#

Reviewed apps/docs SEO readiness across App Router metadata, docs route templates, canonical/Open Graph/Twitter tags, robots/sitemap behavior, OpenAPI/generated reference content, structured data, headings/content semantics, and public assets.

Executive summary#

apps/docs has a solid SEO foundation for guide content: the root layout defines site metadata, guide routes derive per-page titles/descriptions/canonicals/social cards from MDX frontmatter, guide pages render one visible <h1>, and guide JSON-LD includes TechArticle and breadcrumb data. The main risks are deployment/indexability drift and incomplete discovery for generated reference content: the HTML robots flag depends on a Vercel-specific environment variable, the sitemap is generated after build and omits reference/API pages, and some reference metadata constructs incorrect or partial canonicals.

Findings by severity#

High#

H1. Production indexability depends on NEXT_PUBLIC_VERCEL_ENV#

Evidence: IS_PRODUCTION is true only when NEXT_PUBLIC_VERCEL_ENV === 'production' (apps/docs/lib/constants.ts:10), and root metadata uses that flag for robots.index and robots.follow (apps/docs/app/layout.tsx:29-31). At the same time, the static robots file allows crawling (apps/docs/public/robots.txt:1-3) and deployment headers allow indexing on production hosts (apps/docs/public/_headers:1-12, apps/docs/next.config.mjs:144-165).

Impact: A non-Vercel production deployment, or a production build missing this public env var, can emit page-level noindex,nofollow while robots.txt and headers invite crawling. Search engines generally honor the more specific page-level noindex.

Recommended fix: Replace the Vercel-specific production test with an explicit docs indexing env var or host/deployment contract that is set in every production deployment. Keep robots metadata, X-Robots-Tag, and robots.txt aligned from the same source of truth.

Medium#

M1. Sitemap is postbuild-generated, absent from source, and omits reference/API routes#

Evidence: apps/docs/public/sitemap.xml is not present in the checked tree, while robots.txt points crawlers to https://www.assistance.bg/docs/sitemap.xml (apps/docs/public/robots.txt:3). The sitemap script only globs guide and troubleshooting MDX (apps/docs/internals/generate-sitemap.ts:22-50) and writes public/sitemap.xml (apps/docs/internals/generate-sitemap.ts:77-82). It does not include /reference, /reference/api, /reference/cli, SDK pages, crawler pages, or other generated reference routes, even though reference routes are statically enumerated (apps/docs/features/docs/Reference.utils.ts:99-127). The script is only called by postbuild (apps/docs/package.json:37).

Impact: Fresh clones and partial deployments can lack the advertised sitemap, and generated reference/API content has weak discovery signals compared with guide pages.

Recommended fix: Generate sitemap content through Next's App Router app/sitemap.ts or make sitemap generation a required predeploy artifact. Extend the route list with generateReferenceStaticParams() output plus the reference index, API, CLI, and any crawler-friendly reference detail URLs that should be indexed.

M2. SDK reference canonical metadata can be malformed or incomplete#

Evidence: SDK metadata builds url from [BASE_PATH, 'reference', sdkId, path[0]].filter(Boolean).join('/') (apps/docs/features/docs/Reference.utils.ts:153) and only emits alternates.canonical when slug.length > 0 (apps/docs/features/docs/Reference.utils.ts:163-168). This drops the SDK version segment for versioned pages, omits deeper section paths, and produces a relative string without a leading slash such as docs/reference/... when BASE_PATH is /docs. Open Graph uses the same value (apps/docs/features/docs/Reference.utils.ts:170-174).

Impact: Reference pages can publish inconsistent canonical/OG URLs, consolidating versioned or deep reference pages onto the wrong URL or emitting non-absolute social URLs.

Recommended fix: Reuse absoluteDocsUrl()/resolveCanonicalUrl() for reference metadata, include version and full path segments, and emit canonicals for all indexable reference pages including reference roots.

M3. CLI/API/self-hosting reference pages lack canonical, Open Graph, and Twitter overrides#

Evidence: generateReferenceMetadata() returns only title and description for CLI and API pages (apps/docs/features/docs/Reference.utils.ts:176-185) and only a title for self-hosting pages (apps/docs/features/docs/Reference.utils.ts:186-189). These pages therefore inherit only root-level OG/Twitter defaults from apps/docs/app/layout.tsx:33-44.

Impact: Important generated reference landing pages have weaker search and share previews than guide pages, and canonical signals are less explicit.

Recommended fix: Add alternates.canonical, openGraph.title/description/url/images, and twitter.title/description/images for CLI, API, and self-hosting branches, using the same URL helper as guides.

Low#

L1. Open Graph image generation can emit an empty-host URL#

Evidence: MISC_URL defaults to an empty string (apps/docs/lib/constants.ts:13), while generateOpenGraphImageMeta() concatenates it directly into /functions/v1/og-images?... (apps/docs/features/seo/openGraph.ts:12-16). Guide and reference metadata use that helper for dynamic social images (apps/docs/features/docs/GuidesMdx.utils.tsx:226-247, apps/docs/features/docs/Reference.utils.ts:155-174).

Impact: If NEXT_PUBLIC_MISC_URL is missing in production, OG/Twitter image metadata becomes a relative function URL that may not exist on the docs origin.

Recommended fix: Make MISC_URL required for production docs builds or fall back intentionally to the static DEFAULT_OG_IMAGE absolute URL.

L2. Public manifest copy and icon paths are stale for Assistance docs#

Evidence: The favicon manifest still describes “The Postgres Development Platform.” (apps/docs/public/favicon/manifest.json:2-5) and uses root-relative icon URLs such as /favicon/android-icon-36x36.png (apps/docs/public/favicon/manifest.json:10). The docs app runs under basePath: '/docs' (apps/docs/next.config.mjs:61-63), while root metadata generates favicons with BASE_PATH (apps/docs/app/layout.tsx:27-28).

Impact: If the manifest is exposed to crawlers or install surfaces, it can show stale product copy and broken icon URLs under the /docs base path.

Recommended fix: Update manifest copy to Assistance Docs and use /docs/favicon/... paths or generate the manifest through the same base-path-aware favicon helper.

Positive controls#

  • Root layout defines metadataBase, description, favicons, Open Graph, Twitter, and robots metadata (apps/docs/app/layout.tsx:22-44).
  • Guide metadata derives title, description, keywords, canonical URL, Open Graph image, and Twitter card from validated MDX frontmatter (apps/docs/features/docs/GuidesMdx.utils.tsx:217-247; frontmatter validation in apps/docs/lib/docs.ts:19-49).
  • Guide page templates render one primary <h1> from frontmatter and include TechArticle plus BreadcrumbList JSON-LD (apps/docs/features/docs/GuidesMdx.template.tsx:63-107; JSON-LD builder in apps/docs/lib/seo.ts:50-90).
  • Homepage metadata has an explicit canonical for /docs (apps/docs/app/page.tsx:12-27) and emits WebSite/SearchAction JSON-LD (apps/docs/app/page.tsx:10, apps/docs/lib/seo.ts:93-104).
  • Reference pages are statically constrained with dynamicParams = false (apps/docs/app/reference/[...slug]/page.tsx:15) and crawler-specific reference HTML includes title, description, canonical, and image tags (apps/docs/app/api/crawlers/route.ts:67-85).
  • Preview deployment headers block indexing for *.pages.dev and *.vercel.app hosts (apps/docs/public/_headers:14-23; equivalent Next headers in apps/docs/next.config.mjs:156-165).

Verification notes#

  • File inspection confirmed no checked-in apps/docs/public/sitemap.xml exists while robots.txt references it.
  • mise exec -- pnpm --filter docs run build:sitemap was attempted for generator verification, but failed because this isolated worktree has no installed node_modules for globby (ERR_MODULE_NOT_FOUND: Cannot find package 'globby'). No generated files were left behind.