Forum Replies Created

Viewing 6 replies - 1 through 6 (of 6 total)
  • Thread Starter ajjlechu

    (@ajjlechu)

    Thanks a lot! I really appreciate you checking this with your dev. Please let me know if there’s any possible way to handle or block CLI-triggered purges from my side (via code or MU-plugin) 🙂

    Thread Starter ajjlechu

    (@ajjlechu)

    I only have FTP and hosting panel access, no SSH/root.
    Is there any way to block or ignore CLI-triggered cache purges via a must-use plugin or WordPress code (for testing), given that I don’t have direct server access?

    Thread Starter ajjlechu

    (@ajjlechu)

    around 2/3 weeks ago I came across this entry in the logs,

    09/07/25 20:29:32.350 [=CLI=wordpress2477988 1 HK8] X-LiteSpeed-Purge: public,stale,2ae_FD => LiteSpeed\LSC->send_headers()@615 => WP_Hook->apply_filters(,ARRAY)@324 => WP_Hook->do_action(ARRAY)@348 => /wp-includes/load.php@517

    and time-wise it would actually match with the global purge. But as far as I understand, 2ae_FD is not a global purge.

    I suspect that something triggered the purge via CLI, which is why I can’t see the usual trace of it in the logs.

    Thread Starter ajjlechu

    (@ajjlechu)

    Hi @qtwrk & @longnha

    thanks a lot for your detailed reply.

    I downloaded the entire wp-content directory and wrote a Python script to search through all files for litespeed-purge. Please see the file below with the results – everything that contains litespeed-purge is only inside the official LiteSpeed plugin folder, nothing outside of it.

    * **API** Support litespeed_purged_front hook. (Umberto Fiorelli)
    * **API** New filter litespeed_purge_ucss to purge a single page UCSS. (#376681)
    * **API** New API litespeed_purge_all_object and litespeed_purged_all_object action hooks.
    * **API** Removed function litespeed_purge_single_post.
    * [API] Added LITESPEED_PURGE_SILENT const to bypass the notification when purging
    * [NEW FEATURE] New API function litespeed_purge_single_post($post_id).
    * **API** Added litespeed_purge_tags filter to allow manipulation of purge tags.
    // Action litespeed_purge_finalize
    add_action( 'litespeed_purge', __NAMESPACE__ . '\Purge::add' );
    add_action( 'litespeed_purge_all', __NAMESPACE__ . '\Purge::purge_all' );
    add_action( 'litespeed_purge_post', array( $this, 'purge_post' ) );
    add_action( 'litespeed_purge_posttype', __NAMESPACE__ . '\Purge::purge_posttype' );
    add_action( 'litespeed_purge_url', array( $this, 'purge_url' ) );
    add_action( 'litespeed_purge_widget', __NAMESPACE__ . '\Purge::purge_widget' );
    add_action( 'litespeed_purge_esi', __NAMESPACE__ . '\Purge::purge_esi' );
    add_action( 'litespeed_purge_private', __NAMESPACE__ . '\Purge::add_private' );
    add_action( 'litespeed_purge_private_esi', __NAMESPACE__ . '\Purge::add_private_esi' );
    add_action( 'litespeed_purge_private_all', __NAMESPACE__ . '\Purge::add_private_all' );
    // Action litespeed_purged_all
    add_action( 'litespeed_purge_all_object', __NAMESPACE__ . '\Purge::purge_all_object' );
    add_action( 'litespeed_purge_ucss', __NAMESPACE__ . '\Purge::purge_ucss' );
    $purge_post_events = apply_filters('litespeed_purge_post_events', array(
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    do_action('litespeed_purged_all');
    do_action('litespeed_purged_all_lscache');
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    do_action('litespeed_purged_all_ccss');
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    do_action('litespeed_purged_all_ucss');
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    do_action('litespeed_purged_all_lqip');
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    do_action('litespeed_purged_all_avatar');
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    do_action('litespeed_purged_all_localres');
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    do_action('litespeed_purged_all_cssjs');
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::error($msg);
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::error($msg);
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    do_action('litespeed_purged_all_opcache');
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    do_action('litespeed_purged_all_object');
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    do_action('litespeed_purged_single');
    do_action('litespeed_purged_front', $_SERVER['HTTP_REFERER']);
    do_action('litespeed_purge_ucss', $url_tag);
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    do_action('litespeed_purged_frontpage');
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    do_action('litespeed_purged_pages');
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success($msg);
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success(sprintf(__('Purge category %s', 'litespeed-cache'), $val));
    do_action('litespeed_purged_cat', $value);
    !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success(sprintf(__('Purge tag %s', 'litespeed-cache'), $val));
    do_action('litespeed_purged_tag', $val);
    !$quite && !defined('LITESPEED_PURGE_SILENT') && Admin_Display::success(sprintf(__('Purge url %s', 'litespeed-cache'), $val));
    do_action('litespeed_purged_link', $url);
    do_action('litespeed_purged_esi', $tag);
    do_action('litespeed_purged_posttype', $post_type);
    do_action('litespeed_purged_post', $pid);
    do_action('litespeed_purged_widget', $widget_id);
    do_action('litespeed_purged_comment_widget', $recent_comments->id);
    do_action('litespeed_purged_feeds');
    do_action('litespeed_purged_on_logout');
    do_action('litespeed_purge_finalize');
    $purge_tags = apply_filters('litespeed_purge_tags', $purge_tags, $is_private);
    add_action('litespeed_purge_finalize', __CLASS__ . '::purge');
    do_action('litespeed_purge_all', '3rd Autoptimize');
    do_action('litespeed_purge_all', '3rd avada');
    do_action('litespeed_purge_posttype', bbp_get_forum_post_type());
    do_action('litespeed_purge_post', $ancestor);
    do_action('litespeed_purge_widget', $replies_widget->id);
    do_action('litespeed_purge_widget', $topic_widget->id);
    do_action('litespeed_purge_all', '3rd Beaver_Builder');
    do_action('litespeed_purge_all', 'Elementor - Regenerate CSS & Data');
    do_action('litespeed_purge', self::CACHETAG_GALLERIES . $gallery->pageid);
    do_action('litespeed_purge', self::CACHETAG_GALLERIES . sanitize_key($_REQUEST['gallery_id']));
    do_action('litespeed_purge', self::CACHETAG_GALLERIES . sanitize_key($task_list[0]['query']['id']));
    do_action('litespeed_purge', self::CACHETAG_GALLERIES . $image->galleryid);
    do_action('litespeed_purge', self::CACHETAG_GALLERIES . sanitize_key($_GET['gid']));
    do_action('litespeed_purge', self::CACHETAG_GALLERIES . $gid);
    do_action('litespeed_purge', self::CACHETAG_GALLERIES . $new_gallery_id);
    do_action('litespeed_purge', self::CACHETAG_GALLERIES . $new_gallery_id);
    do_action('litespeed_purge', self::CACHETAG_GALLERIES . $image->galleryid);
    do_action('litespeed_purge', self::CACHETAG_GALLERIES . $gid);
    do_action('litespeed_purge', self::CACHETAG_ALBUMS . $aid);
    do_action('litespeed_purge_private_esi', 'storefront-cart-header');
    do_action('litespeed_purge_private_all');
    do_action('litespeed_purge_post', $product->get_id());
    do_action('litespeed_purge_post', $product->get_parent_id());
    do_action('litespeed_purge_post', $translation->element_id);
    do_action('litespeed_purge', self::CACHETAG_TERM . $tr_cat_id);
    do_action('litespeed_purge', self::CACHETAG_TERM . $term_id);
    do_action('litespeed_purge', self::CACHETAG_TERM . $cat);
    do_action('litespeed_purge', self::CACHETAG_TERM . $tag);
    do_action('litespeed_purge_post', $post_id);
    do_action('litespeed_purge_esi', 'yith_wcwl_add');
    var savedPurgeBy = getCookie('litespeed_purgeby_option');
    setCookie('litespeed_purgeby_option', this.value, 30);
    if ( ! empty( $_GET['dologin_gen_link'] ) && ! empty( $_GET['litespeed_purge_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['litespeed_purge_nonce'] ) ), 'litespeed_purge_action' ) ) {
    <a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin.php?page=litespeed-toolbox&dologin_gen_link=1' ), 'litespeed_purge_action', 'litespeed_purge_nonce' ) ); ?>" class="button button-secondary"><?php esc_html_e( 'Generate Link for Current User', 'litespeed-cache' ); ?></a>

    Regarding my crawler: it works and caches correctly. Without it everything shows as MISS; after a full purge and running the crawler I can confirm I get HITs, both on mobile and PC (with old cache cleared).

    The script itself works perfectly and warms up the cache exactly as I want.

    About the debug log: I already set the limit to 300 MB, but the only entries I see there are my own manual purges – nothing else shows up, even though the cache is being purged. Unfortunately, I don’t have access to the server logs either, since the hosting provider doesn’t allow it on shared hosting.

    Hi @longnha 🙂

    I also checked my database and found the same entry – the value in wp_options for litespeed.purge.queue is set to -1. From what I understand it should normally be 0 (or at least not remain stuck at -1).

    I even tried to change it manually to 0 about a week ago, but it didn’t work – it always reverts back to -1.

    Thread Starter ajjlechu

    (@ajjlechu)

    Hi,

    Follow-up to report APYYVKRR.

    Thanks for jumping in!

    How I warm up the cache (custom CRON every 5 min)

    • I prewarm only real pages (home, categories, brands, CMS, products).
    • For each URL I request desktop and mobile variants, with gzip/br and WebP varies.
    • I verify X-LiteSpeed-Cache: hit; if MISS, I requeue.
    • I log the exact timestamp when homepage returns MISS (that’s how I detect purge times).

    cURL examples

    # Desktop HTML (gzip/br)
    curl -sS -D - "https://reintec.ch/..." \
    -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/126" \
    -H "Accept: text/html,application/xhtml+xml" \
    -H "Accept-Encoding: gzip, br" \
    -H "Accept-Language: de-CH,en;q=0.8"

    # Mobile HTML (triggers ismobile vary)
    curl -sS -D - "https://reintec.ch/..." \
    -H "User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_6 like Mac OS X) AppleWebKit/605.1.15 Mobile/15E148" \
    -H "Accept: text/html,application/xhtml+xml" \
    -H "Accept-Encoding: gzip, br"

    What I see

    • After a full warm cycle everything is HIT.
    • Roughly every ~12h there’s a global-like purge → many MISS until warmup completes.
    • My MU sniffers on purge hooks (litespeed_purge_all, litespeed_api_purge, litespeed_purge_post) didn’t catch a caller.
    • Host says the call lands in purge.cls.php, but the origin is unknown.
    • Debug often shows: X-LiteSpeed-Purge: public,stale,2ae_FD yet the cache behaves as if a broader purge happened (home/product/category MISS at once).

    What I tried

    • One-off LiteSpeed reset via PHP on this site (no change).
    • Same prewarmer & LS settings on another site → no unexpected purges there.
    • Purge on upgrade OFF, TTLs long (front page 604800), crawler & QUIC.cloud disabled.

    Code excerpts (minimal, for context)

    warm_critical.php (core ideas only)

    $UA_DESKTOP = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/127';
    $UA_MOBILE = 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 ...) Safari/604.1';
    $ENCS = ['br','gzip']; // warm both encodings
    $BATCH_SIZE = 10; // small batches, every 5 min
    $WARM_MOBILE = 1; // warm mobile (ismobile vary)
    $siteBase = 'https://reintec.ch';

    // For each URL -> desktop & mobile, br & gzip
    foreach ($batchUrls as $u) {
    foreach ($ENCS as $enc) {
    $tasks[] = ['url'=>$u,'ua'=>$UA_DESKTOP,'tag'=>'desk','enc'=>$enc];
    if ($WARM_MOBILE) $tasks[] = ['url'=>$u,'ua'=>$UA_MOBILE,'tag'=>'mob','enc'=>$enc];
    }
    }

    // Parse LS header and re-hit if not HIT
    function parse_ls($resp){ /* ... extract X-LiteSpeed-Cache ... */ }
    if ($ls !== 'hit') { /* single() retry once to confirm */ }

    // Detect cycle wrap; on full pass rotate state & trim logs
    if ($wrapped) { @unlink($stateFile); @unlink($critFile); @unlink($logFile); }

    cron-5min.php (calls warmers + wp-cron)

    $BASE  = 'https://reintec.ch';

    // Critical batch warmup
    hit("$BASE/warm_critical.php?cron=1&mobile=1&token=$TOKEN", 'warm_critical', 24, [
    "X-WARM-TOKEN: $TOKEN",
    'User-Agent: ReinTec-Cron/1.0',
    ]);

    // Whole-site warmup (detached)
    hit("$BASE/warm_wholepage.php?cron=1&detached=1&token=$TOKEN", 'warm_wholepage', 24, [
    "X-WARM-TOKEN: $TOKEN",
    'User-Agent: ReinTec-Cron/1.0',
    ]);

    // Kick WP-Cron (no auth, no cookies)
    hit("$BASE/wp-cron.php?doing_wp_cron=1", 'wp-cron', 20);

    Questions

    1. How can I fully trace the caller of purge_all inside purge.cls.php (hook/backtrace/file/line)? Any recommended filter to attach a backtrace logger?
    2. Could server-level jobs (Enterprise LS nightly tasks, tmp cleaners, inode eviction/limits, etc.) cause a broad purge without WP hooks firing?
    3. Can purge_stale queues or REST/checkout actions escalate into a site-wide purge under some conditions?
    4. Any additional headers I should include in warm-up (beyond UA / Accept / Accept-Encoding / Accept-Language) to avoid bypass/edge variants?

    If helpful I can share the full scripts, but the essence is above. Any hint to pinpoint the exact trigger would be hugely appreciated.

    Thanks!
    Leszek

    Thread Starter ajjlechu

    (@ajjlechu)

    Here is also a screenshot from my custom prewarmer script.
    As you can see, the last detected purge was at 10:40 today.

    The script checks if the homepage is on HIT during the first visit – if it is on MISS, it saves a debug.log file with the exact time and date.
    It only runs every 5 minutes via server CRON.

Viewing 6 replies - 1 through 6 (of 6 total)