Skip to content

fix(react-router-serve): serve /.well-known files - #15340

Open
kklem0 wants to merge 1 commit into
remix-run:mainfrom
kklem0:serve-well-known
Open

fix(react-router-serve): serve /.well-known files#15340
kklem0 wants to merge 1 commit into
remix-run:mainfrom
kklem0:serve-well-known

Conversation

@kklem0

@kklem0 kklem0 commented Jul 22, 2026

Copy link
Copy Markdown

Problem

Express 5's static middleware ignores every dot-segment path by default — send v1 dropped v0's allowance for dot directories when the deprecated hidden option was removed. Since react-router-serve uses express.static with default options, any file under /.well-known/ now falls through all static mounts into the request handler, which responds with app-rendered HTML (or a 404 route) instead of the file.

/.well-known is the RFC 8615 home for cross-cutting platform files, so this silently breaks real deployments:

  • Android App Links (/.well-known/assetlinks.json)
  • iOS Universal Links / associated domains (/.well-known/apple-app-site-association)
  • ACME HTTP-01 challenges (Let's Encrypt)
  • security.txt, OIDC discovery, and the rest of the well-known registry

We hit this in production: our Android assetlinks.json verification received an HTML page. Related friction with .well-known requests reaching the router has come up before in #13516 and #15337 (dev-server side). And because react-router-serve intentionally exposes no options, affected apps can't work around it without ejecting to a custom Express server.

Fix

Mount the client build's .well-known directory explicitly, after the existing static mounts and before the request handler:

app.use(
  "/.well-known",
  express.static(path.join(build.assetsBuildDirectory, ".well-known")),
);

Only the .well-known directory is exposed — all other dotfiles remain hidden, matching what sirv (used by Vite preview) does by default (lukeed/sirv#50). This restores the Express 4-era behavior for the standardized directory without reintroducing broad dot-directory serving.

Testing

Added integration tests in integration/react-router-serve-test.ts for both the classic and RSC Framework Mode templates, run against the real spawned react-router-serve process:

  • /.well-known/security.txt (placed in public/) is served with a 200
  • other dotfiles (e.g. /.hidden.txt) still fall through and 404

All existing tests in the file pass, along with tsc for the package and prettier --check on touched files.

Express 5's static middleware ignores every dot-segment path by default
(send v1 dropped send v0's allowance for dot directories), so RFC 8615
well-known URIs — ACME challenges, Android's assetlinks.json, Apple's
apple-app-site-association — fell through to the request handler and
came back as app-rendered HTML instead of the static file. Mount the
.well-known directory of the client build explicitly; other dotfiles
remain hidden.
Comment thread packages/react-router-serve/cli.ts
@kklem0
kklem0 requested a review from timdorr July 24, 2026 08:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants