litespeedtech / litespeedtech/lscache_wp
"Do Not Cache URIs" does not apply when "Cache REST API" is enabled
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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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