Have you read the Contributing Guidelines on issues?
Prerequisites
Description
With a non-root baseUrl and trailingSlash: true, docusaurus serve returns 404 for two kinds of static assets:
- extensions longer than 4 characters —
.woff2, .webmanifest, .geojson
- any asset requested with a query string —
/img/logo.png?v=1
Both get a 302 to a trailing-slash URL and end up on the 404 page. Web fonts are the most visible casualty, since .woff2 is the standard format.
It comes from the asset check in packages/docusaurus/src/commands/serve.ts:
const looksLikeAsset = !!req.url.match(/\.[a-zA-Z\d]{1,4}$/);
{1,4} is too short for .woff2, and the $ is matched against the raw req.url, so a ?query also prevents the match. applyTrailingSlash(), called right after it, does split the query off first:
// The trailing slash should be handled before the ?search#hash !
const [pathname] = path.split(/[#?]/) as [string, ...string[]];
The asset check just misses that step.
Reproducible demo
No repo needed — the steps below start from a fresh create-docusaurus install and reproduce it in about a minute.
Steps to reproduce
npx create-docusaurus@latest repro-serve classic --javascript && cd repro-serve && npm install
- In
docusaurus.config.js, set baseUrl: '/mysite/' and trailingSlash: true
- Add three static files:
printf 'FAKEWOFF2' > static/custom-font.woff2
printf '{"name":"x"}' > static/site.webmanifest
printf 'FAKEPNG' > static/logo-test.png
npm run build && npm run serve -- --port 3030
- Request them:
curl -sI "http://localhost:3030/mysite/logo-test.png"
curl -sI "http://localhost:3030/mysite/logo-test.png?v=1"
curl -sI "http://localhost:3030/mysite/custom-font.woff2"
curl -sI "http://localhost:3030/mysite/site.webmanifest"
Expected behavior
All four serve the file with status 200 and the correct content type. The trailing slash should only be applied to page URLs, whatever the extension length is and whether or not there is a query string.
Actual behavior
Only the plain .png is served:
/mysite/logo-test.png -> 200 image/png
/mysite/logo-test.png?v=1 -> 302 .../logo-test.png/?v=1 -> 404 text/html
/mysite/custom-font.woff2 -> 302 .../custom-font.woff2/ -> 404 text/html
/mysite/site.webmanifest -> 302 .../site.webmanifest/ -> 404 text/html
Following the redirect returns the HTML 404 page instead of the file:
$ curl -sIL "http://localhost:3030/mysite/custom-font.woff2" | grep -E "^HTTP|^Content-Type"
HTTP/1.1 302 Found
HTTP/1.1 404 Not Found
Content-Type: text/html; charset=utf-8
Same behavior on main (4.0.0), where the regex is /\.[a-z\d]{1,4}$/i.
Splitting off the query/hash and checking the extension of the pathname fixes all three cases while leaving page redirects untouched:
const [pathname] = req.url.split(/[#?]/) as [string, ...string[]];
const looksLikeAsset = !!path.extname(pathname);
With that change, in the same repro, the three failing URLs return 200 and /mysite/docs/intro still redirects to /mysite/docs/intro/.
Your environment
- Public source code: N/A — repro steps above, from a fresh
create-docusaurus site
- Public site URL: N/A — local
docusaurus serve
- Docusaurus version used: 3.10.2, also reproduced on
main (4.0.0)
- Environment name and version: Node.js v24.21.0
- Operating system and version: macOS 15
Self-service
Have you read the Contributing Guidelines on issues?
Prerequisites
npm run clearoryarn clearcommand.rm -rf node_modules yarn.lock package-lock.jsonand re-installing packages.Description
With a non-root
baseUrlandtrailingSlash: true,docusaurus servereturns 404 for two kinds of static assets:.woff2,.webmanifest,.geojson/img/logo.png?v=1Both get a 302 to a trailing-slash URL and end up on the 404 page. Web fonts are the most visible casualty, since
.woff2is the standard format.It comes from the asset check in
packages/docusaurus/src/commands/serve.ts:{1,4}is too short for.woff2, and the$is matched against the rawreq.url, so a?queryalso prevents the match.applyTrailingSlash(), called right after it, does split the query off first:The asset check just misses that step.
Reproducible demo
No repo needed — the steps below start from a fresh
create-docusaurusinstall and reproduce it in about a minute.Steps to reproduce
npx create-docusaurus@latest repro-serve classic --javascript && cd repro-serve && npm installdocusaurus.config.js, setbaseUrl: '/mysite/'andtrailingSlash: truenpm run build && npm run serve -- --port 3030Expected behavior
All four serve the file with status 200 and the correct content type. The trailing slash should only be applied to page URLs, whatever the extension length is and whether or not there is a query string.
Actual behavior
Only the plain
.pngis served:Following the redirect returns the HTML 404 page instead of the file:
Same behavior on
main(4.0.0), where the regex is/\.[a-z\d]{1,4}$/i.Splitting off the query/hash and checking the extension of the pathname fixes all three cases while leaving page redirects untouched:
With that change, in the same repro, the three failing URLs return 200 and
/mysite/docs/introstill redirects to/mysite/docs/intro/.Your environment
create-docusaurussitedocusaurus servemain(4.0.0)Self-service