cloudflare / cloudflare/vinext

Streaming metadata/PPR: match HTML-limited bots as substrings of full user-agent (anchored bypass rule)

Open
#2,815 1 comment 0 reactions 0 assignees View on GitHub
nextjs-tracking
Dominant language
TypeScript
Stars
8.8k
Forks
406
Avg merge
2d 6h
Merged PRs (30d)
120

Description

## Next.js Change

**Commit:** [`d87a203`](https://github.com/vercel/next.js/commit/d87a203bccb292e7717c4b956069f73150e44eca)
**PR:** [#96584](https://github.com/vercel/next.js/pull/96584)

## What changed

Fixes HTML-limited bot detection for the PPR / streaming-metadata **cache-bypass** rule. When Cache Components / PPR is enabled, Next.js generates a route `has` matcher on the `user-agent` header that selectively bypasses the static prerender shell (and blocks streamed metadata) for HTML-limited bots.

The generated matcher value was the raw bot-substring pattern (either `config.htmlLimitedBots` or the built-in `HTML_LIMITED_BOT_UA_RE_STRING`). But route `has` matchers **anchor** header values (`^...$`), so a full user-agent string that merely *contains* a bot token (e.g. `googleweblight` embedded in a real Chrome/Android UA, or a custom `MyBot` inside `Mozilla/5.0 (compatible; MyBot; ...)`) would fail to match and would incorrectly be served the streamed/PPR shell instead of the bot-appropriate blocking response.

The fix wraps the bot pattern in surrounding wildcards so anchored matching preserves `RegExp.test()` substring semantics:

```ts
// packages/next/src/build/index.ts
const htmlLimitedBotsRegexString =
config.htmlLimitedBots || HTML_LIMITED_BOT_UA_RE_STRING
// Route `has` matchers anchor header values. Add surrounding
// wildcards to preserve RegExp.test() substring semantics for
// complete user-agent strings.
const htmlLimitedBotsBypassRegexString = `.*(?:${htmlLimitedBotsRegexString}).*`

// ...used as the `has` header matcher value:
{
type: 'header',
key: 'user-agent',
value: htmlLimitedBotsBypassRegexString, // was: htmlLimitedBotsRegexString
}
```

So the generated matcher becomes `.*(?:...bot patterns...).*` instead of the bare alternation.

## Impact on vinext

vinext reimplements PPR / streaming-metadata bot detection. Any place vinext decides whether to block/stream metadata (or bypass the prerender shell) based on the `user-agent` against the HTML-limited bots list must use **substring** matching against the full incoming user-agent, not an anchored/exact match of the bot-token pattern alone.

What to check/do:

1. **Match full user-agent strings.** When testing the incoming `user-agent` against `htmlLimitedBots` (or the default HTML-limited bot pattern), ensure bots embedded anywhere in the UA string match — e.g. `googleweblight` inside a full mobile UA, or a configured `MyBot` inside `Mozilla/5.0 (compatible; MyBot; +https://example.com/bot)`.
2. **If vinext emits an anchored `has`-style matcher** for this bypass rule (mirroring Next.js's routes-manifest generation), wrap the pattern as `.*(?:).*` so anchored matching still behaves as a substring test.
3. **Honor the `htmlLimitedBots` config option** the same way — the wildcard wrapping applies to both the custom pattern and the built-in default.
4. **Port the tests.** Next.js added coverage:
- full-UA `googleweblight` blocks metadata (default bot list)
- configured custom bot within a full UA blocks metadata
- routes-manifest snapshot asserting the matcher value is `.*(?:).*`, and that the anchored matcher matches a full googleweblight UA but not a plain `Mozilla/5.0`.

## Notes

- Relevant only once vinext implements streaming metadata / PPR bot handling (the `htmlLimitedBots` cache-bypass path). This is a matching-semantics correctness fix, not a new feature.
- The bug was silent: bots with tokens embedded in a longer UA (the common real-world case) were not detected, so they'd receive the streamed shell instead of the blocking metadata response.

Contributor guide

Open the contributing guide

Research direction

Start by locating vinext's streaming-metadata/PPR bot-handling entry point and its htmlLimitedBots matching, then compare the intended behavior with packages/next/src/build/index.ts. Add coverage for default and configured bots embedded in full user-agent strings, plus the routes-manifest matcher behavior described in the issue. Done means embedded bot tokens match while a plain Mozilla/5.0 user-agent does not.

Written by the indexing model from the issue text.

Assessment

Tech stack
next.js, typescript
Domain
backend, web-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.