Skip to content

_redirects: dynamic-rule overflow silently drops rest of file; static/dynamic classification is order-dependent and unsurfaced #14694

Description

@MattieTK

Which Cloudflare product(s) does this pertain to?

Workers, Pages (static assets _redirects handling in packages/workers-shared)

What is the issue or request?

The _redirects parser silently drops the remainder of a file once the dynamic-rule budget is exceeded, and the static/dynamic classification depends on rule ordering in a way that is not surfaced to the user at build or deploy time.

In parseRedirects.ts:

  • canCreateStaticRule starts true and is set to false permanently the first time a rule containing a splat (*) or placeholder (:name) is encountered.
  • After that point, every subsequent rule — including exact-path rules that would otherwise be static — is counted against the 100 dynamic-rule budget.
  • When dynamicRules > maxDynamicRules (100), the parser breaks and skips all remaining lines, including any catch-all at the end of the file. The invalid entry records this, but nothing surfaces it to the user during wrangler deploy.

The documented limit of "2,000 static + 100 dynamic" is therefore only achievable if the entire file is ordered static-first. A single dynamic rule placed early collapses the effective static budget, and a file with more than 100 rules after the first dynamic rule loses its tail with no visible error.

This was reported publicly here: https://x.com/samuelcolvin/status/2076993431818084700 — a large _redirects file 404'd everything after the point where the dynamic budget overflowed, including the catch-all, with no build-time signal.

Requested changes

Two options, not mutually exclusive:

  1. Non-breaking: surface a warning at build/deploy time. Emit a warning from wrangler deploy (and wrangler dev) when:

    • the dynamic-rule limit is exceeded and lines are skipped, or
    • static rules appear after a dynamic rule (so they are being counted as dynamic).

    The invalid messages already contain this information; they just are not surfaced. This changes no runtime behaviour.

  2. Behavioural fix, gated behind a compatibility date / redirects.version. Classify rules by their own shape (splat/placeholder → dynamic, otherwise static) regardless of position, so exact-path rules after a dynamic rule are counted and matched as static.

    This is a breaking change and should not apply to already-deployed configs:

    • Previously-dropped rules would start firing.
    • Precedence would change: staticRedirectsMatcher (exact-map) is evaluated before generateRedirectsMatcher (ordered regex) in rules-engine.ts, so reclassifying an exact-path rule that currently sits after a splat would move it ahead of that splat and can change which redirect wins for overlapping paths — even in files that never approach the limit.

    The existing redirects.version gate in generateRedirectsMatcher and/or a compatibility date would let new deployments opt into the new semantics while existing deployments keep current behaviour.

Docs

The current behaviour is being documented in cloudflare-docs (the _redirects "Per file" section) so that the ordering requirement and the silent-drop behaviour are explicit in the meantime: cloudflare/cloudflare-docs#32083

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature:workers-assetsRelating to Cloudflare Workers Assets: https://developers.cloudflare.com/workers/static-assets/package:workers-sharedRelating to the `@cloudflare/workers-shared` package (asset-worker & router-worker)product:pagesRelating to Cloudflare Pages: https://pages.cloudflare.com/

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions