Skip to content

quarto-nav.js's index.html clean-URL rewrite mangles alternate-format links on index pages #14667

Description

@jph00

I have:

  • searched the issue tracker for similar issues
  • installed the latest version of Quarto CLI (the live example below is built with 1.9.38; see environment notes)
  • formatted my issue following the Bug Reports guide

Bug description

quarto-nav.js rewrites every link on page load to clean up index.html URLs, stashing the original in data-original-href:

links[i].dataset.originalHref = links[i].href;
links[i].href = links[i].href.replace(/\/index\.html/, "/");

The regex is unanchored, so on a site's index page the "Other Formats" link to index.html.md (the markdown twin generated when a site pairs a markdown format with html, e.g. for llms.txt workflows) is rewritten from https://example.com/index.html.md to https://example.com/.md, which 404s. Only the index page is affected, since no other page has index.html as a filename prefix; the same CommonMark link works everywhere else on the site. The served HTML is correct; only the client-side rewrite breaks it.

Still present in main today, in src/resources/projects/website/navigation/quarto-nav.js.

The index.html.md naming comes from output-file: index.html in the page metadata (tools like nbdev set this on every page of the sites they generate).

Steps to reproduce

Primary evidence is the live example under "Actual behavior" plus the code above; a minimal project for reproducing:

_quarto.yml:

project:
  type: website

format:
  html: default
  commonmark: default

index.qmd (the output-file line gives the alternate format the index.html.md name that triggers the bug):

---
title: Home
output-file: index.html
---

Hello.

Then:

quarto render
python3 -m http.server -d _site   # the rewrite is skipped on file:, so serve over http

Open the served index page and inspect the CommonMark link under "Other Formats". Note: on 1.8.25 I could confirm the rendered HTML gets the index.html.md link, but not the client-side rewrite itself, because that version routes site_libs and the .md twin outside _site when output-file is set, so the served page loads no JS; the rewrite is confirmed on the 1.9.38-built live site below.

Actual behavior

On the live example, the link's href in the DOM is rewritten to /.md and 404s; data-original-href holds the correct index.html.md: the CommonMark link on https://fastcore.fast.ai/ (generator quarto-1.9.38) points to https://fastcore.fast.ai/.md, while the same link works on every non-index page, e.g. https://fastcore.fast.ai/tour.html.

Expected behavior

The link keeps pointing at index.html.md. The clean-URL rewrite should only strip index.html at the end of the path, while still allowing a query or fragment:

links[i].href = links[i].href.replace(/\/index\.html(?=[?#]|$)/, "/");

Your environment

  • No IDE involved: static rendered site viewed in a browser (observed in Chrome and Safari)
  • OS: macOS 26.2
  • Live example built with Quarto 1.9.38 on GitHub Actions; the unanchored regex is also in current main and in the quarto-nav.js shipped with 1.8.25

Quarto check output

Quarto 1.8.25
[✓] Checking environment information...
      Quarto cache location: /Users/jhoward/Library/Caches/quarto
[✓] Checking versions of quarto binary dependencies...
      Pandoc version 3.6.3: OK
      Dart Sass version 1.87.0: OK
      Deno version 2.3.1: OK
      Typst version 0.13.0: OK
[✓] Checking versions of quarto dependencies......OK
[✓] Checking Quarto installation......OK
      Version: 1.8.25
      Path: /Applications/quarto/bin
[✓] Checking tools....................OK
      TinyTeX: (not installed)
      Chromium: (not installed)
[✓] Checking LaTeX....................OK
      Tex:  (not detected)
[✓] Checking Chrome Headless....................OK
      Using: Chrome found on system
[✓] Checking basic markdown render....OK
[✓] Checking Python 3 installation....OK
      Version: 3.13.2
      Jupyter: 5.9.1
[✓] Checking Jupyter engine render....OK
[✓] Checking R installation...........(None)

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingllms-txt

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions