Skip to content

static:warm crashes when paginated response headers are flattened by a proxy #14910

Description

@dadaxr

Bug description

Bug: static:clear fails when X-Statamic-Pagination headers are flattened into a comma-separated value

Description

In Statamic 6.23 (and possibly earlier versions), php please static:clear can fail when warming paginated pages if the X-Statamic-Pagination response header has been normalized by a web server, proxy, or CDN.

StaticWarm::outputSuccessLine() expects Response::getHeader() to return three separate header values:

if ($response->hasHeader('X-Statamic-Pagination')) {
    [$currentPage, $totalPages, $pageName] = $response->getHeader('X-Statamic-Pagination');

    $this->warmPaginatedPages(
        $this->uris()->get($index),
        $currentPage,
        $totalPages,
        $pageName
    );
}

This works when the response contains the header three times:

X-Statamic-Pagination: 1
X-Statamic-Pagination: 8
X-Statamic-Pagination: page

In that case:

$response->getHeader('X-Statamic-Pagination');
// ['1', '8', 'page']

However, some servers and proxies combine repeated headers into a single comma-separated header value:

X-Statamic-Pagination: 1, 8, page

This produces:

$response->getHeader('X-Statamic-Pagination');
// ['1, 8, page']

As a result, destructuring fails because the returned array has only one item:

Undefined array key 1
at vendor/statamic/cms/src/Console/Commands/StaticWarm.php:176

Steps to reproduce

see bellow

Suggested fix

Normalize the header values before destructuring them.

For example, the implementation could flatten the header array and split comma-separated values:

$headers = $response->getHeader('X-Statamic-Pagination');

$pagination = collect($headers)
    ->flatMap(fn ($header) => \GuzzleHttp\Psr7\Header::splitList($header))
    ->map(trim(...))
    ->values()
    ->all();

[$currentPage, $totalPages, $pageName] = $pagination;

Using GuzzleHttp\Psr7\Header::splitList() would be safer than a basic explode(','), as it is designed to parse comma-separated HTTP header lists correctly:

https://github.com/guzzle/psr7/blob/HEAD/README.md#guzzlehttppsr7headersplitlist

At minimum, the command should normalize both multi-value and flattened header formats before accessing the pagination values.

How to reproduce

  1. Create a paginated Statamic page that emits X-Statamic-Pagination.
  2. Serve the application through a proxy, web server, or CDN that flattens repeated response headers into a comma-separated value.
  3. Run:
php please static:clear
  1. The static cache warming process fails when it encounters the paginated page.

Expected behaviour

static:clear should support both valid representations:

X-Statamic-Pagination: 1
X-Statamic-Pagination: 8
X-Statamic-Pagination: page

and:

X-Statamic-Pagination: 1, 8, page

The paginated URLs should still be discovered and warmed successfully.

Logs

Environment

Environment
Laravel Version: 12.62.0
PHP Version: 8.4.13
Composer Version: 2.8.12
Environment: local
Debug Mode: ENABLED
Maintenance Mode: OFF
Timezone: UTC
Locale: fr

Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: CACHED

Drivers
Broadcasting: log
Cache: file
Database: mariadb
Logs: stack / daily, sentry_logs
Mail: log
Queue: sync
Session: file

Storage
public/storage: NOT LINKED

Sentry
Enabled: MISSING DSN
Environment: local
Laravel SDK Version: 4.26.0
PHP SDK Version: 4.29.0
Release: NOT SET
Sample Rate Errors: 100%
Sample Rate Performance Monitoring: NOT SET
Sample Rate Profiling: NOT SET
Send Default PII: DISABLED

Statamic
Addons: 2
License Key: Set
Sites: 1
Stache Watcher: Enabled (auto)
Static Caching: half
Version: 6.23.0 PRO

Statamic Addons
statamic-rad-pack/runway: 9.5.2
statamic/seo-pro: 7.12.2

Installation

Fresh statamic/statamic site via CLI

Additional details

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions