litespeedtech / litespeedtech/lscache_wp

"Do Not Cache URIs" does not apply when "Cache REST API" is enabled

Open
#1,040 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
PHP
Stars
257
Forks
123
PR merge metrics
No merged PRs in 30d

Description

# "Do Not Cache URIs" does not apply when "Cache REST API" is enabled

## Summary

With **Cache REST API** on, every `/wp-json/*` request is marked cacheable
through a code path that never consults **Do Not Cache URIs** — so a
correctly configured exclude entry for a REST path is silently ineffective.
There's no warning about this anywhere I could find, and no REST-specific
exclude setting to fall back on.

## Where the two paths diverge (v7.9, `src/control.cls.php`)

Page requests get marked cacheable, and exclude-checked, together, both
inside `finalize()`:

```php
// finalize(), ~line 758
if ( ! $this->_setting_cacheable() ) {
self::set_nocache();
return;
}
```

`_setting_cacheable()` (private, ~line 833) is what walks `O_CACHE_EXC`
("Do Not Cache URIs"), among other admin-configured exclude settings.

REST requests take a completely different, earlier path:

```php
// init_cacheable(), ~line 146
if ( $this->conf( Base::O_CACHE_REST ) ) {
add_action( 'rest_api_init', [ $this, 'set_cacheable' ], 5 );
}
```

and `set_cacheable()` (public, ~line 380) does nothing but set an internal
bitmask flag:

```php
public function set_cacheable( $reason = false ) {
self::$_control |= self::BM_CACHEABLE;
...
}
```

No call to `_setting_cacheable()`, no URI check of any kind. `rest_api_init`
fires well before `finalize()`'s hook point in the request lifecycle
(finalize runs against the rendered output buffer late in the page
lifecycle; REST responses never go through that buffer at all), so a REST
request is marked cacheable and never revisits the exclude list afterward.

I grepped the whole plugin for any REST-scoped exclude option and found
none — `O_CACHE_EXC` and its siblings (`_cat`, `_tag`, `_qs`, `_cookies`,
`_useragents`, `_roles`) are the only exclude mechanisms that exist, and all
of them are wired exclusively through `_setting_cacheable()`.

## Impact

Any REST endpoint whose correct response depends on something outside the
URL — a session cookie, an Authorization header, a nonce — can get cached
and served to the wrong requester once "Cache REST API" is on, regardless of
what's in "Do Not Cache URIs". We hit this concretely: an OAuth
authorization endpoint (`/wp-json/mcp/v1/oauth/authorize`, from a separate
plugin — AI Engine) got cached publicly even with that exact path present
in "Do Not Cache URIs", because the request never reached the code that
checks that list. Reproduced live: registered a client, hit the authorize
URL twice — first request `x-litespeed-cache: miss`; second request,
*same URL*, `x-litespeed-cache: bkd` (a hit), byte-identical body, ~10x
faster. `Cache-Control: public, max-age=604800` on a response whose
correctness depends entirely on the requester's login cookie.

## Suggested fix

Either have `set_cacheable()`'s `rest_api_init` registration also run (or be
gated by) the same exclude checks `_setting_cacheable()` performs — request
URI, query string, cookies, user-agent, roles — or add a REST-specific
exclude list in the "Do Not Cache URIs" section (or a separate setting) and
document that the existing one doesn't cover REST requests.

## Environment

- LiteSpeed Cache 7.9, WordPress + LiteSpeed webserver, QUIC.cloud CDN edge
- Reproduced on 3 separate installs of the same hosting account, all with
the exact same "Do Not Cache URIs" configuration (`/wp-json/mcp/*`,
`/.well-known/*`, plus two unrelated site-specific paths)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/control.cls.php by comparing init_cacheable() and set_cacheable() with finalize() and _setting_cacheable(), focusing on the rest_api_init path and the O_CACHE_EXC checks. Reproduce the reported /wp-json/mcp/v1/oauth/authorize behavior, then verify that a configured REST URI exclusion prevents the request from being marked cacheable without regressing ordinary page caching.

Written by the indexing model from the issue text.

Assessment

Tech stack
php, wordpress
Domain
api, backend, performance, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.