string, 'headers' => array } */ public static function browser_profile() { $ua = apply_filters( 'ai_o_optimiser_browser_user_agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' ); return array( 'user-agent' => (string) $ua, 'headers' => array( 'Accept-Language' => 'en-AU,en;q=0.9', 'Upgrade-Insecure-Requests' => '1', 'Sec-Fetch-Dest' => 'document', 'Sec-Fetch-Mode' => 'navigate', 'Sec-Fetch-Site' => 'none', 'Sec-Fetch-User' => '?1', ), ); } /** * Apply a fetch profile to wp_safe_remote_* args. * * @param array $args Base request args (timeout, redirection, headers…). * @param string $profile 'browser' or 'identified'. * @param string $purpose Purpose label for the identified UA. * @return array Args with user-agent and profile headers merged in. */ public static function profile_args( array $args, $profile, $purpose ) { $headers = isset( $args['headers'] ) && is_array( $args['headers'] ) ? $args['headers'] : array(); // Any UA present in base headers is replaced by the profile UA. unset( $headers['User-Agent'], $headers['user-agent'] ); if ( 'browser' === $profile ) { $browser = self::browser_profile(); $args['user-agent'] = $browser['user-agent']; $args['headers'] = array_merge( $headers, $browser['headers'] ); } else { $args['user-agent'] = self::identified_user_agent( $purpose ); $args['headers'] = $headers; } return $args; } /** * GET a URL with a two-tier profile ladder. * * Tries $primary_profile first. If the response is a WAF-style block and * the fallback filter allows it, retries once with the other profile. * Never converts a genuine page error (404/500) into a retry. * * @param string $url Target URL. * @param array $args Base request args. * @param string $primary_profile 'browser' or 'identified'. * @param string $purpose Purpose label for the identified UA. * @return array { * response array|WP_Error Final (or last failed) response. * profile string Profile that produced the final response. * primary_code int HTTP code the primary profile received. * recovered bool True when the fallback profile succeeded * after the primary was WAF-blocked. * } */ public static function get_with_fallback( $url, array $args, $primary_profile, $purpose ) { $primary_profile = ( 'browser' === $primary_profile ) ? 'browser' : 'identified'; $other_profile = ( 'browser' === $primary_profile ) ? 'identified' : 'browser'; $response = wp_safe_remote_get( $url, self::profile_args( $args, $primary_profile, $purpose ) ); $primary_code = is_wp_error( $response ) ? 0 : (int) wp_remote_retrieve_response_code( $response ); $result = array( 'response' => $response, 'profile' => $primary_profile, 'primary_code' => $primary_code, 'recovered' => false, ); if ( is_wp_error( $response ) || ! self::is_waf_block_code( $primary_code ) ) { return $result; } if ( ! apply_filters( 'ai_o_optimiser_browser_fallback', true, $url, $primary_code ) ) { return $result; } $retry = wp_safe_remote_get( $url, self::profile_args( $args, $other_profile, $purpose ) ); $retry_code = is_wp_error( $retry ) ? 0 : (int) wp_remote_retrieve_response_code( $retry ); if ( ! is_wp_error( $retry ) && $retry_code >= 200 && $retry_code < 400 ) { $result['response'] = $retry; $result['profile'] = $other_profile; $result['recovered'] = true; } return $result; } /** * Standard operator-facing note for a recovered fetch: the edge blocked * one profile but served the other. For an AI-visibility product this is * a headline signal, not noise. * * @param int $blocked_code HTTP code the blocked profile received. * @param string $blocked_profile Profile that was blocked. * @return string */ public static function edge_block_note( $blocked_code, $blocked_profile = 'identified' ) { if ( 'browser' === $blocked_profile ) { return sprintf( /* translators: %d: HTTP status code */ __( 'Edge firewall refused a browser-profile request (HTTP %d) but served an identified one — unusual; review WAF rules, IP reputation and rate limits.', 'ai-o-optimiser' ), (int) $blocked_code ); } return sprintf( /* translators: %d: HTTP status code */ __( 'Edge firewall blocks unrecognised automated agents (HTTP %d to an identified user agent; browser profile was served). AI crawlers such as GPTBot and ClaudeBot are likely affected too — verify they are allowlisted in your WAF/bot-management layer, or both their crawling and this plugin\'s scans will be blocked.', 'ai-o-optimiser' ), (int) $blocked_code ); } }