Services

Technical Seo Audit 2026 06 17


Technical SEO audit — public Next.js apps

Date: 2026-06-17

Scope: static review of apps/www, apps/blog, apps/docs, and apps/academy for metadata base URL handling, robots, sitemaps, canonicals, OpenGraph/Twitter defaults, noindex usage, redirects, and public routing behavior. No product changes were made.

Findings#

High — www production metadata depends on NEXT_PUBLIC_VERCEL_ENV, but Cloudflare/OpenNext builds may not set it#

  • Files: apps/www/lib/constants.ts, apps/www/app/layout.tsx, apps/www/next.config.mjs
  • Evidence: SITE_ORIGIN is https://www.assistance.bg only when process.env.NEXT_PUBLIC_VERCEL_ENV === 'production'; otherwise it uses NEXT_PUBLIC_VERCEL_BRANCH_URL or falls back to http://localhost:3040.
  • Why it matters: root metadata uses metadataBase: new URL(SITE_ORIGIN), OpenGraph image URLs, and canonical resolution from apps/www/app/layout.tsx. A production build/deploy path that does not set the Vercel-specific public env can emit localhost or preview absolute URLs in SEO metadata.
  • Recommendation: make the canonical public origin explicit for www (for example NEXT_PUBLIC_SITE_URL with a production default of https://www.assistance.bg) rather than deriving it from Vercel-only environment names.

High — www sitemap generation is built around legacy Pages Router paths, not current App Router routes#

  • Files: apps/www/internals/generate-sitemap.mjs, apps/www/package.json, apps/www/public/sitemap_www.xml, apps/www/public/robots.txt
  • Evidence: apps/www/package.json runs node ./internals/generate-sitemap.mjs after build. The generator globs pages/*.tsx, pages/**/*.tsx, data/**/*.mdx, _blog/*.mdx, _case-studies/*.mdx, _customers/*.mdx, and _events/*.mdx; it does not glob app/**/page.tsx. Current public routes live under apps/www/app/**/page.tsx.
  • Why it matters: apps/www/public/robots.txt advertises https://www.assistance.bg/sitemap_www.xml, but the generator can omit App Router routes and include legacy/content-derived URLs that may not match the deployed route graph.
  • Recommendation: replace or update apps/www/internals/generate-sitemap.mjs to source current App Router routes and current content collections, then regenerate public/sitemap.xml and public/sitemap_www.xml.

Medium — sitemap index does not include the Academy sitemap#

  • Files: apps/www/internals/generate-sitemap.mjs, apps/academy/app/sitemap.ts, apps/academy/app/robots.ts
  • Evidence: apps/www/internals/generate-sitemap.mjs writes a sitemap index containing only /sitemap_www.xml and /docs/sitemap.xml. Academy exposes https://assistance.bg/academy/sitemap.xml from apps/academy/app/sitemap.ts and advertises it from apps/academy/app/robots.ts.
  • Why it matters: if Search Console or crawlers discover only the root sitemap index, Academy URLs are not included there. Crawlers can still discover Academy via its own robots file when they request /academy/robots.txt, but the root index is incomplete for the public app set.
  • Recommendation: include the Academy sitemap in the root sitemap index if Academy is intended to be discoverable from the main property sitemap submission.

Medium — Docs has environment-specific robots controls, but no App Router robots.ts or sitemap.ts#

  • Files: apps/docs/public/robots.txt, apps/docs/internals/generate-sitemap.ts, apps/docs/next.config.mjs, apps/docs/app/layout.tsx
  • Evidence: docs relies on static public/robots.txt and generated public/sitemap.xml; there are no apps/docs/app/robots.ts or apps/docs/app/sitemap.ts files. apps/docs/next.config.mjs adds X-Robots-Tag: all for host assistance.bg and X-Robots-Tag: noindex for preview hosts matching *.vercel.app or *.pages.dev. apps/docs/app/layout.tsx also sets page-level robots from NEXT_PUBLIC_VERCEL_ENV === 'production'.
  • Why it matters: the static robots/sitemap path is valid, but page metadata and response headers use different environment/host signals. A production Cloudflare route on www.assistance.bg/docs/* depends on the header host matching behavior and may not align with the Vercel-specific metadata flag.
  • Recommendation: standardize docs indexability on deployment host/origin configuration and consider App Router robots.ts/sitemap.ts if runtime-generated route metadata becomes necessary.

Low — Blog and Academy have stronger App Router SEO primitives than www/Docs#

  • Files: apps/blog/app/layout.tsx, apps/blog/app/robots.ts, apps/blog/app/sitemap.ts, apps/academy/app/layout.tsx, apps/academy/app/robots.ts, apps/academy/app/sitemap.ts
  • Evidence: Blog defines metadataBase, root canonical, OpenGraph/Twitter defaults, robots.ts, and sitemap.ts. Academy defines metadataBase, root canonical, OpenGraph/Twitter defaults, protected-route noindex metadata, robots.ts, and sitemap.ts.
  • Note: Blog defaults SITE_ORIGIN to https://assistance.bg/blog (apps/blog/lib/constants.ts) while deployment documentation references both www.assistance.bg/blog and Cloudflare Worker/custom-domain options. Confirm the intended canonical host before changing.

Low — Noindex usage is targeted and limited#

  • Files: apps/www/app/dashboard/sign-in/page.tsx, apps/academy/app/(protected)/layout.tsx, apps/blog/app/services/[slug]/page.tsx, apps/docs/app/not-found.tsx, apps/docs/next.config.mjs
  • Evidence: noindex appears on auth/protected/moved/404 surfaces and docs preview-host headers. No broad accidental noindex was found in the audited public app code.
  • Recommendation: keep this pattern; avoid adding route-group-level robots metadata without verifying child route inheritance.

Static verification performed#

bash
1
find apps/www apps/blog apps/docs apps/academy -path '*/app/*' \
2
\( -name 'robots.ts' -o -name 'sitemap.ts' -o -name 'layout.tsx' -o -name 'page.tsx' \) -print
bash
1
rg -n "metadataBase|alternates|canonical|openGraph|twitter|robots|noindex|sitemap|redirect|permanentRedirect|NextResponse.redirect|SITE_URL|NEXT_PUBLIC_SITE|X-Robots-Tag" \
2
apps/www apps/blog apps/docs apps/academy apps/*/next.config.mjs -S
bash
1
find apps/www apps/docs -path '*/app/*' \( -name 'robots.ts' -o -name 'sitemap.ts' \) -print
2
find apps/www -name '*sitemap*' -o -name '*robots*' | sort
3
find apps/docs -name '*sitemap*' -o -name '*robots*' | sort
bash
1
rg -n "generate-sitemap|sitemap" package.json apps/*/package.json turbo.json .mise.toml -S