.agents/skills/learn-site-structure/redirects.md
Learn supports four redirect mechanisms layered in precedence order. Most page moves and renames produce automatic redirects. Page deletions require manual surgery.
| # | Mechanism | Precedence | Authoritative? | Where configured |
|---|---|---|---|---|
| 1 | Netlify [[redirects]] (in netlify.toml) | edge -- before SPA loads | YES | Generated end-to-end by ingest/autogenerateRedirects.main each ingest |
| 2 | Docusaurus @docusaurus/plugin-client-redirects | client-side, after SPA loads | client-only | docusaurus.config.js:167-178 |
| 3 | React root redirect | client-side fallback | hand-coded | ${NETDATA_REPOS_DIR}/learn/src/pages/index.js:1-6 (/ -> /docs/ask-nedi) |
| 4 | Frontmatter redirect_from | client-side, Docusaurus-native | NOT used | grep yields zero hits in docs/ |
So mechanism 1 is the workhorse. Mechanisms 2 and 3 are limited and supplemental. Mechanism 4 is theoretically supported but not used here.
${NETDATA_REPOS_DIR}/learn/netlify.toml is REGENERATED end
to end by ingest/autogenerateRedirects.main
(autogenerateRedirects.py:172-213) on every ingest run. It
has two sections; both are regenerated each ingest:
Static section (# section: static << START / END) --
copied verbatim from
${NETDATA_REPOS_DIR}/learn/static.toml (hand-curated).
Currently 28 rules covering legacy /docs/agent/... and
/docs/nightly/... paths, the /guides -> /docs
migration, and a handful of category remaps (e.g.
kubernetes-k8s-netdata -> /docs/collecting-metrics/kubernetes).
Dynamic section (# section: dynamic << START / END)
-- built from
${NETDATA_REPOS_DIR}/learn/LegacyLearnCorrelateLinksWithGHURLs.json
joined with the just-finished ingest's
(GH URL -> Learn URL) map. Currently many hundreds of
rules; the file is ~12,700 lines.
Both netlify.toml (the live published file) and
LegacyLearnCorrelateLinksWithGHURLs.json are committed to
git on each ingest PR.
When a node moves in <repo>/docs/.map/map.yaml (or its
meta.label changes -- which changes the slug), the diff-based
addMovedRedirects (autogenerateRedirects.py:124-155):
(custom_edit_url -> new_learn_path)
mapping.ingest/one_commit_back_file-dict.yaml.custom_edit_url whose target moved, adds
https://learn.netdata.cloud<old_path> -> <github_blob_url>
to the redirect set.append_entries_to_json writes the new entries to
LegacyLearnCorrelateLinksWithGHURLs.json.UpdateGHLinksBasedOnMap reads ALL entries from
LegacyLearnCorrelateLinksWithGHURLs.json (legacy + just
appended), looks up their current Learn target via the
mapping, and folds them into the dynamic-redirect
section.The redirect store is anchored to the GitHub blob URL of the
source file, NOT to the old Learn URL directly. So a moved
page's old URL keeps redirecting forever, even after several
subsequent moves -- as long as the source file still exists
somewhere in map.yaml. Each ingest re-resolves
GH URL -> current Learn URL via the live mapping.
If the source file is deleted (the GH URL no longer points to real content), the redirect resolves to a missing URL and serves a 404 unless someone manually edits the JSON. See the delete recipe.
${NETDATA_REPOS_DIR}/learn/docusaurus.config.js:167-178:
redirects: [{ from: '/docs/ask-netdata', to: '/docs/ask-nedi' }]
Currently one entry. These are client-side redirects rendered as static HTML stubs by the Docusaurus build. Used for in-app links the SPA might encounter.
${NETDATA_REPOS_DIR}/learn/src/pages/index.js:1-6 -- the home
page is a React component that redirects / -> /docs/ask-nedi.
This is the LAST FALLBACK for the homepage. If you remove or
rename ask-nedi.mdx (which is part_of_learn: True and
otherwise survives ingest), you break the site root.
redirect_from -- not usedDocusaurus supports redirect_from in frontmatter. A grep
across ${NETDATA_REPOS_DIR}/learn/docs/ returns zero hits.
Don't use this mechanism here -- it's not part of the
established conventions.
Server-then-client:
A redirect from one mechanism cannot override a redirect in a
higher-priority mechanism. So an /docs/old-page URL caught by
the dynamic Netlify section never reaches the Docusaurus
client redirect plugin.
The diff-based mechanism handles it automatically:
<repo>/docs/.map/map.yaml.master.LegacyLearnCorrelateLinksWithGHURLs.json
and netlify.toml.Same as a move (the URL slug is derived from sidebar_label,
so changing meta.label is functionally a move).
The author MUST manually update
${NETDATA_REPOS_DIR}/learn/LegacyLearnCorrelateLinksWithGHURLs.json
per <repo>/docs/.map/README.md:96-104. The recipe:
.md and remove the matching node from
map.yaml. Open the docs PR.LegacyLearnCorrelateLinksWithGHURLs.json in the
learn repo.static.toml under
# section: static for hard-coded one-off redirects.Without this manual step, the redirect resolves to a missing
URL and serves a 404. See recipes/delete-doc-page.md.
Each ingest run commits two files to the learn-repo PR:
netlify.toml (always regenerated end-to-end).LegacyLearnCorrelateLinksWithGHURLs.json (appended to;
preserves legacy entries, adds new diff entries).Both are inspected during the manual review of the
Ingest New Documentation PR.
static.toml redirectsThe hand-curated static section of static.toml is for
one-off redirects that the diff mechanism wouldn't catch:
/docs/agent/... ->
current paths)./docs/collecting-metrics/kubernetes-k8s-netdata ->
/docs/collecting-metrics/kubernetes)./guides -> /docs migration.Edit static.toml directly to add a static redirect; it gets
copied into netlify.toml on the next ingest run.
LegacyLearnCorrelateLinksWithGHURLs.json. The
dynamic redirect section keeps growing -- already ~12,700
lines. Netlify's redirect-rule limit is ~10,000 rules per
site. The repo is approaching/past that ceiling. Not noted
anywhere in the live code or docs. Followup item.daily-learn-link-check.yml) will flag it, but only after
publication.slug: in frontmatter, that value wins. Renaming the file
or moving its map.yaml row does NOT change the URL, so no
redirect is generated. To rename a slug-override page, edit
the slug AND ensure the redirect catalog gets a manual
entry.