'', 'compare_url' => '', 'email_alerts_enabled' => '0', 'alert_email' => get_option('admin_email'), 'alert_score_threshold' => 75, 'alert_cache_threshold' => 60, 'alert_when_page_cache_missing' => '1', 'alert_when_object_cache_missing' => '0', 'alert_frequency' => 'daily', 'weekly_report_enabled' => '0', ); $saved = get_option(AGEROTH_SH_OPTION_SETTINGS, array()); return wp_parse_args(is_array($saved) ? $saved : array(), $defaults); } function ageroth_sh_confidence_label($c){$c=(int)$c; if($c>=65)return'High'; if($c>=25)return'Medium'; return'Low';} function ageroth_sh_human_ms($seconds){return round(((float)$seconds)*1000).' ms';} function ageroth_sh_timing_signal_label($s){if($s==='strong')return'Strong'; if($s==='weak')return'Moderate'; return'Too Fast to Judge';} function ageroth_sh_timing_interpretation($s){if($s==='strong') return 'Strong timing hint'; if($s==='weak') return 'Moderate timing hint'; return 'Too fast to judge';} function ageroth_sh_is_plugin_active_slug($slug){if(!function_exists('is_plugin_active')) require_once ABSPATH.'wp-admin/includes/plugin.php'; return function_exists('is_plugin_active') && is_plugin_active($slug);} function ageroth_sh_sanitize_settings($input){ $output = array(); $input = is_array($input) ? $input : array(); $output['test_url'] = isset($input['test_url']) ? sanitize_text_field(wp_unslash($input['test_url'])) : ''; $output['compare_url'] = isset($input['compare_url']) ? sanitize_text_field(wp_unslash($input['compare_url'])) : ''; $output['email_alerts_enabled'] = !empty($input['email_alerts_enabled']) ? '1' : '0'; $email = isset($input['alert_email']) ? sanitize_email(wp_unslash($input['alert_email'])) : ''; $output['alert_email'] = is_email($email) ? $email : get_option('admin_email'); $score_threshold = isset($input['alert_score_threshold']) ? absint($input['alert_score_threshold']) : 75; $cache_threshold = isset($input['alert_cache_threshold']) ? absint($input['alert_cache_threshold']) : 60; $output['alert_score_threshold'] = max(1, min(100, $score_threshold)); $output['alert_cache_threshold'] = max(1, min(100, $cache_threshold)); $output['alert_when_page_cache_missing'] = !empty($input['alert_when_page_cache_missing']) ? '1' : '0'; $output['alert_when_object_cache_missing'] = !empty($input['alert_when_object_cache_missing']) ? '1' : '0'; $allowed_frequency = array('daily', 'weekly', 'changes_only'); $frequency = isset($input['alert_frequency']) ? sanitize_key(wp_unslash($input['alert_frequency'])) : 'daily'; $output['alert_frequency'] = in_array($frequency, $allowed_frequency, true) ? $frequency : 'daily'; $output['weekly_report_enabled'] = !empty($input['weekly_report_enabled']) ? '1' : '0'; return $output; } function ageroth_sh_normalize_url($value){$value=trim((string)$value); if($value==='') return home_url('/'); if(preg_match('#^https?://#i',$value)) return esc_url_raw($value); return home_url('/' . ltrim($value, '/'));} add_action('admin_menu', function(){ add_menu_page('Ageroth Site Health','Site Health+','manage_options','ageroth-site-health','ageroth_sh_render_dashboard','dashicons-heart',58); add_submenu_page('ageroth-site-health','Ageroth Site Health','Dashboard','manage_options','ageroth-site-health','ageroth_sh_render_dashboard'); add_submenu_page('ageroth-site-health','Ageroth Site Health Settings','Settings','manage_options','ageroth-site-health-settings','ageroth_sh_render_settings'); }); add_action('admin_enqueue_scripts', function($hook){ if (strpos((string)$hook,'ageroth-site-health')===false) return; wp_enqueue_style('ageroth-sh-admin', AGEROTH_SH_URL.'assets/admin.css', [], AGEROTH_SH_VERSION); }); add_filter('plugin_action_links_' . plugin_basename(__FILE__), function($links){ $custom = [ 'Dashboard', 'Settings', ]; return array_merge($custom, $links); }); add_action('admin_init', function(){ register_setting('ageroth_sh_settings_group', AGEROTH_SH_OPTION_SETTINGS, array('sanitize_callback' => 'ageroth_sh_sanitize_settings')); if (!current_user_can('manage_options')) return; if (!empty($_POST['ageroth_sh_run_test']) && check_admin_referer('ageroth_sh_run_test')) { $settings = ageroth_sh_get_settings(); $settings['test_url'] = sanitize_text_field(wp_unslash($_POST['test_url'] ?? '')); $settings['compare_url'] = sanitize_text_field(wp_unslash($_POST['compare_url'] ?? '')); update_option(AGEROTH_SH_OPTION_SETTINGS, $settings, false); wp_safe_redirect(admin_url('admin.php?page=ageroth-site-health')); exit; } if (!empty($_POST['ageroth_sh_use_homepage']) && check_admin_referer('ageroth_sh_use_homepage')) { $settings = ageroth_sh_get_settings(); $settings['test_url'] = ''; update_option(AGEROTH_SH_OPTION_SETTINGS, $settings, false); wp_safe_redirect(admin_url('admin.php?page=ageroth-site-health')); exit; } if (!empty($_POST['ageroth_sh_send_test_alert']) && check_admin_referer('ageroth_sh_send_test_alert')) { ageroth_sh_run_email_alert_check(true); wp_safe_redirect(admin_url('admin.php?page=ageroth-site-health-settings&ageroth_sh_test_alert=sent')); exit; } if (!empty($_POST['ageroth_sh_send_weekly_report_now']) && check_admin_referer('ageroth_sh_send_weekly_report_now')) { ageroth_sh_send_weekly_health_report(true); wp_safe_redirect(admin_url('admin.php?page=ageroth-site-health-settings&ageroth_sh_weekly_report=sent')); exit; } if (!empty($_POST['ageroth_sh_save_feedback']) && check_admin_referer('ageroth_sh_save_feedback')) { $msg = sanitize_textarea_field(wp_unslash($_POST['ageroth_sh_feedback'] ?? '')); if ($msg !== '') { $items = get_option(AGEROTH_SH_OPTION_FEEDBACK, []); if (!is_array($items)) $items = []; array_unshift($items, ['message'=>$msg,'created_at'=>current_time('mysql')]); $items = array_slice($items, 0, 8); update_option(AGEROTH_SH_OPTION_FEEDBACK, $items, false); } wp_safe_redirect(admin_url('admin.php?page=ageroth-site-health')); exit; } }); register_activation_hook(__FILE__, 'ageroth_sh_activate_alert_cron'); register_deactivation_hook(__FILE__, 'ageroth_sh_deactivate_alert_cron'); function ageroth_sh_activate_alert_cron() { if (!wp_next_scheduled(AGEROTH_SH_CRON_HOOK)) { wp_schedule_event(time() + HOUR_IN_SECONDS, 'daily', AGEROTH_SH_CRON_HOOK); wp_schedule_event(time() + DAY_IN_SECONDS, 'weekly', AGEROTH_SH_WEEKLY_CRON_HOOK); } } function ageroth_sh_deactivate_alert_cron() { $timestamp = wp_next_scheduled(AGEROTH_SH_CRON_HOOK); if ($timestamp) { wp_unschedule_event($timestamp, AGEROTH_SH_CRON_HOOK); $weekly_timestamp = wp_next_scheduled(AGEROTH_SH_WEEKLY_CRON_HOOK); if ($weekly_timestamp) { wp_unschedule_event($weekly_timestamp, AGEROTH_SH_WEEKLY_CRON_HOOK); } } } add_filter('cron_schedules', 'ageroth_sh_cron_schedules'); function ageroth_sh_cron_schedules($schedules) { if (!isset($schedules['weekly'])) { $schedules['weekly'] = array('interval' => WEEK_IN_SECONDS, 'display' => 'Once Weekly'); } return $schedules; } add_action(AGEROTH_SH_CRON_HOOK, 'ageroth_sh_run_email_alert_check'); add_action(AGEROTH_SH_WEEKLY_CRON_HOOK, 'ageroth_sh_send_weekly_health_report'); function ageroth_sh_alert_state() { $state = get_option(AGEROTH_SH_OPTION_ALERT_STATE, array()); return is_array($state) ? $state : array(); } function ageroth_sh_update_alert_state($state) { update_option(AGEROTH_SH_OPTION_ALERT_STATE, is_array($state) ? $state : array(), false); } function ageroth_sh_run_email_alert_check($force_send = false) { $settings = ageroth_sh_get_settings(); if (empty($settings['email_alerts_enabled']) && !$force_send) { return false; } $email = isset($settings['alert_email']) ? $settings['alert_email'] : ''; if (!is_email($email)) { return false; } $report = ageroth_sh_build_report(); $issues = ageroth_sh_get_alert_issues($report, $settings); if (empty($issues) && !$force_send) { ageroth_sh_update_alert_state(array( 'last_checked' => current_time('mysql'), 'last_status' => 'healthy', 'last_issues_hash' => '', )); return false; } if ($force_send && empty($issues)) { $issues[] = 'Test alert: no critical health issues were detected at this time.'; } $issues_hash = md5(wp_json_encode($issues)); $state = ageroth_sh_alert_state(); $frequency = isset($settings['alert_frequency']) ? $settings['alert_frequency'] : 'daily'; $last_sent = isset($state['last_sent_time']) ? absint($state['last_sent_time']) : 0; $last_hash = isset($state['last_issues_hash']) ? (string) $state['last_issues_hash'] : ''; if (!$force_send) { if ($frequency === 'changes_only' && $last_hash === $issues_hash) { return false; } if ($frequency === 'daily' && $last_sent > 0 && (time() - $last_sent) < DAY_IN_SECONDS && $last_hash === $issues_hash) { return false; } if ($frequency === 'weekly' && $last_sent > 0 && (time() - $last_sent) < WEEK_IN_SECONDS && $last_hash === $issues_hash) { return false; } } $subject = sprintf('[%s] Ageroth Site Health Alert', wp_specialchars_decode(get_bloginfo('name'), ENT_QUOTES)); $body = ageroth_sh_build_alert_email_body($report, $issues); $sent = wp_mail($email, $subject, $body); $state = array( 'last_checked' => current_time('mysql'), 'last_sent' => $sent ? current_time('mysql') : '', 'last_sent_time' => $sent ? time() : $last_sent, 'last_status' => $sent ? 'sent' : 'failed', 'last_issues_hash' => $issues_hash, 'last_issues' => $issues, ); ageroth_sh_update_alert_state($state); return $sent; } function ageroth_sh_get_alert_issues($report, $settings) { $issues = array(); $score = isset($report['score']) ? absint($report['score']) : 0; $score_threshold = isset($settings['alert_score_threshold']) ? absint($settings['alert_score_threshold']) : 75; if ($score > 0 && $score < $score_threshold) { $issues[] = sprintf('Overall health score is %d, below your alert threshold of %d.', $score, $score_threshold); } $cache_score = isset($report['score_breakdown']['Caching']) ? absint($report['score_breakdown']['Caching']) : 0; $cache_threshold = isset($settings['alert_cache_threshold']) ? absint($settings['alert_cache_threshold']) : 60; if ($cache_score > 0 && $cache_score < $cache_threshold) { $issues[] = sprintf('Caching score is %d, below your alert threshold of %d.', $cache_score, $cache_threshold); } if (!empty($settings['alert_when_page_cache_missing']) && isset($report['primary_page_cache']) && $report['primary_page_cache'] === 'None detected') { $issues[] = 'Primary page cache is no longer detected.'; } if (!empty($settings['alert_when_object_cache_missing']) && isset($report['primary_object_cache']) && $report['primary_object_cache'] === 'None detected') { $issues[] = 'Persistent object cache is not detected.'; } return array_values(array_unique($issues)); } function ageroth_sh_build_alert_email_body($report, $issues) { $lines = array(); $lines[] = 'Ageroth Site Health detected a change or issue that matches your alert settings.'; $lines[] = ''; $lines[] = 'Site: ' . home_url('/'); $lines[] = 'Overall Score: ' . (isset($report['score']) ? $report['score'] : 'Unknown'); $lines[] = 'Page Cache: ' . (isset($report['primary_page_cache']) ? $report['primary_page_cache'] : 'Unknown'); $lines[] = 'Object Cache: ' . (isset($report['primary_object_cache']) ? $report['primary_object_cache'] : 'Unknown'); $lines[] = 'Generated: ' . (isset($report['generated_at']) ? $report['generated_at'] : current_time('mysql')); $lines[] = ''; $lines[] = 'Issues:'; foreach ($issues as $issue) { $lines[] = '- ' . $issue; } if (!empty($report['recommendations'])) { $lines[] = ''; $lines[] = 'Recommendations:'; foreach ($report['recommendations'] as $recommendation) { $lines[] = '- ' . $recommendation; } } $lines[] = ''; $lines[] = 'Open WordPress admin to review the full report.'; $lines[] = 'This email was generated locally by the Ageroth Site Health plugin. No data was sent to external services.'; return implode("\n", $lines); } function ageroth_sh_detect_page_cache() { $map = [ 'wp-fastest-cache/wpFastestCache.php'=>'WP Fastest Cache', 'litespeed-cache/litespeed-cache.php'=>'LiteSpeed Cache', 'wp-rocket/wp-rocket.php'=>'WP Rocket', 'w3-total-cache/w3-total-cache.php'=>'W3 Total Cache', 'wp-super-cache/wp-cache.php'=>'WP Super Cache', 'sg-cachepress/sg-cachepress.php'=>'SiteGround Optimizer', 'breeze/breeze.php'=>'Breeze', 'cache-enabler/cache-enabler.php'=>'Cache Enabler', 'hummingbird-performance/wp-hummingbird.php'=>'Hummingbird', 'swift-performance-lite/performance.php'=>'Swift Performance', 'flying-press/flying-press.php'=>'FlyingPress', 'wp-optimize/wp-optimize.php'=>'WP-Optimize', 'autoptimize/autoptimize.php'=>'Autoptimize' ]; $signals=[]; $primary='None detected'; foreach($map as $slug=>$name){ if(ageroth_sh_is_plugin_active_slug($slug)){ $primary=$name; $signals[]='Active plugin: '.$name; break; } } if(defined('WP_CACHE') && WP_CACHE){ $signals[]='WP_CACHE'; if($primary==='None detected') $primary='Unknown / signal only'; } if(file_exists(WP_CONTENT_DIR.'/advanced-cache.php')){ $signals[]='advanced-cache.php'; if($primary==='None detected') $primary='Unknown / signal only'; } foreach([WP_CONTENT_DIR.'/cache', WP_CONTENT_DIR.'/wpfc-cache', WP_CONTENT_DIR.'/cache/page_enhanced'] as $folder){ if(is_dir($folder)) $signals[]='Cache dir: '.basename($folder); } if(empty($signals)) $signals[]='No cache signal found'; return ['primary'=>$primary,'signals'=>array_values(array_unique($signals))]; } function ageroth_sh_detect_object_cache() { $map=['redis-cache/redis-cache.php'=>'Redis Object Cache','object-cache-pro/object-cache-pro.php'=>'Object Cache Pro','memcached/memcached.php'=>'Memcached','docket-cache/docket-cache.php'=>'Docket Cache','litespeed-cache/litespeed-cache.php'=>'Not detected']; $signals=[]; $primary='None detected'; foreach($map as $slug=>$name){ if(ageroth_sh_is_plugin_active_slug($slug)){ $primary=$name; $signals[]='Active plugin: '.$name; break; } } if(function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()){ $signals[]='wp_using_ext_object_cache()'; if($primary==='None detected') $primary='Unknown / signal only'; } if(file_exists(WP_CONTENT_DIR.'/object-cache.php')){ $signals[]='object-cache.php'; if($primary==='None detected') $primary='Unknown / signal only'; } if(empty($signals)) $signals[]='No object cache signal found'; return ['primary'=>$primary,'signals'=>array_values(array_unique($signals))]; } function ageroth_sh_extract_markers($body){ $body = strtolower((string)$body); $markers=[]; $map=['wp fastest cache'=>'HTML marker: WP Fastest Cache','wpfc-minified'=>'HTML marker: WP Fastest Cache','wp rocket'=>'HTML marker: WP Rocket','litespeed-cache'=>'HTML marker: LiteSpeed Cache','w3 total cache'=>'HTML marker: W3 Total Cache']; foreach($map as $needle=>$label){ if(strpos($body,$needle)!==false) $markers[]=$label; } return array_values(array_unique($markers)); } function ageroth_sh_classify_hit($hit){ $low = strtolower((string)$hit); foreach(['x-litespeed-cache: hit','cf-cache-status: hit','x-cache: hit','x-cache-status: hit','x-fastcgi-cache: hit','x-proxy-cache: hit','x-wp-cf-super-cache-cache: cache','x-kinsta-cache: hit'] as $pattern){ if(strpos($low,$pattern)!==false) return 'positive'; } if(preg_match('/^age:\s*[1-9][0-9]*/',$low)) return 'positive'; foreach(['cf-cache-status: bypass','cf-cache-status: dynamic','cf-cache-status: miss','x-litespeed-cache: miss','x-cache: miss','x-cache-status: miss','x-fastcgi-cache: miss','x-proxy-cache: miss','private'] as $pattern){ if(strpos($low,$pattern)!==false) return 'negative'; } if(strpos($low,'cache-control:')!==false && (strpos($low,'no-cache')!==false || strpos($low,'no-store')!==false || strpos($low,'must-revalidate')!==false)) return 'neutral-cache'; foreach(['html marker:','x-litespeed-cache:','cf-cache-status:','x-cache:','x-cache-status:','x-fastcgi-cache:','x-proxy-cache:','x-wp-cf-super-cache-cache:','x-kinsta-cache:','age:'] as $pattern){ if(strpos($low,$pattern)!==false) return 'neutral-cache'; } return 'generic'; } function ageroth_sh_run_frontend_test($url, $page_cache) { $requests=[$url,$url]; $request_times=[]; $all_hits=[]; $final_status=0; $server_header=""; foreach($requests as $i=>$request_url){ $start=microtime(true); $response=wp_remote_get($request_url,['timeout'=>8,'redirection'=>3,'sslverify'=>false,'user-agent'=>'Ageroth-Site-Health/'.AGEROTH_SH_VERSION,'headers'=>['Accept'=>'text/html,application/xhtml+xml']]); $elapsed=max(0,microtime(true)-$start); if(is_wp_error($response)){ return ['status'=>'warning','status_label'=>'Request Failed','summary'=>'The live frontend request failed before cache headers could be evaluated.','positive_hits'=>[],'negative_hits'=>[],'neutral_hits'=>[],'headers'=>[],'confidence'=>0,'tested_url'=>$url,'http_status'=>0,'diagnostics'=>['Cache Plugin'=>$page_cache['primary'] !== 'None detected' ? $page_cache['primary'] : 'Not detected','Live HIT Header'=>'Not detected','Response Type'=>'Unavailable'],'recommendation'=>'The test request failed. Try again or test a different public URL.','response_type'=>'Unavailable','explanation'=>'The live request failed before cache signals could be evaluated.','what_it_means'=>'The plugin could not complete the live request, so this report cannot confirm how the tested page behaves.','next_steps'=>['Run the test again.','Try a different public URL.','Check that the site can be reached from the server.'],'request_times'=>[],'double_request_summary'=>'No timing data available.','timing_signal'=>'inconclusive','timing_explanation'=>'Timing data was not available for this test.','why_uncached'=>[$response->get_error_message()]]; } $final_status=(int)wp_remote_retrieve_response_code($response); $headers=wp_remote_retrieve_headers($response); $body=(string)wp_remote_retrieve_body($response); $headers_array=[]; if(is_object($headers) && method_exists($headers,'getAll')) $headers_array=(array)$headers->getAll(); elseif(is_array($headers)) $headers_array=$headers; $all_hits[]='HTTP '.$final_status; foreach((array)$headers_array as $name=>$value){ $name=strtolower((string)$name); if(is_array($value)) $value=implode(', ', array_map('strval',$value)); $value=trim(preg_replace('/\s+/',' ',(string)$value)); if($value==='') continue; if($name==='server') $server_header=$value; if(in_array($name,['cache-control','age','x-cache','x-cache-status','x-litespeed-cache','x-fastcgi-cache','x-proxy-cache','cf-cache-status','x-wp-cf-super-cache-cache','x-kinsta-cache','server'],true)) $all_hits[]=$name.': '.$value; } foreach(ageroth_sh_extract_markers($body) as $marker) $all_hits[]=$marker; $request_times[]=['request'=>$i+1,'duration'=>$elapsed]; } $all_hits=array_values(array_unique($all_hits)); $positive_hits=[]; $negative_hits=[]; $neutral_hits=[]; $confidence=0; foreach($all_hits as $hit){ $type=ageroth_sh_classify_hit($hit); if($type==='positive'){ $positive_hits[]=$hit; $confidence+=40; } elseif($type==='negative'){ $negative_hits[]=$hit; $confidence-=15; } elseif($type==='neutral-cache'){ $neutral_hits[]=$hit; $confidence+=10; } } $confidence=max(0,min(100,$confidence)); $has_strong_positive=!empty($positive_hits); $has_marker=false; foreach($all_hits as $hit){ if(stripos($hit,'HTML marker:')!==false) $has_marker=true; } $has_page_cache_locally=$page_cache['primary']!=='None detected'; $timing_signal='inconclusive'; $timing_explanation='Timing data was not available for this test.'; $double_request_summary='Double-request timing was not available.'; $likely_cached=false; if(count($request_times)>=2){ $first=(float)$request_times[0]['duration']; $second=(float)$request_times[1]['duration']; $double_request_summary=sprintf('Request 1: %s • Request 2: %s', ageroth_sh_human_ms($first), ageroth_sh_human_ms($second)); if($second < $first*0.70 && ($first-$second)>0.08){ $timing_signal='strong'; $timing_explanation='The second request was significantly faster, which is a strong timing hint for cached delivery.'; $double_request_summary.=' • The second request was significantly faster.'; $likely_cached=true; $confidence=max($confidence,55); } elseif($second < $first*0.85 && ($first-$second)>0.04){ $timing_signal='weak'; $timing_explanation='The second request was moderately faster, which may suggest a cache warmup effect.'; $double_request_summary.=' • The second request was moderately faster.'; $likely_cached=true; $confidence=max($confidence,35); } else { $timing_signal='inconclusive'; $timing_explanation='Both requests were already very fast, so timing alone could not confirm a cache warmup effect.'; $double_request_summary.=' • The second request was not meaningfully faster.'; } } $result=['status'=>'neutral','status_label'=>'Page Cache Detected','summary'=>'A page cache layer appears to be active, but this response does not expose an explicit cache-hit header.','positive_hits'=>$positive_hits,'negative_hits'=>$negative_hits,'neutral_hits'=>$neutral_hits,'headers'=>array_slice($all_hits,0,10),'confidence'=>max(0,min(100,$confidence)),'tested_url'=>$url,'http_status'=>$final_status,'diagnostics'=>[],'recommendation'=>'Page cache is detected, but no explicit hit header is exposed on this response.','response_type'=>'Cache detected (no explicit HIT header)','explanation'=>'This is common on some cache plugins and does not necessarily mean caching is not working.','what_it_means'=>'Caching appears to be active on this site. However, the tested response did not include a direct HIT header, which is common on some cache plugins and server setups.','next_steps'=>['Test another public URL.','Try again after clearing cache.','Some pages may expose cache signals more clearly than others.'],'request_times'=>$request_times,'double_request_summary'=>$double_request_summary,'timing_signal'=>$timing_signal,'timing_explanation'=>$timing_explanation,'why_uncached'=>[],'server_header'=>$server_header]; if($has_strong_positive){ $result['status']='good'; $result['status_label']='Confirmed Cache Hit'; $result['summary']='A live request returned a clear cache-hit signal for this URL.'; $result['response_type']='Confirmed cache hit'; $result['explanation']='A live request returned a clear cache-hit signal for this URL.'; $result['what_it_means']='Good news. The tested page returned a clear cache-hit signal, so caching is working for this URL.'; $result['next_steps']=['Test another public URL to compare behavior.','Review object cache separately if needed.','Use this result as a baseline for the site.']; $result['recommendation']='A confirmed live cache hit was detected on this URL.'; } elseif(($has_marker || $has_page_cache_locally) && $likely_cached){ $result['status']='good'; $result['status_label']='Likely Cached (Double Request)'; $result['summary']='The second request was meaningfully faster, which suggests this URL is probably being served from cache.'; $result['response_type']='Likely cached (timing hint)'; $result['explanation']='This result is based on timing behavior across two back-to-back requests, even though no explicit HIT header was exposed.'; $result['what_it_means']='Caching appears to be active on this site. The server did not return a direct HIT header, but the second request was faster in a way that suggests cached delivery.'; $result['next_steps']=['Test another public URL to compare the pattern.','Run the test again after clearing cache.','Use Technical Details if you need the raw timing data.']; $result['recommendation']='Double-request timing suggests this URL is probably being served from cache.'; } elseif(!$has_marker && !$has_page_cache_locally && !empty($negative_hits)){ $result['status']='warning'; $result['status_label']='Likely Uncached'; $result['summary']='This page response looks dynamic or bypassed rather than served from cache.'; $result['response_type']='Likely uncached'; $result['explanation']='The tested response looks more like a dynamic or bypassed page than a cached response.'; $result['what_it_means']='This response looks more like a dynamic or bypassed page than a cached response. The page may be excluded from cache, or another rule may be affecting it.'; $result['next_steps']=['Check cache exclusions for this page.','Test a simpler public URL.','Review any plugin or theme code that adds restrictive headers.']; $result['recommendation']='This response looks uncached. Test another public URL and review any cache exclusions if needed.'; } foreach($negative_hits as $hit){ $low=strtolower($hit); if(strpos($low,'no-cache')!==false || strpos($low,'no-store')!==false) $result['why_uncached'][]='The response sent restrictive browser cache headers.'; elseif(strpos($low,'miss')!==false || strpos($low,'bypass')!==false || strpos($low,'dynamic')!==false) $result['why_uncached'][]='The cache layer reported a miss, bypass, or dynamic response.'; elseif(strpos($low,'private')!==false) $result['why_uncached'][]='The response appears private, which usually prevents a public cache hit.'; } $result['why_uncached']=array_values(array_unique($result['why_uncached'])); $result['diagnostics']=['Cache Plugin'=>($has_marker || $has_page_cache_locally)?$page_cache['primary']:'Not detected','Live HIT Header'=>$has_strong_positive?'Detected':'Not detected','Response Type'=>$result['response_type']]; return $result; } function ageroth_sh_compare_urls($home_result, $inner_result){ $home = $home_result['status_label'] ?? 'Unknown'; $inner = $inner_result['status_label'] ?? 'Unknown'; if($home === $inner) return ['summary'=>'Homepage and inner-page cache behavior look similar.','severity'=>'same']; return ['summary'=>'Cache behavior differs between the homepage and the comparison URL.','severity'=>'different']; } function ageroth_sh_server_profile($frontend){ $server = !empty($frontend['server_header']) ? $frontend['server_header'] : (isset($_SERVER['SERVER_SOFTWARE']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_SOFTWARE'])) : 'Unknown'); $opcache = 'Unknown / not exposed'; if (function_exists('opcache_get_status')) { $status = @opcache_get_status(false); if (is_array($status) && !empty($status['opcache_enabled'])) { $opcache = 'Enabled'; } } return [ 'Web Server' => $server, 'PHP Version' => PHP_VERSION, 'OPcache' => $opcache, 'Memory Limit' => defined('WP_MEMORY_LIMIT') ? WP_MEMORY_LIMIT : ini_get('memory_limit'), 'Max Execution Time' => ini_get('max_execution_time') !== false ? ini_get('max_execution_time').'s' : 'Unknown', 'Multisite' => is_multisite() ? 'Yes' : 'No' ]; } function ageroth_sh_cache_intelligence($page_cache,$frontend){ $confidence=(int)($frontend['confidence'] ?? 0); $hit=($frontend['status_label'] ?? '')==='Confirmed Cache Hit'; $takeaway=$hit?'A clear live cache HIT signal was detected.':($page_cache['primary']!=='None detected'?'A cache layer is detected, but this response does not expose a direct HIT header.':'No clear page-cache layer was detected locally.'); return [ 'Plugin / Layer'=>$page_cache['primary'], 'Live Result'=>$frontend['status_label'] ?? 'Unknown', 'Confidence'=>ageroth_sh_confidence_label($confidence).' ('.$confidence.'%)', 'Timing Signal'=>ageroth_sh_timing_signal_label($frontend['timing_signal'] ?? 'inconclusive'), 'Takeaway'=>$takeaway ]; } function ageroth_sh_object_cache_intelligence($object_cache){ return [ 'Object Cache Layer'=>$object_cache['primary'], 'Drop-in File'=>file_exists(WP_CONTENT_DIR.'/object-cache.php')?'Present':'Not found', 'WordPress External Object Cache'=>(function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache())?'Enabled':'Not reported', 'Recommendation'=>$object_cache['primary']!=='None detected'?'Persistent object cache signals were detected.':'Consider persistent object cache for larger or busier sites.' ]; } function ageroth_sh_wc_intelligence(){ $active=class_exists('WooCommerce'); $items=['Status'=>$active?'Active':'Inactive']; if($active && function_exists('wc_get_page_id')){ foreach(['Cart page'=>'cart','Checkout page'=>'checkout','My account page'=>'myaccount'] as $label=>$key){ $id=wc_get_page_id($key); $items[$label]=($id && $id>0)?get_permalink($id):'Not configured'; } $items['Cache note']='Cart, checkout, and account pages should normally stay excluded from full-page cache.'; } else { $items['Cache note']='WooCommerce is not active on this site.'; } return $items; } function ageroth_sh_render_kv_grid($items){ foreach((array)$items as $label=>$value){ echo '
'.esc_html($label).''.esc_html(is_scalar($value)?(string)$value:wp_json_encode($value)).'
'; } } function ageroth_sh_build_report() { $settings = ageroth_sh_get_settings(); $test_url = !empty($settings['test_url']) ? ageroth_sh_normalize_url($settings['test_url']) : home_url('/'); $compare_url = !empty($settings['compare_url']) ? ageroth_sh_normalize_url($settings['compare_url']) : ''; $cache_key = 'ageroth_sh_report_' . md5(AGEROTH_SH_VERSION . '|' . $test_url . '|' . $compare_url); $cached = get_transient($cache_key); if (is_array($cached)) return $cached; global $wp_version; $page_cache=ageroth_sh_detect_page_cache(); $object_cache=ageroth_sh_detect_object_cache(); $frontend=ageroth_sh_run_frontend_test($test_url,$page_cache); $comparison = null; if($compare_url!==''){ $comparison_result=ageroth_sh_run_frontend_test($compare_url,$page_cache); $comparison_meta = ageroth_sh_compare_urls($frontend,$comparison_result); $comparison=['url'=>$compare_url,'result'=>$comparison_result,'summary'=>$comparison_meta['summary'],'severity'=>$comparison_meta['severity']]; } $memory_limit_string = defined('WP_MEMORY_LIMIT') ? WP_MEMORY_LIMIT : ini_get('memory_limit'); $memory_limit_raw = function_exists('wp_convert_hr_to_bytes') ? wp_convert_hr_to_bytes($memory_limit_string) : 0; $memory_limit_mb = (int) round($memory_limit_raw / 1024 / 1024); $php_ok = version_compare(PHP_VERSION,'8.0','>='); $wp_ok = version_compare($wp_version,'6.4','>='); $memory_ok = $memory_limit_mb >= 64; $wc_active = class_exists('WooCommerce'); $wp_debug = defined('WP_DEBUG') && WP_DEBUG; $rest_ok = !empty(rest_get_url_prefix()); $cron_disabled = defined('DISABLE_WP_CRON') && DISABLE_WP_CRON; $checks=[ ['label'=>'PHP Version','value'=>PHP_VERSION,'status'=>$php_ok?'good':'warning','message'=>$php_ok?sprintf('PHP %s looks modern enough for many current setups.', PHP_VERSION):sprintf('PHP %s is older than recommended for a modern WordPress stack.', PHP_VERSION),'points'=>$php_ok?0:12], ['label'=>'WordPress Version','value'=>$wp_version,'status'=>$wp_ok?'good':'warning','message'=>$wp_ok?sprintf('WordPress %s looks reasonably up to date.', $wp_version):sprintf('WordPress %s is behind recent stable releases.', $wp_version),'points'=>$wp_ok?0:10], ['label'=>'Memory Limit','value'=>$memory_limit_mb > 0 ? $memory_limit_mb . 'M' : $memory_limit_string,'status'=>$memory_ok?'good':'warning','message'=>$memory_ok?'The current memory limit looks reasonable for a typical site.':'This memory limit may be low for heavier plugins or growing sites.','points'=>$memory_ok?0:8], ['label'=>'Primary Page Cache','value'=>$page_cache['primary'],'status'=>$page_cache['primary']==='None detected'?'warning':'good','message'=>$page_cache['primary']==='None detected'?'No clear page-cache signal was detected from plugins, constants, or cache folders.':'A page cache layer was detected from the local WordPress setup.','meta'=>implode(' · ', $page_cache['signals']),'points'=>$page_cache['primary']==='None detected'?12:0], ['label'=>'Primary Object Cache','value'=>$object_cache['primary'],'status'=>$object_cache['primary']==='None detected'?'warning':'good','message'=>$object_cache['primary']==='None detected'?'No persistent object cache signal was detected.':'A persistent object cache signal was detected.','meta'=>implode(' · ', $object_cache['signals']),'points'=>$object_cache['primary']==='None detected'?4:0], ['label'=>'WooCommerce','value'=>$wc_active?'Active':'Inactive','status'=>'neutral','message'=>$wc_active?'WooCommerce is active. Cache behavior can vary more on dynamic shop pages.':'WooCommerce is not active on this site.','points'=>0], ['label'=>'WP_DEBUG','value'=>$wp_debug?'Enabled':'Disabled','status'=>$wp_debug?'warning':'good','message'=>$wp_debug?'Debug mode is enabled. That is usually not ideal on production.':'Debug mode appears disabled, which is typical for production.','points'=>$wp_debug?4:0], ['label'=>'REST API','value'=>$rest_ok?'Available':'Restricted','status'=>$rest_ok?'good':'neutral','message'=>$rest_ok?'The REST API appears reachable from WordPress.':'The REST API may be restricted by custom rules or security settings.','points'=>0], ['label'=>'WP-Cron Mode','value'=>$cron_disabled?'Disabled (system cron expected)':'Enabled','status'=>$cron_disabled?'neutral':'good','message'=>$cron_disabled?'WP-Cron is disabled, so a real system cron should exist.':'WP-Cron appears enabled in the default mode.','points'=>0] ]; $performance_score = ($memory_ok ? 100 : 85); $cache_score = min(100, ($page_cache['primary'] !== 'None detected' ? 70 : 30) + (int) ($frontend['confidence'] / 2)); $server_score = ($php_ok && $memory_ok ? 100 : 85); $maintenance_score = ($wp_debug ? 88 : 100); $score = (int) round(($performance_score * 0.25) + ($cache_score * 0.35) + ($server_score * 0.20) + ($maintenance_score * 0.20)); $score = max(0, min(100, $score)); if($score>=85) $score_summary='This site looks healthy overall. Only smaller improvements may be worth reviewing.'; elseif($score>=70) $score_summary='Your site looks usable, but there are some technical improvements worth addressing.'; elseif($score>=50) $score_summary='This site has a few meaningful health or cache signals worth reviewing.'; else $score_summary='This site likely needs attention before it can be considered technically healthy.'; $recommendations=[]; if(!$memory_ok) $recommendations[]='Consider increasing the WordPress memory limit if this site grows or uses heavier plugins.'; if($object_cache['primary']==='None detected') $recommendations[]='Consider enabling persistent object cache if this site becomes busier or uses heavier plugins.'; if(!empty($frontend['recommendation'])) $recommendations[]=$frontend['recommendation']; if($comparison && !empty($comparison['summary'])) $recommendations[]=$comparison['summary']; $report=['score'=>$score,'summary'=>$score_summary,'generated_at'=>current_time('mysql'),'checks'=>$checks,'recommendations'=>array_values(array_unique(array_filter($recommendations))),'primary_page_cache'=>$page_cache['primary'],'primary_object_cache'=>$object_cache['primary'],'snapshot'=>['Home URL'=>home_url('/'),'PHP'=>PHP_VERSION,'WordPress'=>$wp_version,'Theme'=>wp_get_theme()->get('Name'),'Site Type'=>$wc_active ? 'WooCommerce / store capable' : 'Standard WordPress site','Report Generated'=>current_time('mysql')],'frontend_cache_test'=>$frontend,'comparison'=>$comparison,'cache_intelligence'=>ageroth_sh_cache_intelligence($page_cache,$frontend),'object_cache_intelligence'=>ageroth_sh_object_cache_intelligence($object_cache),'woocommerce_intelligence'=>ageroth_sh_wc_intelligence(),'server_profile'=>ageroth_sh_server_profile($frontend),'score_breakdown'=>['Performance'=>$performance_score,'Caching'=>$cache_score,'Server'=>$server_score,'Maintenance'=>$maintenance_score]]; set_transient($cache_key,$report,300); return $report; } function ageroth_sh_get_score_level($score){ if($score>=85) return'good'; if($score>=60) return'medium'; return'poor'; } function ageroth_sh_get_recommendation_groups($report) { $good = array(); $consider = array(); if (!empty($report['checks']) && is_array($report['checks'])) { foreach ($report['checks'] as $check) { $label = isset($check['label']) ? $check['label'] : ''; $status = isset($check['status']) ? $check['status'] : ''; $value = isset($check['value']) ? $check['value'] : ''; if ($status === 'good') { $good[] = trim($label . ($value !== '' ? ': ' . $value : '')); } elseif ($status === 'warning') { $consider[] = !empty($check['message']) ? $check['message'] : $label; } } } if (!empty($report['primary_page_cache']) && $report['primary_page_cache'] !== 'None detected') { $good[] = 'Page cache layer detected: ' . $report['primary_page_cache']; } else { $consider[] = 'Enable a page cache layer to improve response times.'; } if (!empty($report['primary_object_cache']) && $report['primary_object_cache'] !== 'None detected') { $good[] = 'Persistent object cache signal detected: ' . $report['primary_object_cache']; } else { $consider[] = 'No persistent object cache detected. Redis or another object cache can help larger sites.'; } if (!empty($report['frontend_cache_test']['confidence']) && (int) $report['frontend_cache_test']['confidence'] >= 35) { $good[] = 'Frontend cache timing produced a useful signal.'; } else { $consider[] = 'No explicit frontend cache HIT header was exposed. This can be normal, but testing another public URL may help.'; } return array( 'good' => array_values(array_unique(array_slice($good, 0, 8))), 'consider' => array_values(array_unique(array_slice($consider, 0, 8))), ); } function ageroth_sh_get_cache_intelligence_v2($report) { $frontend = isset($report['frontend_cache_test']) ? $report['frontend_cache_test'] : array(); $headers = isset($frontend['headers']) && is_array($frontend['headers']) ? $frontend['headers'] : array(); $methods = array(); if (!empty($report['primary_page_cache']) && $report['primary_page_cache'] !== 'None detected') { $methods[] = 'Plugin / local signal'; } foreach ($headers as $header) { if (stripos($header, 'x-') !== false || stripos($header, 'cf-cache-status') !== false || stripos($header, 'age:') !== false) { $methods[] = 'Response header'; break; } } if (!empty($frontend['neutral_hits'])) { $methods[] = 'HTML / weak cache marker'; } return array( 'Cache Layer' => !empty($report['primary_page_cache']) ? $report['primary_page_cache'] : 'Not detected', 'Detection Method' => !empty($methods) ? implode(' + ', array_values(array_unique($methods))) : 'No clear cache-specific method', 'Live Request Analysis' => !empty($frontend['status_label']) ? $frontend['status_label'] : 'Not tested', 'Timing Analysis' => !empty($frontend['timing_signal']) ? ageroth_sh_timing_signal_label($frontend['timing_signal']) : 'Not available', 'Final Verdict' => !empty($frontend['response_type']) ? $frontend['response_type'] : 'Unknown', ); } function ageroth_sh_build_weekly_report_body($report) { $groups = ageroth_sh_get_recommendation_groups($report); $lines = array('Ageroth Site Health Weekly Report', '', 'Site: ' . home_url('/')); $lines[] = 'Overall Score: ' . (isset($report['score']) ? $report['score'] : 'Unknown'); $lines[] = 'Page Cache: ' . (isset($report['primary_page_cache']) ? $report['primary_page_cache'] : 'Unknown'); $lines[] = 'Object Cache: ' . (isset($report['primary_object_cache']) ? $report['primary_object_cache'] : 'Unknown'); $lines[] = 'Generated: ' . current_time('mysql'); $lines[] = ''; if (!empty($report['score_breakdown']) && is_array($report['score_breakdown'])) { $lines[] = 'Score Breakdown:'; foreach ($report['score_breakdown'] as $label => $score) { $lines[] = '- ' . $label . ': ' . $score; } $lines[] = ''; } if (!empty($groups['good'])) { $lines[] = 'Good:'; foreach ($groups['good'] as $item) { $lines[] = '- ' . $item; } $lines[] = ''; } if (!empty($groups['consider'])) { $lines[] = 'Consider:'; foreach ($groups['consider'] as $item) { $lines[] = '- ' . $item; } $lines[] = ''; } $lines[] = 'Open WordPress admin to review the full report.'; $lines[] = 'This email was generated locally by the Ageroth Site Health plugin. No data was sent to external services.'; return implode("\\n", $lines); } function ageroth_sh_send_weekly_health_report($force_send = false) { $settings = ageroth_sh_get_settings(); if (empty($settings['weekly_report_enabled']) && !$force_send) { return false; } $email = isset($settings['alert_email']) ? $settings['alert_email'] : ''; if (!is_email($email)) { return false; } $report = ageroth_sh_build_report(); $subject = sprintf('[%s] Weekly Site Health Report', wp_specialchars_decode(get_bloginfo('name'), ENT_QUOTES)); $sent = wp_mail($email, $subject, ageroth_sh_build_weekly_report_body($report)); $state = ageroth_sh_alert_state(); $state['last_weekly_report'] = $sent ? current_time('mysql') : ''; $state['last_weekly_report_status'] = $sent ? 'sent' : 'failed'; ageroth_sh_update_alert_state($state); return $sent; } function ageroth_sh_render_dashboard(){ ?>
Ageroth

Ageroth Site Health

Read-only WordPress health checks, cache intelligence, and server diagnostics.

Site Health Overview

Free 2.3.1

Health Score

$value): ?>

Health Score Breakdown

The final score is calculated from the weighted balance of performance, caching, server, and maintenance signals.

Final Score:
$value): ?>

Cache Intelligence

Object Cache / Redis Intelligence

WooCommerce Intelligence

Server Intelligence

What this means

What to do next

Live Cache Test

Tested URL
Final HTTP Status
Confidence
%
Request 1
First live request
Request 2
Second live request
Timing Signal
Timing Interpretation
Double-Request Detection

Homepage vs Inner-Page Comparison

Primary URL Result
Compare URL Result
Comparison Takeaway

System Checks

Recommended Improvements


What would make this report more useful?

Recent Feedback

  • :
Technical Details

Matched Signals

Negative Signals

Neutral Cache Signals

Why this may be uncached

Snapshot

    $value): ?>
  • :

Export Summary

Ageroth Site Health Settings

Free 2.3.1

Weekly report processed. Check your inbox.

Test alert processed. Check the alert status below and your inbox.

Test URLs


Email Alerts

Email alerts are local and use WordPress mail. No data is sent to external services.


Alert Status

This plugin is read-only and does not collect or send personal data.