Skip to content

Prevent page caches from storing content-negotiation redirect#31

Open
TBarregren wants to merge 1 commit intoProgressPlanner:mainfrom
TBarregren:fix/cache-negotiation-redirect
Open

Prevent page caches from storing content-negotiation redirect#31
TBarregren wants to merge 1 commit intoProgressPlanner:mainfrom
TBarregren:fix/cache-negotiation-redirect

Conversation

@TBarregren
Copy link

Summary

  • Full-page caches (LiteSpeed Cache, WP Super Cache, W3TC, etc.) ignore the Vary: Accept header and store the 303 redirect from handle_accept_negotiation() keyed by URL alone
  • All subsequent visitors get redirected to the .md URL instead of seeing the HTML page
  • This adds three cache-prevention layers before the redirect to cover the entire WordPress caching ecosystem

The Fix

Three complementary layers added before the redirect in handle_accept_negotiation():

Layer Target Coverage
DONOTCACHEPAGE constant WP Super Cache, W3TC, LiteSpeed Cache, and most WP page cache plugins Universal WordPress convention
Cache-Control: private, no-store CDNs, reverse proxies (Varnish, Nginx), browser cache HTTP standard
do_action('litespeed_control_set_nocache') LiteSpeed Cache specifically LSCWP's own API; safe no-op if inactive

The .md URLs themselves remain fully cacheable — only the content-negotiation redirect (which depends on the Accept header) is excluded from caching.

Test Plan

  • Normal HTML request returns 200 (no redirect):
    curl -sI https://example.com/some-page/
  • Accept: text/markdown returns 303 with Cache-Control: private, no-store:
    curl -sI -H "Accept: text/markdown" https://example.com/some-page/
  • .md URL returns 200 with text/markdown content type:
    curl -sI https://example.com/some-page.md

Fixes #30
Relates to PITFALLS.md Pitfall #5 ("Content Negotiation Interferes with Caching")

🤖 Generated with Claude Code

Full-page caches (LiteSpeed Cache, WP Super Cache, etc.) ignore
the Vary: Accept header and cache the 303 redirect keyed by URL
alone, causing all visitors to be redirected to the .md URL.

Add three cache-prevention layers before the redirect:
- DONOTCACHEPAGE constant (universal WP cache convention)
- Cache-Control: private, no-store (HTTP standard for proxies/CDNs)
- litespeed_control_set_nocache action (LSCWP API, no-op if inactive)

The .md URLs themselves remain fully cacheable.

Fixes ProgressPlanner#30

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdevalk
Copy link
Member

jdevalk commented Feb 27, 2026

Hmm this solution would prevent caching everywhere, including in solutions that do understand they should... vary cache, by Vary.

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.

Page caches store Accept-header 303 redirect and serve it to all visitors

2 participants