Skip to content

altdoc-multiversion-docs: version directories have no canonical URLs (duplicate content across /dev/, /latest-tag/, /vX.Y.Z/) #332

Description

@d-morrison

altdoc-multiversion-docs.yml deploys the same documentation to several paths (from its own header comment, lines 6-8):

  • /dev/ — default-branch docs
  • /latest-tag/ — current stable
  • /vX.Y.Z/ — one directory per release, kept as an archive
  • /pr-preview/pr<N>/ — per-PR previews

Every one of those serves near-identical HTML. The only <link rel="canonical"> this repo emits is on the root redirect stub, in generate_multiversion_landing_page.py:34 — a one-line page that immediately bounces to the target. Nothing inside the rendered version directories carries one.

So a search engine indexing one of these sites sees N copies of every page with no signal about which is authoritative. The usual outcomes are the archive or /dev/ outranking /latest-tag/, and a reader landing on documentation for a version they are not using.

The fix, and where the idea came from

IndrajeetPatil/workflows/.github/workflows/pkgdown.yaml (MIT) solves this for pkgdown with an inline R script that injects and then verifies a canonical tag in every generated page. Two things about it are worth copying beyond the idea itself:

  1. It fails loudly. It stop()s if the pkgdown config is missing, if url is absent or not ^https://, if zero HTML files were produced, or if the insertion point (<title>, falling back to <head>) does not exist.
  2. It re-scans afterwards and asserts exactly one canonical tag per indexable page, aborting with the list of offending files. 404.html is excluded from the indexable set, and pages that already have a canonical are skipped rather than double-tagged.

That verification pass is the part that matters here: a wrong canonical is silent — the page still renders fine — so this is a check that has to be mechanical rather than eyeballed once and trusted.

Note the irony worth stating plainly: upstream needs this for a single-version site, where the duplicate-content problem is much milder than ours. We have four concurrent copies and no canonicals at all.

What to decide

  • Target. Point every version directory's canonical at the /latest-tag/ equivalent of the same page, so stable is authoritative. Pages that exist only in /dev/ (a newly documented function) have no /latest-tag/ counterpart — decide whether those self-canonicalize or are left untagged.
  • PR previews. These should almost certainly be noindex rather than canonicalized; worth handling in the same pass.
  • Where it runs. Probably a new step in the composite family alongside generate-altdoc-landing-page, reusing resolve-altdoc-base-url for the base URL rather than deriving it a second way.
  • Whether Quarto can do it natively. Check site-url handling before writing a post-processor — if Quarto emits canonicals from config, the fix may be configuration plus verification rather than HTML rewriting.

Source: IndrajeetPatil/workflows, MIT.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions