Paginator maxLimit clamping behavior when clients request oversized page sizes
0 reputation · 08 Jun 2022, 20:36 UTC
0 reputation · 08 Jun 2022, 20:36 UTC
I'm designing an API-style index endpoint on CakePHP that exposes pagination to clients through query string parameters, using the Paginator component's limit and maxLimit settings. The goal is to let clients pick a page size up to a hard ceiling, while guaranteeing that a crafted request with a very large limit value cannot force an unbounded result set.
My understanding is that limit sets the default page size and maxLimit caps what a client can request, with values above the cap being clamped rather than honored. What I'm unsure about is the exact fallback when maxLimit is not configured at all: I believe there is a framework default cap (historically 100), but I have not confirmed whether that default still applies in current 4.x/5.x releases or whether omitting it changes the clamping behavior.
I'm also unclear how per-call options passed to paginate() interact with the controller-level $paginate property when only one of them defines maxLimit.
Specifically: (1) Is the clamping silent, or is there any signal in the paging metadata that the requested limit was reduced? (2) What is the documented default cap when maxLimit is unset in CakePHP 5.x? (3) Do per-call options fully replace the controller defaults for pagination, or are they merged key by key?
35275 reputation · 09 Jun 2022, 07:25 UTC
In CakePHP 4.x and 5.x, the Paginator component implements silent clamping. If a client requests a limit via query parameters that exceeds the defined maxLimit, the framework automatically reduces the limit to the maxLimit value before executing the query. There is no built-in signal, exception, or metadata flag in the paging object to notify the client that their requested limit was capped; the response simply contains the maximum allowed number of records.
When maxLimit is not explicitly configured in your controller or global settings, CakePHP typically defaults to a cap of 100. This prevents accidental or malicious requests from triggering unbounded result sets that could exhaust server memory.
Options passed directly to the paginate() method are merged with the controller-level $paginate property. This merge happens on a per-key basis, meaning method-level options take precedence over property-level defaults. For example, if your controller defines a maxLimit of 50, but you pass ['maxLimit' => 100] into the paginate() call, the limit for that specific request will be 100.
To confirm this behavior in your specific environment, you can use the following tests:
?limit=1000 on an endpoint where maxLimit is unset. Inspect the resulting Paging object or the executed SQL LIMIT clause to confirm it is capped at 100.$this->paginate = ['limit' => 10]; in your controller, then call $this->paginate(['limit' => 20]);. The resulting page size should be 20.maxLimit to 25 and request ?limit=50. The result set should contain exactly 25 records.Diagnostic Note: Are you using a custom Paging class or a modified Paginator component? Custom implementations may override the default clamping logic or the standard 100-record cap.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.