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:
-
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.
-
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
Which Cloudflare product(s) does this pertain to?
Workers, Pages (static assets
_redirectshandling inpackages/workers-shared)What is the issue or request?
The
_redirectsparser 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:canCreateStaticRulestartstrueand is set tofalsepermanently the first time a rule containing a splat (*) or placeholder (:name) is encountered.dynamicRules > maxDynamicRules(100), the parserbreaks and skips all remaining lines, including any catch-all at the end of the file. Theinvalidentry records this, but nothing surfaces it to the user duringwrangler 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
_redirectsfile 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:
Non-breaking: surface a warning at build/deploy time. Emit a warning from
wrangler deploy(andwrangler dev) when:The
invalidmessages already contain this information; they just are not surfaced. This changes no runtime behaviour.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:
staticRedirectsMatcher(exact-map) is evaluated beforegenerateRedirectsMatcher(ordered regex) inrules-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.versiongate ingenerateRedirectsMatcherand/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