Skip to content

fix: skip temporary Cache-Control header on non-cacheable pages - #1135

Open
selul wants to merge 1 commit into
developmentfrom
fix/1082-cache-header-dynamic-pages
Open

fix: skip temporary Cache-Control header on non-cacheable pages#1135
selul wants to merge 1 commit into
developmentfrom
fix/1082-cache-header-dynamic-pages

Conversation

@selul

@selul selul commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

While page profiling was pending, Optimole sent Cache-Control: max-age=300 on every not-logged-in request, and PHP's header() replaced the no-cache header WooCommerce sets on cart and checkout — so proxies could cache user-specific pages for five minutes (issue #1082). The header is now sent only when the page carries no signal against caching.

What changed

  • should_send_temporary_cache_header() — new decision guard in Optml_Manager. The header is skipped when DONOTCACHEPAGE is set, or when any Cache-Control header exists already.

  • Never override, never downgrade — before: the header replaced whatever Cache-Control was set, including WooCommerce's no-cache, must-revalidate, max-age=0 and longer-lived policies from cache plugins. After: an existing Cache-Control header always wins.

  • optml_send_temporary_cache_header filter — receives the computed decision; developers can force the header off or on contextually.

  • Cacheable pages keep the current behavior — a page with no blocking signal still gets max-age=300 while its profile is pending.

Note

Explicit is_cart()/is_checkout()/is_account_page() exclusions are not needed: WooCommerce sets both DONOTCACHEPAGE and its no-cache header at template_redirect, long before the buffer flush where Optimole runs, so both signals are honored. The same applies to EDD and membership plugins that use nocache_headers().

Header decision

flowchart LR
    A[Profiling pending<br/>for this page] --> B{New:<br/>DONOTCACHEPAGE<br/>set?}:::added
    B -- Yes --> S[Skip header]
    B -- No --> C{New:<br/>Cache-Control<br/>already set?}:::added
    C -- Yes --> S
    C -- No --> F[New:<br/>optml_send_temporary_cache_header]:::added
    F --> D[Send max-age=300]

    classDef added fill:#1a7f37,color:#fff,stroke:#116329,stroke-width:3px
Loading

QA

  1. On a connected site with WooCommerce active, clear stored profiles:

    wp transient delete --all

    Then request the checkout page as a guest:

    curl -sI https://your-site.test/checkout/ | grep -i cache-control

    Expect: Cache-Control: no-cache, must-revalidate, max-age=0 (WooCommerce's header). Without this fix, the output is Cache-Control: max-age=300.

  2. Request a plain blog post as a guest directly after clearing transients:

    curl -sI https://your-site.test/hello-world/ | grep -i cache-control

    Expect: Cache-Control: max-age=300 — the pending-profile behavior on cacheable pages is unchanged.

  3. Add add_filter( 'optml_send_temporary_cache_header', '__return_false' ); to the theme's functions.php. Repeat step 2.

    Expect: no Cache-Control: max-age=300 on any page.

🤖 Generated with Claude Code

While page profiling is pending, replace_content() sent
'Cache-Control: max-age=300' for every not-logged-in request with
headers still unsent. Because PHP's header() replaces same-name
headers, it overwrote the no-cache header WooCommerce sets on cart,
checkout and account pages, letting proxies cache user-specific pages
for five minutes.

The header is now sent only when DONOTCACHEPAGE is not set and no
Cache-Control header exists yet, so a no-cache or longer-lived policy
set by WordPress, WooCommerce or a cache plugin is never overridden.
The new optml_send_temporary_cache_header filter lets developers
override the decision in both directions.

Fixes #1082

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pirate-bot

Copy link
Copy Markdown
Collaborator

Plugin build for aab6989 is ready 🛎️!

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.

2 participants