Skip to content

[6.x] Fix: statamic:static:warm drops --header values on paginated pages - #15278

Open
steveparks wants to merge 3 commits into
statamic:6.xfrom
steveparks:patch-2
Open

[6.x] Fix: statamic:static:warm drops --header values on paginated pages#15278
steveparks wants to merge 3 commits into
statamic:6.xfrom
steveparks:patch-2

Conversation

@steveparks

Copy link
Copy Markdown
Contributor

statamic:static:warm attaches --header values to its main request pass:

// requests(), line ~221
return new Request('GET', $uri, $headers);

but warmPaginatedPages() — which follows an index page's X-Statamic-Pagination header to warm pages 2..n builds its requests without them:

// warmPaginatedPages(), line ~136
$requests = $urls->map(fn (string $url) => new Request('GET', $url))->all();

So a warm run with --header "X-My-Token: …" identifies /tags to whatever is in front of the origin and does not identify /tags?page=2. Anything keyed on that header (eg a CDN/WAF skip rule, an origin allowlist, basic-auth-by-header on a staging site) applies to page 1 of every paginated URL and nothing else.

The fix

Parse the headers the same way the main pass does and pass them into the paginated requests.

No behaviour change for anyone not passing --header.

Note

I'm an inexperienced contributor. I've tried to make sure I follow the contributor guide, but please do point out anything I missed or could improve on.

`statamic:static:warm` attaches `--header` values to its main request pass:

```php
// requests(), line ~221
return new Request('GET', $uri, $headers);
```

but `warmPaginatedPages()` — which follows an index page's `X-Statamic-Pagination` header to warm pages 2..n — builds its requests without them:

```php
// warmPaginatedPages(), line ~136
$requests = $urls->map(fn (string $url) => new Request('GET', $url))->all();
```

So a warm run with `--header "X-My-Token: …"` identifies `/tags` to whatever is in front of the origin and does **not** identify `/tags?page=2`. Anything keyed on that header — a CDN/WAF skip rule, an origin allowlist, basic-auth-by-header on a staging site — applies to page 1 of every paginated URL and nothing else.

This is invisible until the thing in front of the origin starts acting on unidentified traffic, at which point every paginated page silently fails to cache while the command still reports success (the follow-up pool's rejections are printed, but see the second bug below, which makes them name the wrong URL).

In my case, on a site behind Cloudflare with a skip rule matching the warm's header: in a 15-hour window where bot protection was challenging automated traffic, the origin's own warm took 1,782 managed challenges against 4,191 skips — one client, one user-agent, split purely by whether a request was a pagination follow-up. Guzzle cannot pass a managed challenge, so none of those pages entered the cache.

### The fix

Parse the headers the same way the main pass does and pass them into the paginated requests.

### Notes

- No behaviour change for anyone not passing `--header`.
@steveparks steveparks changed the title Fix: statamic:static:warm drops --header values on paginated pages [6.x] Fix: statamic:static:warm drops --header values on paginated pages Aug 26, 2026
@steveparks

Copy link
Copy Markdown
Contributor Author

My first fail was the title - have fixed that.
Second fail is with code style for the diff. Not sure how to fix that — I simply created it using the Github.com UI workflow, creating a fork, making an edit, creating a commit and then a PR. Sorry

@duncanmcclean

Copy link
Copy Markdown
Member

Looks like indentation is throwing off the linter:

CleanShot 2026-08-26 at 15 18 45

No worries if you can't fix it. We can do it when we get around to reviewing this.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants