Skip to content

Fix ISR routes using undocumented $0 token in Vercel route dest fields - #18044

Merged
matthewp merged 3 commits into
mainfrom
factory/fix-18028
Sep 17, 2026
Merged

matthewp merged 3 commits into
mainfrom
factory/fix-18028

Conversation

@astro-factory

@astro-factory astro-factory Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Changes

  • Replaces $0 with $1 in ISR route dest fields by wrapping each route's src regex in an outer capture group (e.g. ^/foo/?$ → ^(/foo/?)$). $0 is excluded from Vercel's documented substitution character class ([1-9a-zA-Z]); when the production proxy intermittently failed to substitute it, the literal string "$0" was silently accepted by the entrypoint, resolved to pathname /$0, and — with trailingSlash: 'always' — emitted a cacheable 301 redirect that poisoned the ISR cache for the full expiration window.
  • Adds a realPath.startsWith('/') guard in the entrypoint as defense in depth. Any future substitution failure now results in the request falling through to the /_isr default instead of silently rendering a nonsense path.

Testing

  • Two new tests in packages/integrations/vercel/test/isr.test.ts: 'uses $1 (not $0) in ISR route dest' verifies the generated config.json no longer contains $0; 'ignores x_astro_path that does not start with /' covers the entrypoint guard for un-substituted path values.

Docs

  • No docs update needed; this is an internal adapter implementation detail with no user-facing API change.

Closes #18028

@astro-factory astro-factory Bot added the fix verified Reporter confirmed the triage bot fix works label Sep 17, 2026
@changeset-bot

changeset-bot Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c5e9f64

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@astrojs/vercel Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

factory Bot and others added 2 commits September 17, 2026 10:26
…writes

Replace the undocumented `$0` capture reference in ISR route `dest` with
`$1` by wrapping each ISR route's `src` regex in an outer capture group.
Also validate that the path override starts with `/` in the entrypoint
as defense in depth against substitution failures.

Closes #18028
The startsWith('/') guard ignored a malformed trusted override and fell
through to the internal /_isr pathname. With a server catch-all
([...slug].astro), /_isr is a valid page, so a failing $1/$0 substitution
rendered 200 instead of failing. Return an explicit 404 before app.match()
when a trusted override channel (valid middleware secret or path token)
carries a missing or non-absolute path. Untrusted requests keep the existing
ignore-override behavior.

Also covers the improvement in test coverage:
- catch-all fixture: trusted overrides $0, $1, relative, and empty all
  return 404 with no Location header and render no catch-all page; a valid
  override still renders 200; an override with no path at all returns 404
- isr fixture: guard test now covers $0/relative/empty and asserts no
  Location header; table-driven RegExp.exec() over emitted patterns proves
  group 1 is the full pathname (static, dynamic, 404)
- spread fixture: group-1 exec assertions for root and spread patterns
@pdig-tobiasbreit

Copy link
Copy Markdown

:shipit:

The previous wording described the internal mechanism (capture group
references, $1, the entrypoint, /_isr). Per the changeset guidelines, a
patch changeset should state the user-facing effect.
@matthewp
matthewp merged commit 98c07e1 into main Sep 17, 2026
41 of 42 checks passed
@matthewp
matthewp deleted the factory/fix-18028 branch September 17, 2026 17:21
@astrobot-houston astrobot-houston mentioned this pull request Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix verified Reporter confirmed the triage bot fix works pkg: integration Related to any renderer integration (scope)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@astrojs/vercel: ISR routes can serve a cached redirect to /$0 when Vercel doesn't substitute the undocumented $0 token

2 participants