Skip to content

BASE_URL preview deploys ship a broken web app manifest: 5 hard-coded root-absolute paths 404 under a non-root baseUrl #337

Description

@hivecommons-hive

Finding

docusaurus.config.js:26-28 documents SITE_URL/BASE_URL overrides as the
supported way to deploy a non-root preview:

Override with SITE_URL/BASE_URL for non-production deployments such as GitHub
Pages previews (e.g. SITE_URL=https://castrojo.github.io BASE_URL=/endusers/).

Five paths 404 when you do that. Verified by running that exact build, not
by inference:

SITE_URL=https://castrojo.github.io BASE_URL=/endusers/ npx docusaurus build

at 00b44df, node v26.8.2, 2026-09-19.

What the build emits

Config-derived references are baseUrl-prefixed correctly:

<link data-rh=true rel=icon href=/endusers/img/favicon.ico />
"logo":"https://castrojo.github.io/endusers/img/cloud-native-end-users.svg"

The two headTags entries are emitted verbatim, with no prefix:

<link rel=manifest href=/manifest.json>
<link rel=apple-touch-icon sizes=180x180 href=/favicons/apple-touch-icon.png>

Docusaurus applies baseUrl to favicon and to fields it owns, but it does
not rewrite headTags attribute values — they are passed straight through.

And build/manifest.json is copied out of static/ byte-for-byte, because
Docusaurus performs no transformation on static/:

"start_url": "/",
"src": "/favicons/favicon.svg",
"src": "/favicons/android-chrome-192x192.png",
"src": "/favicons/android-chrome-512x512.png",

Net effect on a preview deploy

path emitted resolves to correct target
/manifest.json origin root /endusers/manifest.json
/favicons/apple-touch-icon.png origin root /endusers/favicons/...
start_url: "/" origin root /endusers/
3x icons[].src origin root /endusers/favicons/...

The manifest link 404s, so the site is not installable at all; the
apple-touch-icon 404s, so iOS home-screen saves fall back to a screenshot; and
even if the manifest were fetched, its start_url and all three icons would
404 too. On castrojo.github.io those root paths may belong to an unrelated
site, which is worse than a 404.

None of this is visible in the build output: it exits [SUCCESS], and
onBrokenLinks: 'throw' governs page routes rather than static assets.

Recommendation

Two independent edits. Both are production changes, so this issue is filed
without a PR — the quality lane is test-only and does not open production PRs.
See the note at the bottom.

1. static/manifest.json — switch to manifest-relative paths

Per the appmanifest spec,
member URLs are resolved against the manifest's own URL. Making the paths
relative fixes every baseUrl at once with no build machinery. Exact
replacement for the four affected lines:

-  "start_url": "/",
+  "start_url": ".",
...
-      "src": "/favicons/favicon.svg",
+      "src": "favicons/favicon.svg",
...
-      "src": "/favicons/android-chrome-192x192.png",
+      "src": "favicons/android-chrome-192x192.png",
...
-      "src": "/favicons/android-chrome-512x512.png",
+      "src": "favicons/android-chrome-512x512.png",

With the manifest served at /endusers/manifest.json, start_url: "."
resolves to /endusers/ and each icon to /endusers/favicons/.... At the
production root it is unchanged from today.

This is already compatible with the test landing in #335: that test asserts
manifest paths are same-origin (no scheme, no // prefix) and resolves
both relative and root-absolute forms under static/, so it stays green
before and after this edit. It was written that way deliberately so it
would not block this fix.

2. docusaurus.config.js — prefix the two headTags hrefs

 const siteUrl = process.env.SITE_URL || 'https://endusers.cncf.io';
 const baseUrl = process.env.BASE_URL || '/';
+
+// Docusaurus does not apply baseUrl to headTags attribute values; they are
+// emitted verbatim. Prefix them here so non-root preview deploys resolve.
+const withBaseUrl = (path) =>
+  `${baseUrl.replace(/\/$/, '')}/${path.replace(/^\//, '')}`;
       attributes: {
         rel: 'manifest',
-        href: '/manifest.json',
+        href: withBaseUrl('/manifest.json'),
       },
       attributes: {
         rel: 'apple-touch-icon',
         sizes: '180x180',
-        href: '/favicons/apple-touch-icon.png',
+        href: withBaseUrl('/favicons/apple-touch-icon.png'),
       },

At the default baseUrl: '/' both expressions produce exactly the strings
that are there today, so production output is byte-identical and the build
stays green.

Verifying a fix

SITE_URL=https://castrojo.github.io BASE_URL=/endusers/ npx docusaurus build --out-dir /tmp/b
grep -o '<link[^>]*manifest[^>]*>' /tmp/b/index.html   # expect href=/endusers/manifest.json
grep -o '<link[^>]*apple[^>]*>'    /tmp/b/index.html   # expect href=/endusers/favicons/...
grep -E 'start_url|"src"'          /tmp/b/manifest.json # expect relative paths

Disjointness from open PRs

Priority

  • Impact: high — every non-root preview deploy ships a wholly broken manifest and icon set, silently, via the workflow the config itself documents
  • Effort: low — four one-line JSON edits plus a three-line config helper; production output at the default baseUrl is byte-identical

Note on why there is no PR

Both edits are production changes (static/manifest.json and
docusaurus.config.js). The quality lane opens test-only PRs, so this needs a
human or a production-capable lane to land. The exact replacement text is
given above so applying it is mechanical.


Filed by quality agent (hold-gated mode)

— hive: agent=quality backend=copilot model=claude-opus-5

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/qualityApproved by a Hive merger/owner for auto-merge on green CIhive/hosted-available-lke648397-260827-5n31Approved by a Hive merger/owner for auto-merge on green CIqualityApproved by a Hive merger/owner for auto-merge on green CI

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions