Prerender robots.txt so the static build keeps its sitemap reference

This commit is contained in:
Djeex
2026-09-04 23:02:50 +02:00
parent 90eb205e85
commit ebf65c247c
2 changed files with 7 additions and 5 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ This project starts from the `docus` i18n starter template (`extends: ['docus']`
- **301 redirects (`routeRules`).** The old site served French content at root-level URLs (e.g. `/generalites/reseau/nat`, no locale prefix, on a separate `french` git branch). Restructuring into a single repo with `@nuxtjs/i18n`'s `/fr/...` prefix changed every French URL, which would otherwise break external links, bookmarks, and search-engine rankings for those pages built up over time. All 44 mappings use `statusCode: 301` explicitly — Nitro's default redirect status is 307 (temporary), which search engines don't treat as "please re-index this at the new URL" the way a 301 (permanent) does. There's deliberately **no** `/``/fr` redirect: root already serves English by default, and `@nuxtjs/i18n`'s `detectBrowserLanguage` already handles sending French-browser visitors to `/fr` automatically — a static redirect rule would just fight with that. - **301 redirects (`routeRules`).** The old site served French content at root-level URLs (e.g. `/generalites/reseau/nat`, no locale prefix, on a separate `french` git branch). Restructuring into a single repo with `@nuxtjs/i18n`'s `/fr/...` prefix changed every French URL, which would otherwise break external links, bookmarks, and search-engine rankings for those pages built up over time. All 44 mappings use `statusCode: 301` explicitly — Nitro's default redirect status is 307 (temporary), which search engines don't treat as "please re-index this at the new URL" the way a 301 (permanent) does. There's deliberately **no** `/``/fr` redirect: root already serves English by default, and `@nuxtjs/i18n`'s `detectBrowserLanguage` already handles sending French-browser visitors to `/fr` automatically — a static redirect rule would just fight with that.
- **`site.trailingSlash: true`.** The site builds as a static export (`nuxt build`, deployed as static files on a web server) and the production host 301-redirects a bare `path` request to `path/` (verified against `docu.djeex.fr`), so canonical/og:url/sitemap URLs need to already carry the trailing slash — otherwise the canonical tag points at the very URL the server redirects away from, a loop that keeps the page out of search results. This is documented, official behavior for the wider Nuxt SEO ecosystem (`nuxtseo.com`'s "Trailing Slashes" guide), but Docus doesn't depend on `nuxt-seo-utils` for its canonical/og:url logic — it hand-rolls its own in `useSeo.ts` via a plain `joinURL(site.url, route.path)` that never checks this setting. That gap is why the items below exist alongside it. - **`site.trailingSlash: true`.** The site builds as a static export (`nuxt build`, deployed as static files on a web server) and the production host 301-redirects a bare `path` request to `path/` (verified against `docu.djeex.fr`), so canonical/og:url/sitemap URLs need to already carry the trailing slash — otherwise the canonical tag points at the very URL the server redirects away from, a loop that keeps the page out of search results. This is documented, official behavior for the wider Nuxt SEO ecosystem (`nuxtseo.com`'s "Trailing Slashes" guide), but Docus doesn't depend on `nuxt-seo-utils` for its canonical/og:url logic — it hand-rolls its own in `useSeo.ts` via a plain `joinURL(site.url, route.path)` that never checks this setting. That gap is why the items below exist alongside it.
- **`nitro.prerender.autoSubfolderIndex: true`.** Docus sets this to `false` in its own `nuxt.config.ts`, which writes every route as `path.html` instead of `path/index.html` — the exact opposite of what the trailing-slash setup above needs, since the host would then redirect `/path` to `/path/` and find no directory there. Restoring the Nitro default puts the files back where the advertised URLs actually point. - **`nitro.prerender.autoSubfolderIndex: true`.** Docus sets this to `false` in its own `nuxt.config.ts`, which writes every route as `path.html` instead of `path/index.html` — the exact opposite of what the trailing-slash setup above needs, since the host would then redirect `/path` to `/path/` and find no directory there. Restoring the Nitro default puts the files back where the advertised URLs actually point.
- **`nitro.prerender.routes: ['/']`.** Docus's `nitro:config` hook seeds one prerender route per locale (`/en`, `/fr`) but never `/` itself, so the site root has no file at all and 404s on a static host. Adding it renders `@nuxtjs/i18n`'s own root redirect to the default locale. (No `routeRules` entry for `/` instead: a hard redirect there would override i18n's `detectBrowserLanguage`, which is what currently sends French-browser visitors to `/fr`.) - **`nitro.prerender.routes: ['/', '/robots.txt']`.** Docus's `nitro:config` hook seeds one prerender route per locale (`/en`, `/fr`) and `/sitemap.xml`, but never `/` or `/robots.txt` — both exist as server routes yet were never written to the static output, so both 404 on a static host. `/` loses i18n's redirect to the default locale, and robots.txt loses the `Sitemap:` line that points crawlers at the sitemap (the live site has this gap today: requesting `/robots.txt` returns the site's HTML). Prerendering them is all that's needed. (Deliberately not a `routeRules` redirect for `/`: a hard redirect there would override i18n's `detectBrowserLanguage`, which is what sends French-browser visitors to `/fr`.)
- **`experimental.defaults.nuxtLink.trailingSlash: 'append'`.** The native Nuxt-core (not `@nuxtjs/i18n`'s own, separate `trailingSlash` option — that one only affects `switchLocalePath()` and double-appends the slash on hreflang alternate links) way to make every `<NuxtLink>` href, including the ones i18n's `switchLocalePath` builds for hreflang tags, resolve with a trailing slash already, matching both `site.trailingSlash` and the directory-style files on disk. - **`experimental.defaults.nuxtLink.trailingSlash: 'append'`.** The native Nuxt-core (not `@nuxtjs/i18n`'s own, separate `trailingSlash` option — that one only affects `switchLocalePath()` and double-appends the slash on hreflang alternate links) way to make every `<NuxtLink>` href, including the ones i18n's `switchLocalePath` builds for hreflang tags, resolve with a trailing slash already, matching both `site.trailingSlash` and the directory-style files on disk.
> **Do not add a global trailing-slash redirect middleware here.** An earlier revision had `app/middleware/trailing-slash.global.ts` 301-redirecting bare paths to their slash form. It broke the production build outright: Nitro's prerender crawler seeds on `/en` and `/fr`, the middleware turned both into redirect responses, and since Nitro extracts no links from a redirect the crawl stopped immediately — 31 routes and 22 HTML files instead of 557 and 146, with every content page missing. The host already performs that redirect server-side, so the middleware bought nothing. > **Do not add a global trailing-slash redirect middleware here.** An earlier revision had `app/middleware/trailing-slash.global.ts` 301-redirecting bare paths to their slash form. It broke the production build outright: Nitro's prerender crawler seeds on `/en` and `/fr`, the middleware turned both into redirect responses, and since Nitro extracts no links from a redirect the crawl stopped immediately — 31 routes and 22 HTML files instead of 557 and 146, with every content page missing. The host already performs that redirect server-side, so the middleware bought nothing.
+6 -4
View File
@@ -84,10 +84,12 @@ export default defineNuxtConfig({
// path to its slash form. Back to `path/index.html` so what's on disk // path to its slash form. Back to `path/index.html` so what's on disk
// matches the URLs we advertise. // matches the URLs we advertise.
autoSubfolderIndex: true, autoSubfolderIndex: true,
// Docus only seeds `/en` and `/fr` (one per locale), so nothing is ever // Docus only seeds `/en` and `/fr` (one per locale), so neither `/` nor
// written for `/` itself and the site root 404s on a static host. This // `/robots.txt` is ever written and both 404 on a static host — `/` loses
// renders i18n's own root redirect to the default locale. // the redirect to the default locale, and robots.txt loses the `Sitemap:`
routes: ['/'], // line pointing crawlers at sitemap.xml. Both routes exist server-side,
// they just need to be prerendered.
routes: ['/', '/robots.txt'],
}, },
}, },
// Keeps <NuxtLink> hrefs (including the ones i18n's switchLocalePath builds // Keeps <NuxtLink> hrefs (including the ones i18n's switchLocalePath builds