Fixing LifterLMS 404 Errors on Courses, Lessons and the Dashboard
Work through every common cause of a LifterLMS 404: stale rewrite rules, permalink bases that clash with page slugs, unpublished or detached lessons…
Read articleCaching makes a WordPress site fast by serving a stored copy of a page instead of building it again. On a LifterLMS site, that same stored copy can show one student another student's progress, make a Mark Complete button do nothing, or break checkout. Getting the LifterLMS cache setup right is mostly about deciding what must never be cached.
This guide is for site owners and developers configuring a caching plugin, host-level cache or CDN on a LifterLMS site. It explains what LifterLMS already excludes on its own, the pages and cookies you must add, how to handle JavaScript optimisation and object caching, and how to test that it all works for a real learner.
If your goal is overall speed rather than fixing a conflict, start with our guide on how to speed up a LifterLMS site and come back here for the caching rules.
LifterLMS tells caches to stay away from three kinds of page:
On those pages it sends Cache-Control: no-cache, must-revalidate, max-age=0, no-store headers and defines the DONOTCACHEPAGE, DONOTCACHEOBJECT and DONOTCACHEDB constants that many WordPress caching plugins respect. On WP Engine, it also sets that host's own no-cache cookie for the page.
Two gaps follow from this. First, course, lesson and membership pages are not on the list, because for logged-out visitors they are perfectly cacheable. Second, any cache that ignores headers and constants, such as some server caches and CDN "cache everything" rules, ignores LifterLMS's exclusions too. Those two gaps are where nearly all conflicts come from.
For a logged-in learner, almost every LifterLMS page is personal: progress bars, completed lessons, the continue button, enrollment state, notifications. Pages also contain WordPress nonces, security tokens tied to the logged-in user that expire after about a day. Serve a cached copy and the tokens belong to someone else or have expired.
That causes the classic symptoms:
| Symptom | What the cache did |
|---|---|
| Mark Complete reloads the page and nothing changes | Served a lesson with an expired nonce, which LifterLMS silently ignores |
| A student sees another student's progress or name | Served one user's page to another |
| Logged-in students see "Enroll now" on a course they own | Served the logged-out version of the course page |
| Lesson timer never completes | Served a lesson with a stale timer session and nonce |
| Login form just reloads without logging in | Served a login form whose nonce had expired |
| Coupons vanish or checkout notices are missing | Ignored the LifterLMS session cookie |
So the first setting to check in any caching layer is the one that bypasses the cache for logged-in users. Most WordPress caching plugins do this by default; server and CDN caches often do not.
Configure every caching layer, plugin, host and CDN, with these rules:
| Rule | Why |
|---|---|
Bypass when a wordpress_logged_in_* cookie is present | All personalised learner pages |
Bypass when a wp_llms_session_* cookie is present | LifterLMS uses it for coupons and notices during checkout and browsing |
Never cache the checkout page, its confirm-payment endpoint, or URLs with a plan query string | Checkout is built per buyer and per plan |
Never cache the student dashboard page and its endpoints, such as /my-courses/ | Entirely personal |
Never cache quiz pages (the /quiz/ base by default) | Attempts and timers |
Do not cache POST requests or admin-ajax.php | Lesson timer heartbeats, quizzes and forms |
Use the actual slugs from your site: the dashboard and checkout pages can have any slug, and the bases can be changed in Settings > Permalinks.
The wp_llms_session_* cookie is only set once a visitor has something to store, so it does not stop normal catalog browsing from being cached. LifterLMS lists its cookies in its cookies FAQ.
If a caching plugin on your site respects DONOTCACHEPAGE but does not bypass logged-in users, you can tell LifterLMS to mark more pages as uncacheable. The llms_no_cache filter decides whether the current request gets no-cache treatment:
<?php
/**
* Never cache courses, lessons or memberships for logged-in users.
*/
add_filter( 'llms_no_cache', 'llmsx_no_cache_for_learners' );
function llmsx_no_cache_for_learners( $no_cache ) {
if ( $no_cache ) {
return true;
}
return is_user_logged_in() && is_singular( array( 'course', 'lesson', 'llms_membership' ) );
}
To add specific pages, such as a custom "My learning" page, use the llms_no_cache_page_ids filter, which receives the array of page IDs that already includes checkout and the dashboard. Neither filter helps with a cache that runs before WordPress loads, so server and CDN rules still matter.
Course sales pages, the catalog and membership pages are your marketing pages, and caching them for visitors is a big speed win. Two things to watch:
A CDN or host cache sits in front of WordPress and must be told about the same rules.
wordpress_logged_in_ and wp_llms_session_ cookies, and exclude checkout, dashboard and quiz paths. Cloudflare honours no-store from your origin by default on its self-serve plans, which helps with the pages LifterLMS marks itself.Performance plugins that combine, defer or delay JavaScript cause a separate class of problem. LifterLMS's front end relies on scripts for quizzes, for the lesson timer that unlocks Mark Complete and, through gateway add-ons, for payment fields at checkout. "Delay JavaScript until user interaction" is the usual culprit: the button stays disabled because the script that should enable it has not run.
Exclude LifterLMS scripts, including llms-lesson-timer, from delay and combine features, exclude your payment gateway's scripts, and test a quiz, a timed lesson and checkout after every optimisation change.
A persistent object cache such as Redis or Memcached is compatible with LifterLMS and helps busy sites. Recent releases fixed several stale-value problems: version 10.2.0 corrected cached forms, membership data, grades, subscription counts and template paths on persistent object caches, so stay current. After a migration, or if data looks out of date, flush the object cache. Our guide to object caching with Redis covers setup.
no-store, and no cache "HIT" header from your CDN or host.Yes, for logged-out visitors on course, catalog and membership pages. Bypass the cache for logged-in users and never cache checkout, the student dashboard or quizzes.
Yes, provided HTML is not cached for logged-in users. APO handles this automatically; custom cache-everything rules need cookie bypasses.
Because a cached page was being served to logged-in users. Clearing removes the bad copy until it is cached again. Fix the bypass rule instead.
Check one setting in every caching layer today: is HTML bypassed for logged-in users? Then add the checkout, dashboard and quiz exclusions and run the two-student test above.
If you want a fast academy without the conflicts, our LifterLMS optimization service configures caching, CDN and script optimisation together and tests them against real learner journeys. Contact us with your host and caching stack.
Work through every common cause of a LifterLMS 404: stale rewrite rules, permalink bases that clash with page slugs, unpublished or detached lessons…
Read article
Why LifterLMS subscriptions stop renewing and how to fix each cause: the staging lock, stalled cron and scheduled actions, gateway problems, declined…
Read article
A methodical way to find out why a LifterLMS student cannot open a course or lesson: the order LifterLMS checks access in, enrollment and order…
Read articleSkip the trial and error. Get specialist LifterLMS help and ship faster.