Skip to content

Commit c9651e2

Browse files
committed
fix(layer): emit absolute sitemap URLs from configured site.url
The /sitemap.xml route resolved its base URL with `inferSiteURL()`, which only reads environment variables (NUXT_SITE_URL, VERCEL_URL, CF_PAGES_URL, …). A `site.url` set in nuxt.config was never consulted, so sites that configure it the documented way fell back to `''` and emitted relative `<loc>` values: <loc>/docs/getting-started/introduction</loc> The sitemap protocol requires absolute URLs, so search engines reject these. Expose the resolved `site.url` through `runtimeConfig.public.site` (the same pattern already used for `mcp.route`) and prefer it in the route, keeping `inferSiteURL()` as the fallback so env-var-only deploys are unaffected. `withoutTrailingSlash` avoids `https://example.com//docs/...` when the configured url ends in a slash.
1 parent 6f001d4 commit c9651e2

2 files changed

Lines changed: 14 additions & 1 deletion

File tree

layer/modules/config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ export default defineNuxtModule({
5959
branch: getGitBranch(),
6060
})
6161

62+
/*
63+
** Site URL (expose to the server: /sitemap.xml needs it at runtime)
64+
*/
65+
nuxt.options.runtimeConfig.public.site = defu(
66+
nuxt.options.runtimeConfig.public.site as { url?: string } | undefined,
67+
{ url: (typeof nuxt.options.site === 'object' && nuxt.options.site?.url) || url },
68+
)
69+
6270
/*
6371
** MCP route (expose to client so the page header dropdown stays in sync
6472
** with the user-configured `mcp.route` from @nuxtjs/mcp-toolkit)

layer/server/routes/sitemap.xml.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { queryCollection } from '@nuxt/content/server'
2+
import { withoutTrailingSlash } from 'ufo'
23
import { inferSiteURL } from '../../utils/meta'
34
import { getAvailableLocales, getCollectionsToQuery, isNavigationPath } from '../utils/content'
45

@@ -9,7 +10,11 @@ interface SitemapUrl {
910

1011
export default defineEventHandler(async (event) => {
1112
const config = useRuntimeConfig(event)
12-
const siteUrl = inferSiteURL() || ''
13+
// Config first: inferSiteURL() only reads env vars, so a configured
14+
// site.url would be dropped and every <loc> emitted relative.
15+
const siteUrl = withoutTrailingSlash(
16+
(config.public.site as { url?: string } | undefined)?.url || inferSiteURL() || '',
17+
)
1318

1419
const availableLocales = getAvailableLocales(config.public as Record<string, unknown>)
1520
const collections = getCollectionsToQuery(undefined, availableLocales)

0 commit comments

Comments
 (0)