PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.1
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.1
2.7.1 2.7.2 2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 All 141 releases
← All changes | otto/Otto_pixel_class.php +50 -10 2.6.22 → 2.7.1 View file →
@@ -100,10 +100,10 @@
100 100 return false;
101 101 }
102 102
103 103 # Parse saved domain + incoming URL
104 - $saved_domain = parse_url($saved['domain'], PHP_URL_HOST);
105 - $incoming_domain = parse_url($url, PHP_URL_HOST);
104 + $saved_domain = wp_parse_url($saved['domain'], PHP_URL_HOST);
105 + $incoming_domain = wp_parse_url($url, PHP_URL_HOST);
106 106
107 107 # Domain mismatch - not crawled
108 108 if (empty($saved_domain) || empty($incoming_domain) || strcasecmp($saved_domain, $incoming_domain) !== 0) {
109 109 return false;
@@ -109,9 +109,9 @@
109 109 return false;
110 110 }
111 111
112 112 # Parse incoming path (ignore query string)
113 - $parsed_url = parse_url($url);
113 + $parsed_url = wp_parse_url($url);
114 114 $url_path = $parsed_url['path'] ?? '/';
115 115
116 116 # Ensure path starts with / but don't modify trailing slashes
117 117 # Otto stores paths exactly as they appear in URLs
@@ -196,8 +196,21 @@
196 196
197 197 # render route html
198 198 function render_route_html(){
199 199
200 + # Headless frontends do not consume WordPress-rendered HTML, so avoid
201 + # suggestion lookups, buffering and internal fetches altogether.
202 + # Falls back to a guarded direct read rather than skipping the check:
203 + # silently skipping would render OTTO markup on a headless site.
204 + # @phpstan-ignore-next-line function.alreadyNarrowedType
205 + $headless = function_exists('metasync_headless_is_active')
206 + ? metasync_headless_is_active()
207 + : (class_exists('Metasync_Headless_Config') && Metasync_Headless_Config::is_active());
208 +
209 + if ($headless) {
210 + return;
211 + }
212 +
200 213 # Clear per-request statics before any path runs. Harmless under PHP-FPM
201 214 # (the process ends with the request) but required under persistent-worker
202 215 # SAPIs, where a latched flag would silently disable cache capping and
203 216 # failure reporting for every later request in that worker.
@@ -298,15 +311,13 @@
298 311
299 312 # comment out for fixing pagination issues
300 313 # $route = rtrim($route, '/');
301 314
302 - # Now that OTTO has confirmed suggestions to apply to this URL,
303 - # disable SiteGround SG Optimizer page caching for THIS request only.
304 - # Doing it here (instead of unconditionally on the `wp` hook) ensures
305 - # pages without OTTO suggestions keep being served from SG's page cache.
306 - if (function_exists('metasync_otto_disable_sg_page_cache')) {
307 - metasync_otto_disable_sg_page_cache();
308 - }
315 + # SiteGround's page cache is no longer disabled up-front for every OTTO page.
316 + # The cacheability decision now happens on the HTTP path in render_via_http(),
317 + # AFTER the internal fetch — only session/form pages and incomplete renders are
318 + # blocked, so plain content pages are finally cacheable. (SiteGround always uses
319 + # the HTTP path; the disable is a no-op off SiteGround, so nothing else loses it.)
309 320
310 321 # Analyze what OTTO is providing for SEO plugin blocking
311 322 $blocking_flags = $this->analyze_otto_blocking($suggestions);
312 323
@@ -680,8 +691,16 @@
680 691 $route_html = $this->get_route_html($route, $cache_track_key, $suggestions);
681 692
682 693 # check that route html is valid
683 694 if(empty($route_html)){
695 + # Fetch failed / empty (e.g. SG Optimizer truncated the internal response) —
696 + # WordPress will serve the un-optimized page. Cap how long that copy may be
697 + # cached, using the same mechanism as the deliberate skips above rather than a
698 + # second, competing one: it reconciles existing directives, keeps logged-in
699 + # responses fully no-store, and never grants caching nothing had granted.
700 + if (function_exists('metasync_otto_limit_response_cache')) {
701 + metasync_otto_limit_response_cache('HTTP_FETCH_FAILED');
702 + }
684 703 return false;
685 704 }
686 705
687 706 $route_html_string = is_string($route_html) ? $route_html : $route_html->__toString();
@@ -796,10 +815,31 @@
796 815 }
797 816 }
798 817 }
799 818
819 + # Decide cacheability from the internal fetch + the final output, then serve.
820 + # 1) Hand the visitor the session their page was built around (fixes forms that
821 + # were silently failing on the HTTP path because the fetch cookie was dropped).
822 + # 2) Block shared caching when the fetched page set a cookie, when its own render
823 + # declared itself uncacheable (PHP's session cache-limiter — true for resumed
824 + # sessions too), when the visitor carries cart/auth cookies that change the
825 + # rendered content, or when the render came back incomplete.
826 + # 3) A plain, complete page gets no cache header at all → the host finally caches
827 + # it (the SiteGround/Oxygen relief this ticket is about).
828 + Metasync_Otto_Render_Strategy::passthrough_http_fetch_cookies();
829 +
830 + $must_not_cache =
831 + Metasync_Otto_Render_Strategy::http_fetch_had_cookie()
832 + || Metasync_Otto_Render_Strategy::http_fetch_no_cache()
833 + || Metasync_Otto_Render_Strategy::request_has_visitor_specific_cookie()
834 + || !Metasync_Otto_Render_Strategy::http_output_is_complete($route_html_string);
835 +
800 836 # Send response headers
801 837 Metasync_Otto_Render_Strategy::send_headers($cache_status);
838 +
839 + if ($must_not_cache) {
840 + Metasync_Otto_Render_Strategy::block_http_cache();
841 + }
802 842
803 843 # continue to render the html
804 844 echo $route_html_string;
805 845