From dae7f2ac24a733a8c94c3d464adb22b3175ede32 Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Tue, 26 Aug 2025 19:50:35 -0700 Subject: [PATCH 01/37] Initialized with queries from 2024 --- .../a11y_overall_tech_usage_by_rank.sql | 61 +++++++++ .../third-parties/a11y_technology_usage.sql | 35 ++++++ .../a11y_technology_usage_by_rank.sql | 65 ++++++++++ .../third-parties/compressed_images_by_3p.sql | 81 ++++++++++++ sql/2025/third-parties/content_encoding.sql | 51 ++++++++ .../content_encoding_by_content_type.sql | 55 +++++++++ .../csp_allowed_host_frequency.sql | 84 +++++++++++++ sql/2025/third-parties/depth_of_gtm_calls.sql | 93 ++++++++++++++ ...distribution_of_3XX_response_body_size.sql | 64 ++++++++++ ...ion_of_lighthouse_unminified_css_by_3p.sql | 61 +++++++++ ...tion_of_lighthouse_unminified_js_by_3p.sql | 62 ++++++++++ ...ibution_of_lighthouse_unused_css_by_3p.sql | 62 ++++++++++ ...ribution_of_lighthouse_unused_js_by_3p.sql | 62 ++++++++++ ...lighthouse_uses_optimized_images_by_3p.sql | 61 +++++++++ ...tion_of_size_and_time_by_third_parties.sql | 65 ++++++++++ ...distribution_of_third_parties_by_frame.sql | 86 +++++++++++++ ...of_third_parties_by_number_of_websites.sql | 65 ++++++++++ ...of_websites_by_number_of_third_parties.sql | 63 ++++++++++ ...tes_by_number_of_third_party_providers.sql | 63 ++++++++++ .../third-parties/iframe_allow_attribute.sql | 45 +++++++ .../iframe_attribute_popular_hosts.sql | 54 ++++++++ ...ighthouse_average_unminified_css_by_3p.sql | 44 +++++++ ...lighthouse_average_unminified_js_by_3p.sql | 42 +++++++ .../lighthouse_third_party_facades.sql | 20 +++ .../lighthouse_unminified_css_by_3p.sql | 70 +++++++++++ .../lighthouse_unminified_js_by_3p.sql | 70 +++++++++++ .../lighthouse_unminified_js_by_3p_by_url.sql | 75 +++++++++++ ...unminified_uses_optimized_images_by_3p.sql | 70 +++++++++++ .../lighthouse_unused_css_bytes_by_3p.sql | 70 +++++++++++ .../lighthouse_unused_js_bytes_by_3p.sql | 70 +++++++++++ .../number_of_third_parties_by_rank.sql | 82 +++++++++++++ ..._of_third_parties_by_rank_and_category.sql | 84 +++++++++++++ ...umber_of_third_party_providers_by_rank.sql | 78 ++++++++++++ ...d_party_providers_by_rank_and_category.sql | 84 +++++++++++++ ...number_of_third_party_requests_by_rank.sql | 60 +++++++++ ..._third_party_requests_per_page_by_rank.sql | 60 +++++++++ ...rcent_of_third_parties_by_content_type.sql | 53 ++++++++ ..._of_third_parties_using_document_write.sql | 71 +++++++++++ ..._third_parties_using_legacy_javascript.sql | 71 +++++++++++ ...parties_using_legacy_javascript_by_url.sql | 88 +++++++++++++ .../percent_of_third_party_cache.sql | 75 +++++++++++ ...and_bytes_by_category_and_content_type.sql | 86 +++++++++++++ ...t_of_third_party_with_security_headers.sql | 73 +++++++++++ .../percent_of_websites_with_third_party.sql | 51 ++++++++ ...f_websites_with_third_party_by_ranking.sql | 64 ++++++++++ .../scripts_using_async_defer.sql | 76 ++++++++++++ .../scripts_using_async_defer_by_3p.sql | 81 ++++++++++++ sql/2025/third-parties/tao_by_third_party.sql | 105 ++++++++++++++++ .../third_parties_blocking_main_thread.sql | 71 +++++++++++ ...rties_blocking_main_thread_percentiles.sql | 59 +++++++++ ...ocking_main_thread_percentiles_by_host.sql | 70 +++++++++++ .../third_parties_blocking_rendering.sql | 116 ++++++++++++++++++ ...parties_blocking_rendering_percentiles.sql | 114 +++++++++++++++++ .../third_parties_using_legacy_javascript.sql | 57 +++++++++ ...d_parties_by_median_body_size_and_time.sql | 87 +++++++++++++ ...00_third_parties_by_number_of_websites.sql | 76 ++++++++++++ ...d_parties_by_client_and_frame_location.sql | 106 ++++++++++++++++ .../usage_of_lite_youtube_embed.sql | 37 ++++++ sql/2025/third-parties/usage_of_partytown.sql | 37 ++++++ 59 files changed, 4041 insertions(+) create mode 100644 sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql create mode 100644 sql/2025/third-parties/a11y_technology_usage.sql create mode 100644 sql/2025/third-parties/a11y_technology_usage_by_rank.sql create mode 100644 sql/2025/third-parties/compressed_images_by_3p.sql create mode 100644 sql/2025/third-parties/content_encoding.sql create mode 100644 sql/2025/third-parties/content_encoding_by_content_type.sql create mode 100644 sql/2025/third-parties/csp_allowed_host_frequency.sql create mode 100644 sql/2025/third-parties/depth_of_gtm_calls.sql create mode 100644 sql/2025/third-parties/distribution_of_3XX_response_body_size.sql create mode 100644 sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql create mode 100644 sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql create mode 100644 sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql create mode 100644 sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql create mode 100644 sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql create mode 100644 sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql create mode 100644 sql/2025/third-parties/distribution_of_third_parties_by_frame.sql create mode 100644 sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql create mode 100644 sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql create mode 100644 sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql create mode 100644 sql/2025/third-parties/iframe_allow_attribute.sql create mode 100644 sql/2025/third-parties/iframe_attribute_popular_hosts.sql create mode 100644 sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql create mode 100644 sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql create mode 100644 sql/2025/third-parties/lighthouse_third_party_facades.sql create mode 100644 sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql create mode 100644 sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql create mode 100644 sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql create mode 100644 sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql create mode 100644 sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql create mode 100644 sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql create mode 100644 sql/2025/third-parties/number_of_third_parties_by_rank.sql create mode 100644 sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql create mode 100644 sql/2025/third-parties/number_of_third_party_providers_by_rank.sql create mode 100644 sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql create mode 100644 sql/2025/third-parties/number_of_third_party_requests_by_rank.sql create mode 100644 sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql create mode 100644 sql/2025/third-parties/percent_of_third_parties_by_content_type.sql create mode 100644 sql/2025/third-parties/percent_of_third_parties_using_document_write.sql create mode 100644 sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql create mode 100644 sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql create mode 100644 sql/2025/third-parties/percent_of_third_party_cache.sql create mode 100644 sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql create mode 100644 sql/2025/third-parties/percent_of_third_party_with_security_headers.sql create mode 100644 sql/2025/third-parties/percent_of_websites_with_third_party.sql create mode 100644 sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql create mode 100644 sql/2025/third-parties/scripts_using_async_defer.sql create mode 100644 sql/2025/third-parties/scripts_using_async_defer_by_3p.sql create mode 100644 sql/2025/third-parties/tao_by_third_party.sql create mode 100644 sql/2025/third-parties/third_parties_blocking_main_thread.sql create mode 100644 sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql create mode 100644 sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql create mode 100644 sql/2025/third-parties/third_parties_blocking_rendering.sql create mode 100644 sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql create mode 100644 sql/2025/third-parties/third_parties_using_legacy_javascript.sql create mode 100644 sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql create mode 100644 sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql create mode 100644 sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql create mode 100644 sql/2025/third-parties/usage_of_lite_youtube_embed.sql create mode 100644 sql/2025/third-parties/usage_of_partytown.sql diff --git a/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql b/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql new file mode 100644 index 00000000000..df65fa3359c --- /dev/null +++ b/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql @@ -0,0 +1,61 @@ +#standardSQL +# Overall A11Y technology usage by domain rank + +WITH a11y_technologies AS ( + SELECT + _TABLE_SUFFIX AS client, + url + FROM + `httparchive.technologies.2024_06_01_*` + WHERE + category = 'Accessibility' +), + +pages AS ( + SELECT + _TABLE_SUFFIX AS client, + url, + rank_grouping + FROM + `httparchive.summary_pages.2024_06_01_*`, + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + WHERE + rank <= rank_grouping +), + +rank_totals AS ( + SELECT + _TABLE_SUFFIX AS client, + rank_grouping, + COUNT(0) AS total + FROM + `httparchive.summary_pages.2024_06_01_*`, + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + WHERE + rank <= rank_grouping + GROUP BY + client, + rank_grouping +) + +SELECT + client, + rank_grouping AS rank, + COUNT(DISTINCT url) AS freq, + total, + (COUNT(DISTINCT url) / total) * 100 AS pct +FROM + a11y_technologies +LEFT OUTER JOIN + pages +USING (client, url) +JOIN + rank_totals +USING (client, rank_grouping) +GROUP BY + rank_grouping, + total, + client +ORDER BY + client, + rank diff --git a/sql/2025/third-parties/a11y_technology_usage.sql b/sql/2025/third-parties/a11y_technology_usage.sql new file mode 100644 index 00000000000..ca76cdc1053 --- /dev/null +++ b/sql/2025/third-parties/a11y_technology_usage.sql @@ -0,0 +1,35 @@ +#standardSQL +# A11Y technology usage + +WITH a11y_technologies AS ( + SELECT + _TABLE_SUFFIX AS client, + COUNT(DISTINCT url) AS freq + FROM + `httparchive.technologies.2024_06_01_*` + WHERE + category = 'Accessibility' + GROUP BY + client +), + +pages AS ( + SELECT + _TABLE_SUFFIX AS client, + COUNT(0) AS total + FROM + `httparchive.summary_pages.2024_06_01_*` + GROUP BY + client +) + +SELECT + client, + freq, + total, + (freq / total) * 100 AS pct +FROM + a11y_technologies +JOIN + pages +USING (client) diff --git a/sql/2025/third-parties/a11y_technology_usage_by_rank.sql b/sql/2025/third-parties/a11y_technology_usage_by_rank.sql new file mode 100644 index 00000000000..d2cc1da3c0a --- /dev/null +++ b/sql/2025/third-parties/a11y_technology_usage_by_rank.sql @@ -0,0 +1,65 @@ +#standardSQL +# A11Y technology usage by domain rank + +WITH a11y_technologies AS ( + SELECT + _TABLE_SUFFIX AS client, + app, + url + FROM + `httparchive.technologies.2024_06_01_*` + WHERE + category = 'Accessibility' +), + +pages AS ( + SELECT + _TABLE_SUFFIX AS client, + url, + rank_grouping + FROM + `httparchive.summary_pages.2024_06_01_*`, + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + WHERE + rank <= rank_grouping +), + +rank_totals AS ( + SELECT + _TABLE_SUFFIX AS client, + rank_grouping, + COUNT(0) AS total + FROM + `httparchive.summary_pages.2024_06_01_*`, + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + WHERE + rank <= rank_grouping + GROUP BY + client, + rank_grouping +) + +SELECT + client, + rank_grouping AS rank, + app, + COUNT(0) AS freq, + total, + (COUNT(0) / total) * 100 AS pct +FROM + a11y_technologies +LEFT OUTER JOIN + pages +USING (client, url) +JOIN + rank_totals +USING (client, rank_grouping) +GROUP BY + rank_grouping, + total, + client, + app +ORDER BY + client, + rank, + pct DESC diff --git a/sql/2025/third-parties/compressed_images_by_3p.sql b/sql/2025/third-parties/compressed_images_by_3p.sql new file mode 100644 index 00000000000..a9b4db22682 --- /dev/null +++ b/sql/2025/third-parties/compressed_images_by_3p.sql @@ -0,0 +1,81 @@ +#standardSQL +# Compressed images (excluding SVG) by third parties + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url, + resp_content_encoding AS content_encoding, + type, + respBodySize AS size + FROM + `httparchive.summary_requests.2024_06_01_*` + WHERE + type = 'image' AND ( + resp_content_encoding = 'gzip' OR + resp_content_encoding = 'br' + ) AND NOT ( + resp_content_type LIKE 'image/svg%' OR + ENDS_WITH(url, '.svg') + ) +), + +third_party AS ( + SELECT + NET.HOST(domain) AS domain, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain + HAVING + page_usage >= 50 +) + +SELECT + client, + content_encoding, + domain, + size, + SUM(size) OVER (PARTITION BY client) AS total_size, + size / SUM(size) OVER (PARTITION BY client) AS pct_size, + num_requests, + total_requests, + pct_requests +FROM ( + SELECT + client, + content_encoding, + domain, + COUNT(0) AS num_requests, + SUM(size) AS size, + SUM(COUNT(0)) OVER (PARTITION BY client) AS total_requests, + COUNT(0) / SUM(COUNT(0)) OVER (PARTITION BY client) AS pct_requests, + RANK() OVER (PARTITION BY client, type, content_encoding ORDER BY COUNT(0) DESC) AS domain_rank + FROM + requests + LEFT JOIN + third_party + ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) + WHERE + domain IS NOT NULL + GROUP BY + client, + type, + content_encoding, + domain +) +WHERE + domain_rank <= 100 +ORDER BY + client, + content_encoding, + size DESC diff --git a/sql/2025/third-parties/content_encoding.sql b/sql/2025/third-parties/content_encoding.sql new file mode 100644 index 00000000000..f79dad7fdbb --- /dev/null +++ b/sql/2025/third-parties/content_encoding.sql @@ -0,0 +1,51 @@ +#standardSQL +#content-encoding by third parties + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url, + resp_content_encoding AS content_encoding + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + NET.HOST(domain) AS domain, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain + HAVING + page_usage >= 50 +) + +SELECT + client, + content_encoding, + COUNT(0) AS num_requests, + SUM(COUNT(0)) OVER (PARTITION BY client) AS total, + COUNT(0) / SUM(COUNT(0)) OVER (PARTITION BY client) AS pct +FROM + requests +LEFT JOIN + third_party +ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) +WHERE + domain IS NOT NULL +GROUP BY + client, + content_encoding +ORDER BY + client, + num_requests DESC diff --git a/sql/2025/third-parties/content_encoding_by_content_type.sql b/sql/2025/third-parties/content_encoding_by_content_type.sql new file mode 100644 index 00000000000..efd2a7cdae1 --- /dev/null +++ b/sql/2025/third-parties/content_encoding_by_content_type.sql @@ -0,0 +1,55 @@ +#standardSQL +#content-encoding by third parties by content-type + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url, + resp_content_encoding AS content_encoding, + type + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + NET.HOST(domain) AS domain, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain + HAVING + page_usage >= 50 +) + +SELECT + client, + type, + content_encoding, + COUNT(0) AS num_requests, + SUM(COUNT(0)) OVER (PARTITION BY client, type) AS total, + COUNT(0) / SUM(COUNT(0)) OVER (PARTITION BY client, type) AS pct +FROM + requests +LEFT JOIN + third_party +ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) +WHERE + domain IS NOT NULL +GROUP BY + client, + type, + content_encoding +ORDER BY + client, + type, + num_requests DESC diff --git a/sql/2025/third-parties/csp_allowed_host_frequency.sql b/sql/2025/third-parties/csp_allowed_host_frequency.sql new file mode 100644 index 00000000000..95d169ce290 --- /dev/null +++ b/sql/2025/third-parties/csp_allowed_host_frequency.sql @@ -0,0 +1,84 @@ +#standardSQL +# CSP on home pages: most prevalent allowed hosts + +CREATE TEMPORARY FUNCTION getHeader(headers STRING, headername STRING) +RETURNS STRING DETERMINISTIC +LANGUAGE js AS ''' + const parsed_headers = JSON.parse(headers); + const matching_headers = parsed_headers.filter(h => h.name.toLowerCase() == headername.toLowerCase()); + if (matching_headers.length > 0) { + return matching_headers[0].value; + } + return null; +'''; + +WITH totals AS ( + SELECT + client, + COUNT(0) AS total + FROM + `httparchive.all.requests` + WHERE + date = '2024-06-01' AND + is_main_document + GROUP BY + client +), + +csp_data AS ( + SELECT + client, + page, + getHeader(TO_JSON_STRING(response_headers), 'Content-Security-Policy') AS csp_header + FROM + `httparchive.all.requests` + WHERE + date = '2024-06-01' AND + is_main_document AND + response_headers IS NOT NULL +), + +csp_expanded AS ( + SELECT + client, + page, + csp_allowed_host + FROM + csp_data, + UNNEST(REGEXP_EXTRACT_ALL(csp_header, r'(?i)(https*://[^\s;]+)[\s;]')) AS csp_allowed_host + WHERE + csp_header IS NOT NULL +), + +ranked_csp AS ( + SELECT + client, + csp_allowed_host, + COUNT(DISTINCT page) AS freq, + total AS total_pages, + COUNT(DISTINCT page) / total AS pct, + RANK() OVER (PARTITION BY client ORDER BY COUNT(DISTINCT page) DESC) AS csp_allowed_host_rank + FROM + csp_expanded + JOIN + totals + USING (client) + GROUP BY + client, + total, + csp_allowed_host +) + +SELECT + client, + csp_allowed_host, + freq, + total_pages, + pct +FROM + ranked_csp +WHERE + csp_allowed_host_rank <= 100 +ORDER BY + client, + pct DESC; diff --git a/sql/2025/third-parties/depth_of_gtm_calls.sql b/sql/2025/third-parties/depth_of_gtm_calls.sql new file mode 100644 index 00000000000..46cc89546d0 --- /dev/null +++ b/sql/2025/third-parties/depth_of_gtm_calls.sql @@ -0,0 +1,93 @@ +CREATE TEMP FUNCTION findAllInitiators(rootPage STRING, data ARRAY>) +RETURNS ARRAY +LANGUAGE js AS """ + // Helper function to find all initiator_etlds for a given root_page + function findInitiators(page, visited, data) { + // Find all entries where the root_page matches and the initiator_etld hasn't been visited + const initiators = data + .filter(row => row.root_page === page && !visited.includes(row.initiator_etld)) + .map(row => row.initiator_etld); + + // Add the newly found initiators to the visited list + visited = visited.concat(initiators); + + // Recursively process all new initiators + initiators.forEach(initiator => { + visited = findInitiators(initiator, visited, data); + }); + + return visited; + } + + // Main call: Start recursion from the rootPage + // Use a Set to ensure that all returned values are distinct + return Array.from(new Set(findInitiators(rootPage, [], data))); +"""; + + +CREATE TEMP FUNCTION mean_depth_and_next_element_after_gtm(input_array ARRAY) +RETURNS STRUCT> +LANGUAGE js AS """ + // Initialize the array to hold names of next elements + const nextElements = []; + + // Traverse the input array to find "googletagmanager.com" and capture the next element + for (let i = 0; i < input_array.length - 1; i++) { // -1 to avoid out-of-bounds + if (input_array[i] === 'googletagmanager.com') { + nextElements.push(input_array[i + 1]); + } + } + + // If no "googletagmanager.com" is found, return NULL + if (nextElements.length === 0) { + return { mean_depth: null, next_elements: [] }; + } + + // Calculate mean depth for all next elements + const meanDepth = nextElements.length > 0 + ? nextElements.reduce((sum, _, idx) => sum + (idx + 2), 0) / nextElements.length + : null; + + // Return the result as a struct + return { mean_depth: meanDepth, next_elements: nextElements }; +"""; + + +WITH data AS ( + -- TP interact with other tps + SELECT + * + FROM ( + SELECT + client, + NET.REG_DOMAIN(root_page) AS root_page, + NET.REG_DOMAIN(url) AS third_party, + NET.REG_DOMAIN(JSON_VALUE(payload, '$._initiator')) AS initiator_etld + FROM + `httparchive.all.requests` + WHERE + NET.REG_DOMAIN(root_page) != NET.REG_DOMAIN(url) AND + date = '2024-06-01' + ) + WHERE third_party != initiator_etld AND + root_page != initiator_etld + GROUP BY client, root_page, third_party, initiator_etld +) + +SELECT client, next_elements_after_gtm, count(0) AS c FROM ( + SELECT + client, + result.mean_depth AS mean_depth_after_gtm, + result.next_elements AS next_elements_after_gtm + FROM ( + SELECT + root_page, + client, + findAllInitiators(root_page, ARRAY_AGG(STRUCT(root_page, third_party, initiator_etld))) AS all_initiators + FROM data + GROUP BY root_page, client + ), + UNNEST([mean_depth_and_next_element_after_gtm(all_initiators)]) AS result + WHERE result.mean_depth IS NOT NULL + ORDER BY mean_depth_after_gtm +) GROUP BY client, next_elements_after_gtm ORDER BY c; diff --git a/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql b/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql new file mode 100644 index 00000000000..9e4ca7954b3 --- /dev/null +++ b/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql @@ -0,0 +1,64 @@ +#standardSQL +# Distribution of response body size by redirected third parties +# HTTP status codes documentation: https://developer.mozilla.org/docs/Web/HTTP/Status + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url, + status, + respBodySize AS body_size + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + category + HAVING + page_usage >= 50 +), + +base AS ( + SELECT + client, + domain, + IF(status BETWEEN 300 AND 399, 1, 0) AS redirected, + body_size + FROM + requests + LEFT JOIN + third_party + ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) +) + +SELECT + client, + percentile, + APPROX_QUANTILES(body_size, 1000)[OFFSET(percentile * 10)] AS approx_redirect_body_size +FROM + base, + UNNEST(GENERATE_ARRAY(1, 100)) AS percentile +WHERE + redirected = 1 +GROUP BY + client, + percentile +ORDER BY + client, + percentile diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql new file mode 100644 index 00000000000..798d1bd4558 --- /dev/null +++ b/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql @@ -0,0 +1,61 @@ +#standardSQL +# Pages with unminified third-party CSS + +CREATE TEMPORARY FUNCTION getUnminifiedCssUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(({url, wastedBytes}) => { + return {url, wastedBytes}; + }); +} catch (e) { + return []; +} +'''; + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, + SUM(potential_savings) AS potential_total_savings + FROM ( + SELECT + _TABLE_SUFFIX AS client, + lighthouse.url AS page, + NET.HOST(data.url) AS domain, + data.wastedBytes AS potential_savings + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUnminifiedCssUrls(JSON_EXTRACT(report, "$.audits['unminified-css']"))) AS data + ) AS potential_third_parties + LEFT OUTER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page +) + +SELECT + client, + percentile, + APPROX_QUANTILES(potential_third_party_savings, 1000)[OFFSET(percentile * 10)] AS potential_third_party_savings_bytes, + APPROX_QUANTILES(potential_total_savings, 1000)[OFFSET(percentile * 10)] AS potential_total_savings_bytes +FROM + base, + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile +GROUP BY + client, + percentile +ORDER BY + client, + percentile diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql new file mode 100644 index 00000000000..99de8659ec9 --- /dev/null +++ b/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql @@ -0,0 +1,62 @@ +#standardSQL +# Pages with unminified third-party JavaScript + +CREATE TEMPORARY FUNCTION getUnminifiedJavascriptUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(({url, wastedBytes}) => { + return {url, wastedBytes}; + }); +} catch (e) { + return []; +} +'''; + + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, + SUM(potential_savings) AS potential_total_savings + FROM ( + SELECT + _TABLE_SUFFIX AS client, + lighthouse.url AS page, + NET.HOST(data.url) AS domain, + data.wastedBytes AS potential_savings + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUnminifiedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS data + ) AS potential_third_parties + LEFT OUTER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page +) + +SELECT + client, + percentile, + APPROX_QUANTILES(potential_third_party_savings, 1000)[OFFSET(percentile * 10)] AS potential_third_party_savings_bytes, + APPROX_QUANTILES(potential_total_savings, 1000)[OFFSET(percentile * 10)] AS potential_total_savings_bytes +FROM + base, + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile +GROUP BY + client, + percentile +ORDER BY + client, + percentile diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql new file mode 100644 index 00000000000..d760a82e9c2 --- /dev/null +++ b/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql @@ -0,0 +1,62 @@ +#standardSQL +# Pages with unused third-party CSS + +CREATE TEMPORARY FUNCTION getUnusedCSSUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(({url, wastedBytes}) => { + return {url, wastedBytes}; + }); +} catch (e) { + return []; +} +'''; + + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, + SUM(potential_savings) AS potential_total_savings + FROM ( + SELECT + _TABLE_SUFFIX AS client, + lighthouse.url AS page, + NET.HOST(data.url) AS domain, + data.wastedBytes AS potential_savings + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUnusedCSSUrls(JSON_EXTRACT(report, "$.audits['unused-css-rules']"))) AS data + ) AS potential_third_parties + LEFT OUTER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page +) + +SELECT + client, + percentile, + APPROX_QUANTILES(potential_third_party_savings, 1000)[OFFSET(percentile * 10)] AS potential_third_party_savings_bytes, + APPROX_QUANTILES(potential_total_savings, 1000)[OFFSET(percentile * 10)] AS potential_total_savings_bytes +FROM + base, + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile +GROUP BY + client, + percentile +ORDER BY + client, + percentile diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql new file mode 100644 index 00000000000..60688c36785 --- /dev/null +++ b/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql @@ -0,0 +1,62 @@ +#standardSQL +# Pages with unused third-party JavaScript + +CREATE TEMPORARY FUNCTION getUnusedJavascriptUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(({url, wastedBytes}) => { + return {url, wastedBytes}; + }); +} catch (e) { + return []; +} +'''; + + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, + SUM(potential_savings) AS potential_total_savings + FROM ( + SELECT + _TABLE_SUFFIX AS client, + lighthouse.url AS page, + NET.HOST(data.url) AS domain, + data.wastedBytes AS potential_savings + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUnusedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unused-javascript']"))) AS data + ) AS potential_third_parties + LEFT OUTER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page +) + +SELECT + client, + percentile, + APPROX_QUANTILES(potential_third_party_savings, 1000)[OFFSET(percentile * 10)] AS potential_third_party_savings_bytes, + APPROX_QUANTILES(potential_total_savings, 1000)[OFFSET(percentile * 10)] AS potential_total_savings_bytes +FROM + base, + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile +GROUP BY + client, + percentile +ORDER BY + client, + percentile diff --git a/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql new file mode 100644 index 00000000000..f4aebe8b831 --- /dev/null +++ b/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql @@ -0,0 +1,61 @@ +#standardSQL +# Third-party pages with unoptimized images + +CREATE TEMPORARY FUNCTION getUnminifiedImageUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(({url, wastedBytes}) => { + return {url, wastedBytes}; + }); +} catch (e) { + return []; +} +'''; + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, + SUM(potential_savings) AS potential_total_savings + FROM ( + SELECT + _TABLE_SUFFIX AS client, + lighthouse.url AS page, + NET.HOST(data.url) AS domain, + data.wastedBytes AS potential_savings + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUnminifiedImageUrls(JSON_EXTRACT(report, "$.audits['uses-optimized-images']"))) AS data + ) AS potential_third_parties + LEFT OUTER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page +) + +SELECT + client, + percentile, + APPROX_QUANTILES(potential_third_party_savings, 1000)[OFFSET(percentile * 10)] AS potential_third_party_savings_bytes, + APPROX_QUANTILES(potential_total_savings, 1000)[OFFSET(percentile * 10)] AS potential_total_savings_bytes +FROM + base, + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile +GROUP BY + client, + percentile +ORDER BY + client, + percentile diff --git a/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql b/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql new file mode 100644 index 00000000000..88c7138ecbf --- /dev/null +++ b/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql @@ -0,0 +1,65 @@ +#standardSQL +# Distribution of third party requests size and time by category + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url, + respBodySize AS body_size, + time + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + category + HAVING + page_usage >= 50 +), + +base AS ( + SELECT + client, + category, + body_size, + time + FROM + requests + INNER JOIN + third_party + ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) +) + +SELECT + client, + category, + percentile, + APPROX_QUANTILES(body_size, 1000)[OFFSET(percentile * 10)] AS body_size, + APPROX_QUANTILES(time, 1000)[OFFSET(percentile * 10)] AS time -- noqa: L010 +FROM + base, + UNNEST(GENERATE_ARRAY(1, 100)) AS percentile +GROUP BY + client, + category, + percentile +ORDER BY + client, + category, + percentile diff --git a/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql b/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql new file mode 100644 index 00000000000..5b19681ec76 --- /dev/null +++ b/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql @@ -0,0 +1,86 @@ +#standardSQL +# Distribution of third-parties embedded in main vs. in iframes + +WITH document_frameid AS ( + SELECT + client, + NET.HOST(page) AS page_host, + NET.HOST(url) AS frame_host, + CASE + WHEN is_main_document = true + THEN JSON_EXTRACT_SCALAR(payload, '$._frame_id') + END AS mainframe_id, + JSON_EXTRACT_SCALAR(payload, '$._frame_id') AS frame_id, + is_main_document + FROM `httparchive.all.requests` AS requests + WHERE requests.date = '2024-06-01' AND requests.is_root_page = true +), + +page_frames AS ( + SELECT + client, + page_host, + frame_host, + CASE + WHEN frame_host != page_host + THEN true + ELSE false + END AS tp_flag, + is_main_document, + frame_id, + COALESCE(mainframe_id, FIRST_VALUE(mainframe_id) OVER (PARTITION BY page_host ORDER BY is_main_document DESC)) AS mainframe_id, + CASE + WHEN frame_id = COALESCE(mainframe_id, FIRST_VALUE(mainframe_id) OVER (PARTITION BY page_host ORDER BY is_main_document DESC)) + THEN 'mainframe' + ELSE 'iframe' + END AS frame_type + FROM document_frameid +), + +combined_frame_counts AS ( + SELECT + client, + page_host, + frame_host, + tp_flag, + CASE + WHEN COUNT(DISTINCT frame_type) = 1 AND MAX(CASE WHEN frame_type = 'mainframe' THEN 1 ELSE 0 END) = 1 + THEN 'mainframe-only' + WHEN COUNT(DISTINCT frame_type) = 1 AND MAX(CASE WHEN frame_type = 'iframe' THEN 1 ELSE 0 END) = 1 + THEN 'iframe-only' + WHEN COUNT(DISTINCT frame_id) >= 2 AND COUNT(DISTINCT frame_type) = 2 + THEN 'both' + END AS frame_presence + FROM page_frames + GROUP BY client, page_host, frame_host, tp_flag +), + +aggregated_counts AS ( + SELECT + client, + COUNT(DISTINCT page_host) AS distinct_publisher_count, + COUNT(DISTINCT CASE WHEN tp_flag THEN frame_host ELSE null END) AS distinct_third_party_count, + COUNT(DISTINCT CASE WHEN frame_presence = 'mainframe-only' AND tp_flag THEN page_host ELSE null END) AS distinct_publishers_mainframe_only, + COUNT(DISTINCT CASE WHEN frame_presence = 'iframe-only' AND tp_flag THEN page_host ELSE null END) AS distinct_publishers_iframe_only, + COUNT(DISTINCT CASE WHEN frame_presence = 'both' AND tp_flag THEN page_host ELSE null END) AS distinct_publishers_both, + COUNT(DISTINCT CASE WHEN frame_presence = 'mainframe-only' AND tp_flag THEN frame_host ELSE null END) AS distinct_mainframe_third_party_count, + COUNT(DISTINCT CASE WHEN frame_presence = 'iframe-only' AND tp_flag THEN frame_host ELSE null END) AS distinct_iframe_third_party_count, + COUNT(DISTINCT CASE WHEN frame_presence = 'both' AND tp_flag THEN frame_host ELSE null END) AS distinct_both_third_party_count + FROM combined_frame_counts + GROUP BY client +) + +SELECT + client, + distinct_publisher_count, + distinct_third_party_count, + distinct_publishers_mainframe_only, + distinct_publishers_iframe_only, + distinct_publishers_both, + distinct_mainframe_third_party_count, + distinct_mainframe_third_party_count / distinct_third_party_count AS pct_tps_in_mainframe_only, + distinct_iframe_third_party_count, + distinct_iframe_third_party_count / distinct_third_party_count AS pct_tps_in_iframe_only, + distinct_both_third_party_count, + distinct_both_third_party_count / distinct_third_party_count AS pct_tps_in_both +FROM aggregated_counts; diff --git a/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql b/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql new file mode 100644 index 00000000000..5e648f75ec6 --- /dev/null +++ b/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql @@ -0,0 +1,65 @@ +#standardSQL +# Distribution of third parties by number of websites + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + canonicalDomain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + canonicalDomain, + category + HAVING + page_usage >= 50 +), + +base AS ( + SELECT + client, + canonicalDomain, + COUNT(DISTINCT page) AS pages_per_third_party + FROM + requests + LEFT JOIN + third_party + ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) + WHERE + canonicalDomain IS NOT NULL + GROUP BY + client, + canonicalDomain +) + +SELECT + client, + percentile, + APPROX_QUANTILES(pages_per_third_party, 1000)[OFFSET(percentile * 10)] AS approx_pages_per_third_party +FROM + base, + UNNEST([10, 25, 50, 75, 90]) AS percentile +GROUP BY + client, + percentile +ORDER BY + client, + percentile diff --git a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql new file mode 100644 index 00000000000..94cfc0d7464 --- /dev/null +++ b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql @@ -0,0 +1,63 @@ +#standardSQL +# Distribution of websites by number of third party + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + canonicalDomain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + canonicalDomain, + category + HAVING + page_usage >= 50 +), + +base AS ( + SELECT + client, + page, + COUNT(domain) AS third_parties_per_page + FROM + requests + LEFT JOIN + third_party + ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) + GROUP BY + client, + page +) + +SELECT + client, + percentile, + APPROX_QUANTILES(third_parties_per_page, 1000)[OFFSET(percentile * 10)] AS approx_third_parties_per_page +FROM + base, + UNNEST([10, 25, 50, 75, 90]) AS percentile +GROUP BY + client, + percentile +ORDER BY + client, + percentile diff --git a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql new file mode 100644 index 00000000000..162b1cc9f71 --- /dev/null +++ b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql @@ -0,0 +1,63 @@ +#standardSQL +# Distribution of websites by number of third party + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + canonicalDomain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + canonicalDomain, + category + HAVING + page_usage >= 50 +), + +base AS ( + SELECT + client, + page, + COUNT(DISTINCT canonicalDomain) AS third_parties_per_page + FROM + requests + LEFT JOIN + third_party + ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) + GROUP BY + client, + page +) + +SELECT + client, + percentile, + APPROX_QUANTILES(third_parties_per_page, 1000)[OFFSET(percentile * 10)] AS approx_third_parties_per_page +FROM + base, + UNNEST([10, 25, 50, 75, 90]) AS percentile +GROUP BY + client, + percentile +ORDER BY + client, + percentile diff --git a/sql/2025/third-parties/iframe_allow_attribute.sql b/sql/2025/third-parties/iframe_allow_attribute.sql new file mode 100644 index 00000000000..c412c6455e2 --- /dev/null +++ b/sql/2025/third-parties/iframe_allow_attribute.sql @@ -0,0 +1,45 @@ +#standardSQL +# usage of different directives for allow attribute on iframes + +CREATE TEMP FUNCTION getNumWithAllowAttribute(payload STRING) AS (( + SELECT + COUNT(0) + FROM + UNNEST(JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox')) AS iframeAttr + WHERE + JSON_EXTRACT_SCALAR(iframeAttr, '$.allow') IS NOT NULL +)); + +SELECT + client, + SPLIT(TRIM(allow_attr), ' ')[OFFSET(0)] AS directive, + total_iframes_with_allow, + COUNT(0) AS freq, + COUNT(0) / total_iframes_with_allow AS pct +FROM ( + SELECT + _TABLE_SUFFIX AS client, + JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox') AS iframeAttrs + FROM + `httparchive.pages.2024_06_01_*` +), + UNNEST(iframeAttrs) AS iframeAttr, + UNNEST(REGEXP_EXTRACT_ALL(JSON_EXTRACT_SCALAR(iframeAttr, '$.allow'), r'(?i)([^,;]+)')) AS allow_attr +JOIN ( + SELECT + _TABLE_SUFFIX AS client, + SUM(getNumWithAllowAttribute(payload)) AS total_iframes_with_allow + FROM + `httparchive.pages.2024_06_01_*` + GROUP BY + client +) USING (client) +GROUP BY + client, + directive, + total_iframes_with_allow +HAVING + pct > 0.001 +ORDER BY + client, + pct DESC diff --git a/sql/2025/third-parties/iframe_attribute_popular_hosts.sql b/sql/2025/third-parties/iframe_attribute_popular_hosts.sql new file mode 100644 index 00000000000..f7f1d49a1e2 --- /dev/null +++ b/sql/2025/third-parties/iframe_attribute_popular_hosts.sql @@ -0,0 +1,54 @@ +#standardSQL +# most common hostnames of iframes that have the allow or sandbox attribute + +CREATE TEMP FUNCTION hasPolicy(attr STRING, policy_type STRING) +RETURNS BOOL DETERMINISTIC +LANGUAGE js AS ''' + const $ = JSON.parse(attr); + return $[policy_type] !== null; +'''; + +SELECT + client, + policy_type, + hostname, + COUNTIF(has_policy) AS freq, + total_iframes, + COUNTIF(has_policy) / total_iframes AS pct +FROM ( + SELECT + client, + policy_type, + JSON_EXTRACT_SCALAR(iframeAttr, '$.hostname') AS hostname, + hasPolicy(iframeAttr, policy_type) AS has_policy + FROM ( + SELECT + _TABLE_SUFFIX AS client, + JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox') AS iframeAttrs + FROM + `httparchive.pages.2024_06_01_*` + ), + UNNEST(iframeAttrs) AS iframeAttr, + UNNEST(['allow', 'sandbox']) AS policy_type +) +JOIN ( + SELECT + _TABLE_SUFFIX AS client, + SUM(ARRAY_LENGTH(JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox'))) AS total_iframes + FROM + `httparchive.pages.2024_06_01_*` + GROUP BY + client +) +USING (client) +GROUP BY + client, + total_iframes, + policy_type, + hostname +HAVING + pct > 0.001 +ORDER BY + client, + policy_type, + pct DESC diff --git a/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql b/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql new file mode 100644 index 00000000000..dafd87772b5 --- /dev/null +++ b/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql @@ -0,0 +1,44 @@ +#standardSQL +# Pages with unminified CSS by 1P/3P +CREATE TEMPORARY FUNCTION getUnminifiedJsUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(({url, wastedBytes}) => { + return {url, wastedBytes}; + }); +} catch (e) { + return []; +} +'''; + +SELECT + client, + AVG(pct_1p_wasted_bytes) AS avg_pct_1p_wasted_bytes, + AVG(pct_3p_wasted_bytes) AS avg_pct_3p_wasted_bytes +FROM ( + SELECT + client, + page, + SUM(IF(is_3p, 0, wasted_bytes)) / SUM(wasted_bytes) AS pct_1p_wasted_bytes, + SUM(IF(is_3p, wasted_bytes, 0)) / SUM(wasted_bytes) AS pct_3p_wasted_bytes + FROM ( + SELECT + client, + page, + NET.HOST(unminified.url) IS NOT NULL AND NET.HOST(unminified.url) IN ( + SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2024-06-01' AND category != 'hosting' + ) AS is_3p, + unminified.wastedBytes AS wasted_bytes + FROM + `httparchive.all.pages` AS allpages + CROSS JOIN + UNNEST(getUnminifiedJsUrls(JSON_EXTRACT(allpages.lighthouse, "$.audits['unminified-css']"))) AS unminified + WHERE allpages.date = '2024-06-01' AND allpages.is_root_page = TRUE + ) + GROUP BY + client, + page +) +GROUP BY + client diff --git a/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql b/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql new file mode 100644 index 00000000000..49e36b4a5dc --- /dev/null +++ b/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql @@ -0,0 +1,42 @@ +#standardSQL +# Pages with unminified JS by 1P/3P +CREATE TEMPORARY FUNCTION getUnminifiedJsUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(({url, wastedBytes}) => { + return {url, wastedBytes}; + }); +} catch (e) { + return []; +} +'''; + +SELECT + client, + AVG(pct_1p_wasted_bytes) AS avg_pct_1p_wasted_bytes, + AVG(pct_3p_wasted_bytes) AS avg_pct_3p_wasted_bytes +FROM ( + SELECT + client, + page, + SUM(IF(is_3p, 0, wasted_bytes)) / SUM(wasted_bytes) AS pct_1p_wasted_bytes, + SUM(IF(is_3p, wasted_bytes, 0)) / SUM(wasted_bytes) AS pct_3p_wasted_bytes + FROM ( + SELECT + _TABLE_SUFFIX AS client, + lighthouse.url AS page, + NET.HOST(unminified.url) IS NOT NULL AND NET.HOST(unminified.url) IN ( + SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2024-06-01' AND category != 'hosting' + ) AS is_3p, + unminified.wastedBytes AS wasted_bytes + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUnminifiedJsUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS unminified + ) + GROUP BY + client, + page +) +GROUP BY + client diff --git a/sql/2025/third-parties/lighthouse_third_party_facades.sql b/sql/2025/third-parties/lighthouse_third_party_facades.sql new file mode 100644 index 00000000000..725460ce1a5 --- /dev/null +++ b/sql/2025/third-parties/lighthouse_third_party_facades.sql @@ -0,0 +1,20 @@ +SELECT + client, + fail, + total, + pct +FROM ( + SELECT + _TABLE_SUFFIX AS client, + COUNTIF(SAFE_CAST(JSON_VALUE(report, '$.audits.third-party-facades.score') AS FLOAT64) < 0.9) AS fail, + SUM(COUNT(0)) OVER (PARTITION BY _TABLE_SUFFIX) AS total, + COUNTIF(SAFE_CAST(JSON_VALUE(report, '$.audits.third-party-facades.score') AS FLOAT64) < 0.9) / SUM(COUNT(0)) OVER (PARTITION BY _TABLE_SUFFIX) AS pct + FROM + `httparchive.lighthouse.2024_06_01_*` + GROUP BY + client +) +WHERE + total > 100 +ORDER BY + client diff --git a/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql b/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql new file mode 100644 index 00000000000..a36e300d222 --- /dev/null +++ b/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql @@ -0,0 +1,70 @@ +#standardSQL +# Third-party pages with unminified CSS + +CREATE TEMPORARY FUNCTION getUnminifiedCssUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(({url, wastedBytes, totalBytes}) => { + return {url, wastedBytes, totalBytes}; + }); +} catch (e) { + return []; +} +'''; + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + potential_third_parties.domain AS domain, + SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, + SUM(IF(third_party_domains.domain IS NOT NULL, transfer_size, 0)) AS third_party_transfer_size + FROM ( + SELECT + _TABLE_SUFFIX AS client, + NET.HOST(data.url) AS domain, + lighthouse.url AS page, + data.wastedBytes AS potential_savings, + data.totalBytes AS transfer_size + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUnminifiedCssUrls(JSON_EXTRACT(report, "$.audits['unminified-css']"))) AS data + ) AS potential_third_parties + LEFT OUTER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page, + domain +) + +SELECT + client, + domain, + COUNT(DISTINCT page) AS total_pages, + SUM(third_party_transfer_size) AS third_party_transfer_size_bytes, + SUM(potential_third_party_savings) AS potential_third_party_savings_bytes, + SUM(potential_third_party_savings) / SUM(third_party_transfer_size) AS pct_potential_third_party_savings, + SUM(potential_third_party_savings) / COUNT(DISTINCT page) AS potential_third_party_savings_bytes_per_page +FROM + base +WHERE + potential_third_party_savings > 0 +GROUP BY + client, + domain +ORDER BY + client, + total_pages DESC, + potential_third_party_savings_bytes_per_page DESC, + domain diff --git a/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql b/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql new file mode 100644 index 00000000000..4cd75c0b3c8 --- /dev/null +++ b/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql @@ -0,0 +1,70 @@ +#standardSQL +# Third-party pages with unminified JavaScript + +CREATE TEMPORARY FUNCTION getUnminifiedJavascriptUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(({url, wastedBytes, totalBytes}) => { + return {url, wastedBytes, totalBytes}; + }); +} catch (e) { + return []; +} +'''; + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + potential_third_parties.domain AS domain, + SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, + SUM(IF(third_party_domains.domain IS NOT NULL, transfer_size, 0)) AS third_party_transfer_size + FROM ( + SELECT + _TABLE_SUFFIX AS client, + NET.HOST(data.url) AS domain, + lighthouse.url AS page, + data.wastedBytes AS potential_savings, + data.totalBytes AS transfer_size + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUnminifiedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS data + ) AS potential_third_parties + LEFT OUTER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page, + domain +) + +SELECT + client, + domain, + COUNT(DISTINCT page) AS total_pages, + SUM(third_party_transfer_size) AS third_party_transfer_size_bytes, + SUM(potential_third_party_savings) AS potential_third_party_savings_bytes, + SUM(potential_third_party_savings) / SUM(third_party_transfer_size) AS pct_potential_third_party_savings, + SUM(potential_third_party_savings) / COUNT(DISTINCT page) AS potential_third_party_savings_bytes_per_page +FROM + base +WHERE + potential_third_party_savings > 0 +GROUP BY + client, + domain +ORDER BY + client, + total_pages DESC, + potential_third_party_savings_bytes_per_page DESC, + domain diff --git a/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql b/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql new file mode 100644 index 00000000000..41b0b19d910 --- /dev/null +++ b/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql @@ -0,0 +1,75 @@ +#standardSQL +# Third-party pages with unminified JavaScript + +CREATE TEMPORARY FUNCTION getUnminifiedJavascriptUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(({url, wastedBytes, totalBytes}) => { + return {url, wastedBytes, totalBytes}; + }); +} catch (e) { + return []; +} +'''; + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + potential_third_parties.domain AS domain, + potential_third_parties.url AS url, + SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, + SUM(IF(third_party_domains.domain IS NOT NULL, transfer_size, 0)) AS third_party_transfer_size + FROM ( + SELECT + _TABLE_SUFFIX AS client, + NET.HOST(data.url) AS domain, + data.url AS url, + lighthouse.url AS page, + data.wastedBytes AS potential_savings, + data.totalBytes AS transfer_size + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUnminifiedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS data + ) AS potential_third_parties + LEFT OUTER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page, + domain, + url +) + +SELECT + client, + domain, + url, + COUNT(DISTINCT page) AS total_pages, + SUM(third_party_transfer_size) AS third_party_transfer_size_bytes, + SUM(potential_third_party_savings) AS potential_third_party_savings_bytes, + SUM(potential_third_party_savings) / SUM(third_party_transfer_size) AS pct_potential_third_party_savings, + SUM(potential_third_party_savings) / COUNT(DISTINCT page) AS potential_third_party_savings_bytes_per_page +FROM + base +WHERE + potential_third_party_savings > 0 +GROUP BY + client, + domain, + url +ORDER BY + client, + total_pages DESC, + potential_third_party_savings_bytes_per_page DESC, + domain diff --git a/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql b/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql new file mode 100644 index 00000000000..8a4ff88ef38 --- /dev/null +++ b/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql @@ -0,0 +1,70 @@ +#standardSQL +# Third-party pages with unoptimized images + +CREATE TEMPORARY FUNCTION getUnminifiedImageUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(({url, wastedBytes, totalBytes}) => { + return {url, wastedBytes, totalBytes}; + }); +} catch (e) { + return []; +} +'''; + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + potential_third_parties.domain AS domain, + SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, + SUM(IF(third_party_domains.domain IS NOT NULL, transfer_size, 0)) AS third_party_transfer_size + FROM ( + SELECT + _TABLE_SUFFIX AS client, + NET.HOST(data.url) AS domain, + lighthouse.url AS page, + data.wastedBytes AS potential_savings, + data.totalBytes AS transfer_size + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUnminifiedImageUrls(JSON_EXTRACT(report, "$.audits['uses-optimized-images']"))) AS data + ) AS potential_third_parties + LEFT OUTER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page, + domain +) + +SELECT + client, + domain, + COUNT(DISTINCT page) AS total_pages, + SUM(third_party_transfer_size) AS third_party_transfer_size_bytes, + SUM(potential_third_party_savings) AS potential_third_party_savings_bytes, + SUM(potential_third_party_savings) / SUM(third_party_transfer_size) AS pct_potential_third_party_savings, + SUM(potential_third_party_savings) / COUNT(DISTINCT page) AS potential_third_party_savings_bytes_per_page +FROM + base +WHERE + potential_third_party_savings > 0 +GROUP BY + client, + domain +ORDER BY + client, + total_pages DESC, + potential_third_party_savings_bytes_per_page DESC, + domain diff --git a/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql b/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql new file mode 100644 index 00000000000..5ff6f31495f --- /dev/null +++ b/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql @@ -0,0 +1,70 @@ +#bq-tandardSQL +# Third-party pages with unused CSS + +CREATE TEMPORARY FUNCTION getUnusedCssUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(({url, wastedBytes, totalBytes}) => { + return {url, wastedBytes, totalBytes}; + }); +} catch (e) { + return []; +} +'''; + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + potential_third_parties.domain AS domain, + SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, + SUM(IF(third_party_domains.domain IS NOT NULL, transfer_size, 0)) AS third_party_transfer_size + FROM ( + SELECT + _TABLE_SUFFIX AS client, + NET.HOST(data.url) AS domain, + lighthouse.url AS page, + data.wastedBytes AS potential_savings, + data.totalBytes AS transfer_size + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUnusedCssUrls(JSON_EXTRACT(report, "$.audits['unused-css-rules']"))) AS data + ) AS potential_third_parties + LEFT OUTER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page, + domain +) + +SELECT + client, + domain, + COUNT(DISTINCT page) AS total_pages, + SUM(third_party_transfer_size) AS third_party_transfer_size_bytes, + SUM(potential_third_party_savings) AS potential_third_party_savings_bytes, + SUM(potential_third_party_savings) / SUM(third_party_transfer_size) AS pct_potential_third_party_savings, + SUM(potential_third_party_savings) / COUNT(DISTINCT page) AS potential_third_party_savings_bytes_per_page +FROM + base +WHERE + potential_third_party_savings > 0 +GROUP BY + client, + domain +ORDER BY + client, + total_pages DESC, + potential_third_party_savings_bytes_per_page DESC, + domain diff --git a/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql b/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql new file mode 100644 index 00000000000..cce307298b2 --- /dev/null +++ b/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql @@ -0,0 +1,70 @@ +#standardSQL +# Third-party pages with unused JavaScript + +CREATE TEMPORARY FUNCTION getUnusedJavascriptUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(({url, wastedBytes, totalBytes}) => { + return {url, wastedBytes, totalBytes}; + }); +} catch (e) { + return []; +} +'''; + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + potential_third_parties.domain AS domain, + SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, + SUM(IF(third_party_domains.domain IS NOT NULL, transfer_size, 0)) AS third_party_transfer_size + FROM ( + SELECT + _TABLE_SUFFIX AS client, + NET.HOST(data.url) AS domain, + lighthouse.url AS page, + data.wastedBytes AS potential_savings, + data.totalBytes AS transfer_size + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUnusedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unused-javascript']"))) AS data + ) AS potential_third_parties + LEFT OUTER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page, + domain +) + +SELECT + client, + domain, + COUNT(DISTINCT page) AS total_pages, + SUM(third_party_transfer_size) AS third_party_transfer_size_bytes, + SUM(potential_third_party_savings) AS potential_third_party_savings_bytes, + SUM(potential_third_party_savings) / SUM(third_party_transfer_size) AS pct_potential_third_party_savings, + SUM(potential_third_party_savings) / COUNT(DISTINCT page) AS potential_third_party_savings_bytes_per_page +FROM + base +WHERE + potential_third_party_savings > 0 +GROUP BY + client, + domain +ORDER BY + client, + total_pages DESC, + potential_third_party_savings_bytes_per_page DESC, + domain diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank.sql b/sql/2025/third-parties/number_of_third_parties_by_rank.sql new file mode 100644 index 00000000000..a97d0e617e0 --- /dev/null +++ b/sql/2025/third-parties/number_of_third_parties_by_rank.sql @@ -0,0 +1,82 @@ +#standardSQL +# Number of third-parties per websites by rank +WITH requests AS ( + SELECT + client, + page, + url + FROM + `httparchive.all.requests` AS req + WHERE + req.date = '2024-06-01' AND + req.is_root_page = true +), + +pages AS ( + SELECT + client, + page, + rank + FROM + `httparchive.all.pages` AS pg + WHERE + pg.date = '2024-06-01' AND + pg.is_root_page = true +), + +third_party AS ( + SELECT + domain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + category + HAVING + page_usage >= 50 +), + +base AS ( + SELECT + client, + page, + rank, + COUNT(domain) AS third_parties_per_page + FROM + requests + LEFT JOIN + third_party + ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) + INNER JOIN + pages + USING (client, page) + GROUP BY + client, + page, + rank +) + +SELECT + client, + rank_grouping, + APPROX_QUANTILES(third_parties_per_page, 1000)[OFFSET(500)] AS p50_third_parties_per_page +FROM + base, + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping +WHERE + rank <= rank_grouping +GROUP BY + client, + rank_grouping +ORDER BY + client, + rank_grouping diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql new file mode 100644 index 00000000000..55cbbd657c7 --- /dev/null +++ b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql @@ -0,0 +1,84 @@ +#standardSQL +# Number of third-parties per websites by rank and category + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +pages AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + rank + FROM + `httparchive.summary_pages.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + canonicalDomain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category NOT IN ('hosting') + GROUP BY + domain, + canonicalDomain, + category + HAVING + page_usage >= 50 +), + +base AS ( + SELECT + client, + category, + page, + rank, + COUNT(domain) AS third_parties_per_page + FROM + requests + LEFT JOIN + third_party + ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) + INNER JOIN + pages + USING (client, page) + GROUP BY + client, + category, + page, + rank +) + +SELECT + client, + category, + rank_grouping, + APPROX_QUANTILES(third_parties_per_page, 1000)[OFFSET(500)] AS p50_third_parties_per_page +FROM + base, + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping +WHERE + rank <= rank_grouping +GROUP BY + client, + category, + rank_grouping +ORDER BY + client, + category, + rank_grouping diff --git a/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql b/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql new file mode 100644 index 00000000000..15577d1481a --- /dev/null +++ b/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql @@ -0,0 +1,78 @@ +#standardSQL +# Number of distinct third-party providers per websites by rank +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +pages AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + rank + FROM + `httparchive.summary_pages.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + canonicalDomain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + canonicalDomain, + category + HAVING + page_usage >= 50 +), + +base AS ( + SELECT + client, + page, + rank, + COUNT(DISTINCT canonicalDomain) AS third_parties_per_page + FROM + requests + LEFT JOIN + third_party + ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) + INNER JOIN + pages + USING (client, page) + GROUP BY + client, + page, + rank +) + +SELECT + client, + rank_grouping, + APPROX_QUANTILES(third_parties_per_page, 1000)[OFFSET(500)] AS p50_third_parties_per_page +FROM + base, + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping +WHERE + rank <= rank_grouping +GROUP BY + client, + rank_grouping +ORDER BY + client, + rank_grouping diff --git a/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql new file mode 100644 index 00000000000..5bc156dea77 --- /dev/null +++ b/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql @@ -0,0 +1,84 @@ +#standardSQL +# Number of third-party providers per websites by rank and category + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +pages AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + rank + FROM + `httparchive.summary_pages.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + canonicalDomain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category NOT IN ('hosting') + GROUP BY + domain, + canonicalDomain, + category + HAVING + page_usage >= 50 +), + +base AS ( + SELECT + client, + category, + page, + rank, + COUNT(DISTINCT canonicalDomain) AS third_parties_per_page + FROM + requests + LEFT JOIN + third_party + ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) + INNER JOIN + pages + USING (client, page) + GROUP BY + client, + category, + page, + rank +) + +SELECT + client, + category, + rank_grouping, + APPROX_QUANTILES(third_parties_per_page, 1000)[OFFSET(500)] AS p50_third_parties_per_page +FROM + base, + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping +WHERE + rank <= rank_grouping +GROUP BY + client, + category, + rank_grouping +ORDER BY + client, + category, + rank_grouping diff --git a/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql b/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql new file mode 100644 index 00000000000..8aa31d57ce4 --- /dev/null +++ b/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql @@ -0,0 +1,60 @@ +#standardSQL +# Number of third-party requests by rank +WITH requests AS ( + SELECT + client, + page, + url + FROM + `httparchive.all.requests` AS req + WHERE + req.date = '2024-06-01' AND + req.is_root_page = true +), + +pages AS ( + SELECT + client, + page, + rank + FROM + `httparchive.all.pages` AS pg + WHERE + pg.date = '2024-06-01' AND + pg.is_root_page = true +), + +third_party AS ( + SELECT + tp.client, + tp.rank, + COUNT(DISTINCT r.url) AS distinct_tp_requests, + COUNT(r.url) AS tp_requests, + rank_grouping + FROM + pages tp + INNER JOIN + requests r + ON NET.HOST(tp.page) = NET.HOST(r.page) AND tp.client = r.client + CROSS JOIN UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + WHERE + tp.rank <= rank_grouping + GROUP BY + tp.client, + tp.rank, + rank_grouping +) + +SELECT + client, + rank_grouping, + APPROX_QUANTILES(distinct_tp_requests, 1000)[OFFSET(500)] AS median_distinct_tp_requests, + APPROX_QUANTILES(tp_requests, 1000)[OFFSET(500)] AS median_tp_requests +FROM + third_party +GROUP BY + client, + rank_grouping +ORDER BY + client, + rank_grouping; diff --git a/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql b/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql new file mode 100644 index 00000000000..dcc609ef8ba --- /dev/null +++ b/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql @@ -0,0 +1,60 @@ +#standardSQL +# Number of third-party requests per page by rank +WITH requests AS ( + SELECT + client, + page, + url + FROM + `httparchive.all.requests` AS req + WHERE + req.date = '2024-06-01' AND + req.is_root_page = true +), + +pages AS ( + SELECT + client, + page, + rank + FROM + `httparchive.all.pages` AS pg + WHERE + pg.date = '2024-06-01' AND + pg.is_root_page = true +), + +third_party AS ( + SELECT + tp.client, + tp.page, + tp.rank, + COUNT(DISTINCT r.url) AS distinct_tp_requests, + COUNT(r.url) AS tp_requests + FROM + pages tp + INNER JOIN + requests r + ON NET.HOST(tp.page) = NET.HOST(r.page) AND tp.client = r.client + GROUP BY + tp.client, + tp.page, + tp.rank +) + +SELECT + client, + rank_grouping, + APPROX_QUANTILES(distinct_tp_requests, 1000)[OFFSET(500)] AS p50_distinct_tp_requests_per_page, + APPROX_QUANTILES(tp_requests, 1000)[OFFSET(500)] AS p50_tp_requests_per_page +FROM + third_party, + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping +WHERE + rank <= rank_grouping +GROUP BY + client, + rank_grouping +ORDER BY + client, + rank_grouping; diff --git a/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql b/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql new file mode 100644 index 00000000000..32279f1349f --- /dev/null +++ b/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql @@ -0,0 +1,53 @@ +#standardSQL +# Percent of third party requests by content type. + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url, + type AS contentType + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + category + HAVING + page_usage >= 50 +) + +SELECT + client, + contentType, + COUNT(0) AS requests, + SUM(COUNT(0)) OVER (PARTITION BY client) AS total_requests, + COUNT(0) / SUM(COUNT(0)) OVER (PARTITION BY client) AS pct_requests +FROM + requests +LEFT JOIN + third_party +ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) +WHERE + domain IS NOT NULL +GROUP BY + client, + contentType +ORDER BY + client, + contentType diff --git a/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql b/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql new file mode 100644 index 00000000000..c5c0928b33a --- /dev/null +++ b/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql @@ -0,0 +1,71 @@ +#standardSQL +# Percent of third-parties that use document.write + +CREATE TEMPORARY FUNCTION +getUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(i => ({url: i.source.url})); +} catch(e) { + return []; +} +'''; + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + third_party_domains.domain AS domain + FROM + ( + SELECT + _TABLE_SUFFIX AS client, + NET.HOST(data.url) AS domain, + lighthouse.url AS page + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['no-document-write']"))) AS data + ) AS potential_third_parties + INNER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page, + domain +) + +SELECT + base.client AS client, + domain, + COUNT(0) AS freq, + total, + COUNT(0) / total AS pct +FROM + base +JOIN ( + SELECT + _TABLE_SUFFIX AS client, + COUNT(DISTINCT url) AS total + FROM + `httparchive.lighthouse.2024_06_01_*` + GROUP BY + _TABLE_SUFFIX +) +USING (client) +GROUP BY + client, + domain, + total +ORDER BY + client, + freq DESC diff --git a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql new file mode 100644 index 00000000000..a2b91afb054 --- /dev/null +++ b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql @@ -0,0 +1,71 @@ +#standardSQL +# Percent third-party scripts that use legacy JavaScript + +CREATE TEMPORARY FUNCTION +getUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(i => ({url: i.url})); +} catch(e) { + return []; +} +'''; + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + third_party_domains.domain AS domain + FROM + ( + SELECT + _TABLE_SUFFIX AS client, + NET.HOST(data.url) AS domain, + lighthouse.url AS page + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['legacy-javascript']"))) AS data + ) AS potential_third_parties + INNER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page, + domain +) + +SELECT + base.client AS client, + domain, + COUNT(0) AS freq, + total, + COUNT(0) / total AS pct +FROM + base +JOIN ( + SELECT + _TABLE_SUFFIX AS client, + COUNT(DISTINCT url) AS total + FROM + `httparchive.lighthouse.2024_06_01_*` + GROUP BY + _TABLE_SUFFIX +) +USING (client) +GROUP BY + client, + domain, + total +ORDER BY + client, + freq DESC diff --git a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql new file mode 100644 index 00000000000..80cd3ba707e --- /dev/null +++ b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql @@ -0,0 +1,88 @@ +#standardSQL +# Percent third-party scripts that use legacy JavaScript by URLs + +CREATE TEMPORARY FUNCTION +getUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(i => ({url: i.url})); +} catch(e) { + return []; +} +'''; + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + third_party_domains.domain AS domain, + url + FROM + ( + SELECT + _TABLE_SUFFIX AS client, + data.url AS url, + NET.HOST(data.url) AS domain, + lighthouse.url AS page + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['legacy-javascript']"))) AS data + ) AS potential_third_parties + INNER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page, + domain, + url +) + +SELECT + client, + domain, + url, + freq, + total, + pct +FROM ( + SELECT + base.client AS client, + domain, + url, + COUNT(0) AS freq, + total, + COUNT(0) / total AS pct, + RANK() OVER (PARTITION BY base.client ORDER BY COUNT(0) DESC) AS url_rank + FROM + base + JOIN ( + SELECT + _TABLE_SUFFIX AS client, + COUNT(DISTINCT url) AS total + FROM + `httparchive.lighthouse.2024_06_01_*` + GROUP BY + _TABLE_SUFFIX + ) + USING (client) + GROUP BY + client, + domain, + url, + total +) +WHERE + url_rank <= 100 +ORDER BY + client, + freq DESC diff --git a/sql/2025/third-parties/percent_of_third_party_cache.sql b/sql/2025/third-parties/percent_of_third_party_cache.sql new file mode 100644 index 00000000000..16bf3f855d0 --- /dev/null +++ b/sql/2025/third-parties/percent_of_third_party_cache.sql @@ -0,0 +1,75 @@ +#standardSQL +# Percent of third party requests cached +# Cache-Control documentation: https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control#Directives + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + resp_cache_control, + status, + respOtherHeaders, + reqOtherHeaders, + type, + url, + pageid AS page + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + category + HAVING + page_usage >= 50 +), + +base AS ( + SELECT + client, + type, + IF( + ( + status IN (301, 302, 307, 308, 410) AND + NOT REGEXP_CONTAINS(resp_cache_control, r'(?i)private|no-store') AND + NOT REGEXP_CONTAINS(reqOtherHeaders, r'Authorization') + ) OR + ( + status IN (301, 302, 307, 308, 410) OR + REGEXP_CONTAINS(resp_cache_control, r'public|max-age|s-maxage') OR + REGEXP_CONTAINS(respOtherHeaders, r'Expires') + ), 1, 0 + ) AS cached + FROM + requests + LEFT JOIN + third_party + ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) + WHERE + domain IS NOT NULL +) + +SELECT + client, + type, + SUM(cached) AS cached_requests, + COUNT(0) AS total_requests, + SUM(cached) / COUNT(0) AS pct_cached_requests +FROM + base +GROUP BY + client, + type diff --git a/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql b/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql new file mode 100644 index 00000000000..dd45c790156 --- /dev/null +++ b/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql @@ -0,0 +1,86 @@ +#standardSQL +# Percent of third party requests and bytes by category and content type. + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url, + type AS contentType, + respBodySize AS body_size + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + category + HAVING + page_usage >= 50 +), + +base AS ( + SELECT + client, + page, + category, + contentType, + body_size + FROM + requests + INNER JOIN + third_party + ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) +), + +requests_per_page_and_category AS ( + SELECT + client, + page, + category, + contentType, + SUM(SUM(body_size)) OVER (PARTITION BY page) AS total_page_size, + SUM(body_size) AS body_size, + SUM(COUNT(0)) OVER (PARTITION BY page) AS total_page_requests, + COUNT(0) AS requests + FROM + base + GROUP BY + client, + page, + category, + contentType +) + +SELECT + client, + category, + contentType, + SUM(requests) AS requests, + SAFE_DIVIDE(SUM(requests), SUM(SUM(requests)) OVER (PARTITION BY client, category)) AS pct_requests, + SUM(body_size) AS body_size, + SAFE_DIVIDE(SUM(body_size), SUM(SUM(body_size)) OVER (PARTITION BY client, category)) AS pct_body_size +FROM + requests_per_page_and_category +GROUP BY + client, + category, + contentType +ORDER BY + client, + category, + contentType diff --git a/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql b/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql new file mode 100644 index 00000000000..7701723753a --- /dev/null +++ b/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql @@ -0,0 +1,73 @@ +#standardSQL +# Percent of third-party requests with security headers + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url, + RTRIM(urlShort, '/') AS origin, + respOtherHeaders + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + category + HAVING + page_usage >= 50 +), + +headers AS ( + SELECT + client, + requests.origin AS req_origin, + LOWER(respOtherHeaders) AS respOtherHeaders, + third_party.category AS req_category + FROM requests + INNER JOIN third_party + ON NET.HOST(requests.origin) = NET.HOST(third_party.domain) +), + +base AS ( + SELECT + client, + req_origin, + req_category, + IF(STRPOS(respOtherHeaders, 'strict-transport-security') > 0, 1, 0) AS hsts_header, + IF(STRPOS(respOtherHeaders, 'x-content-type-options') > 0, 1, 0) AS x_content_type_options_header, + IF(STRPOS(respOtherHeaders, 'x-frame-options') > 0, 1, 0) AS x_frame_options_header, + IF(STRPOS(respOtherHeaders, 'x-xss-protection') > 0, 1, 0) AS x_xss_protection_header + FROM headers +) + +SELECT + client, + req_category, + COUNT(0) AS total_requests, + SUM(hsts_header) / COUNT(0) AS pct_hsts_header_requests, + SUM(x_content_type_options_header) / COUNT(0) AS pct_x_content_type_options_header_requests, + SUM(x_frame_options_header) / COUNT(0) AS pct_x_frame_options_header_requests, + SUM(x_xss_protection_header) / COUNT(0) AS pct_x_xss_protection_header_requests +FROM + base +GROUP BY + client, + req_category +ORDER BY + client, + req_category diff --git a/sql/2025/third-parties/percent_of_websites_with_third_party.sql b/sql/2025/third-parties/percent_of_websites_with_third_party.sql new file mode 100644 index 00000000000..732377fff57 --- /dev/null +++ b/sql/2025/third-parties/percent_of_websites_with_third_party.sql @@ -0,0 +1,51 @@ +#standardSQL +# Percent of websites with third parties + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url, + respBodySize + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + category, + COUNT(DISTINCT page) AS page_usage, + COUNT(0) AS request_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + category + HAVING + page_usage > 50 +) + +SELECT + client, + COUNT(DISTINCT IF(domain IS NOT NULL, page, NULL)) AS pages_with_third_party, + COUNT(DISTINCT page) AS total_pages, + COUNT(DISTINCT IF(domain IS NOT NULL, page, NULL)) / COUNT(DISTINCT page) AS pct_pages_with_third_party, + COUNTIF(domain IS NOT NULL) AS third_party_requests, + COUNT(0) AS total_requests, + COUNTIF(domain IS NOT NULL) / COUNT(0) AS pct_third_party_requests, + SUM(IF(domain IS NOT NULL, respBodySize, 0)) AS third_party_body_size, + SUM(respBodySize) AS total_body_size, + SUM(IF(domain IS NOT NULL, respBodySize, 0)) / SUM(respBodySize) AS pct_body_size +FROM + requests +LEFT JOIN third_party +ON NET.HOST(requests.url) = NET.HOST(third_party.domain) +GROUP BY + client diff --git a/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql b/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql new file mode 100644 index 00000000000..b6ceadbf8eb --- /dev/null +++ b/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql @@ -0,0 +1,64 @@ +#standardSQL +# Percent of websites with third parties by ranking + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + category + HAVING + page_usage >= 50 +), + +pages AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + rank + FROM + `httparchive.summary_pages.2024_06_01_*` +) + +SELECT + client, + rank_grouping, + COUNT(DISTINCT IF(domain IS NOT NULL, page, NULL)) AS pages_with_third_party, + COUNT(DISTINCT page) AS total_pages, + COUNT(DISTINCT IF(domain IS NOT NULL, page, NULL)) / COUNT(DISTINCT page) AS pct_pages_with_third_party +FROM + pages +JOIN + requests +USING (client, page) +LEFT JOIN + third_party +ON NET.HOST(requests.url) = NET.HOST(third_party.domain), + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping +WHERE + rank <= rank_grouping +GROUP BY + client, + rank_grouping +ORDER BY + client, + rank_grouping diff --git a/sql/2025/third-parties/scripts_using_async_defer.sql b/sql/2025/third-parties/scripts_using_async_defer.sql new file mode 100644 index 00000000000..cd324dfd581 --- /dev/null +++ b/sql/2025/third-parties/scripts_using_async_defer.sql @@ -0,0 +1,76 @@ +#standardSQL +# 3P scripts using async or defer +# (capped to 1 hit per domain per page) +CREATE TEMPORARY FUNCTION getScripts(str STRING) +RETURNS ARRAY> +LANGUAGE js AS ''' + try { + var almanac = JSON.parse(str); + + if (Array.isArray(almanac) || typeof almanac != "object") { + return result; + } + + if (almanac.scripts && almanac.scripts.nodes) { + return almanac.scripts.nodes.map((n) => ({ + src: n.src, + isAsync: n.hasOwnProperty("async"), + isDefer: n.hasOwnProperty("defer"), + })); + } + + return []; + } catch (e) { + return []; + } +'''; + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + third_party_domains.domain AS domain, + COUNTIF(isAsync) AS async_count, + COUNTIF(isDefer) AS defer_count + FROM + ( + SELECT + _TABLE_SUFFIX AS client, + NET.HOST(data.src) AS domain, + data.isAsync AS isAsync, + data.isDefer AS isDefer, + pages.url AS page + FROM + `httparchive.pages.2024_06_01_*` AS pages, + UNNEST(getScripts(JSON_EXTRACT_SCALAR(payload, '$._almanac'))) AS data + ) AS potential_third_parties + INNER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page, + domain +) + +SELECT + base.client AS client, + COUNTIF(async_count > 0) AS freq_async, + COUNTIF(defer_count > 0) AS freq_defer, + COUNT(0) AS total, + COUNTIF(async_count > 0) / COUNT(0) AS pct_async, + COUNTIF(defer_count > 0) / COUNT(0) AS pct_defer +FROM + base +GROUP BY + client +ORDER BY + client diff --git a/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql b/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql new file mode 100644 index 00000000000..0bed778f659 --- /dev/null +++ b/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql @@ -0,0 +1,81 @@ +#standardSQL +# 3P scripts using async or defer +# (capped to 1 hit per domain per page) +CREATE TEMPORARY FUNCTION getScripts(str STRING) +RETURNS ARRAY> +LANGUAGE js AS ''' + try { + var almanac = JSON.parse(str); + + if (Array.isArray(almanac) || typeof almanac != "object") { + return result; + } + + if (almanac.scripts && almanac.scripts.nodes) { + return almanac.scripts.nodes.map((n) => ({ + src: n.src, + isAsync: n.hasOwnProperty("async"), + isDefer: n.hasOwnProperty("defer"), + })); + } + + return []; + } catch (e) { + return []; + } +'''; + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + third_party_domains.domain AS domain, + COUNTIF(isAsync) AS async_count, + COUNTIF(isDefer) AS defer_count + FROM + ( + SELECT + _TABLE_SUFFIX AS client, + NET.HOST(data.src) AS domain, + data.isAsync AS isAsync, + data.isDefer AS isDefer, + pages.url AS page + FROM + `httparchive.pages.2024_06_01_*` AS pages, + UNNEST(getScripts(JSON_EXTRACT_SCALAR(payload, '$._almanac'))) AS data + ) AS potential_third_parties + INNER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page, + domain +) + +SELECT + base.client AS client, + domain, + COUNTIF(async_count > 0) AS freq_async, + COUNTIF(defer_count > 0) AS freq_defer, + COUNT(DISTINCT page) AS page_usage, + COUNTIF(async_count > 0) / COUNT(DISTINCT page) AS pct_async, + COUNTIF(defer_count > 0) / COUNT(DISTINCT page) AS pct_defer +FROM + base +GROUP BY + client, + domain +HAVING + page_usage > 50 +ORDER BY + client, + page_usage DESC diff --git a/sql/2025/third-parties/tao_by_third_party.sql b/sql/2025/third-parties/tao_by_third_party.sql new file mode 100644 index 00000000000..d7fdf825816 --- /dev/null +++ b/sql/2025/third-parties/tao_by_third_party.sql @@ -0,0 +1,105 @@ +#standardSQL +# Percent of third-party requests with "Timing-Allow-Origin" headers +# Header reference: https://developer.mozilla.org/docs/Web/HTTP/Headers/Timing-Allow-Origin + +CREATE TEMP FUNCTION get_tao(headers STRING) +RETURNS STRING LANGUAGE js AS ''' + try { + const regex = /timing-allow-origin = (\\*|(http.*?,? )+)/gm; + output = regex.exec(headers)[1]+", "; + output = output.replace(/, , $/, ", "); + return output; + } catch (e) { + return false; + } +'''; + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url, + RTRIM(urlShort, '/') AS origin, + respOtherHeaders + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +pages AS ( + SELECT + _TABLE_SUFFIX AS client, + url, + pageid AS page, + RTRIM(urlShort, '/') AS origin + FROM + `httparchive.summary_pages.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + category + HAVING + page_usage >= 50 +), + +headers AS ( + SELECT + requests.client AS client, + requests.origin AS req_origin, + pages.origin AS page_origin, + get_tao(LOWER(respOtherHeaders)) AS timing_allow_origin, + respOtherHeaders, + third_party.category AS req_category + FROM requests + LEFT JOIN pages + USING (client, page) + INNER JOIN third_party + ON NET.HOST(requests.origin) = NET.HOST(third_party.domain) +), + +base AS ( + SELECT + client, + IF(respOtherHeaders LIKE '%timing-allow-origin = %', 1, 0) AS tao_header_present, + IF( + page_origin = req_origin OR + timing_allow_origin = '*' OR + timing_allow_origin LIKE '*,%' OR + timing_allow_origin LIKE '%,*' OR + timing_allow_origin LIKE '%,*,%' OR + timing_allow_origin LIKE '%, *,%' OR + timing_allow_origin = page_origin OR + timing_allow_origin LIKE page_origin || ',' OR + timing_allow_origin LIKE '%,' || page_origin OR + timing_allow_origin LIKE '%, ' || page_origin OR + timing_allow_origin LIKE '%,' || page_origin || ',%' OR + timing_allow_origin LIKE '%, ' || page_origin || ',%', + 1, 0 + ) AS timing_allowed + FROM headers +) + +SELECT + client, + SUM(tao_header_present) AS tao_requests, + SUM(timing_allowed) AS timing_allowed_requests, + COUNT(0) AS total_requests, + SUM(tao_header_present) / COUNT(0) AS pct_tao_requests, + SUM(timing_allowed) / COUNT(0) AS pct_timing_allowed_requests +FROM + base +GROUP BY + client diff --git a/sql/2025/third-parties/third_parties_blocking_main_thread.sql b/sql/2025/third-parties/third_parties_blocking_main_thread.sql new file mode 100644 index 00000000000..77368d280f1 --- /dev/null +++ b/sql/2025/third-parties/third_parties_blocking_main_thread.sql @@ -0,0 +1,71 @@ +#standardSQL +# Third-Party domains which block the main thread +# +# As Lighthouse measures all impact there is no need to do a separate total +# Lighthouse also gives a useable category. So no need to use almanac.third-parties table +# +# Based heavily on research by Houssein Djirdeh: +# https://docs.google.com/spreadsheets/d/1Td-4qFjuBzxp8af_if5iBC0Lkqm_OROb7_2OcbxrU_g/edit?resourcekey=0-ZCfve5cngWxF0-sv5pLRzg#gid=1628564987 + +SELECT + client, + domain, + category, + total_pages, + blocking_pages, + non_blocking_pages, + pct_blocking_pages, + pct_non_blocking_pages, + p50_transfer_size_kib, + p50_blocking_time, + total_pages_rank +FROM ( + SELECT + client, + domain, + category, + COUNT(DISTINCT page) AS total_pages, + COUNTIF(blocking > 0) AS blocking_pages, + COUNT(DISTINCT page) - COUNTIF(blocking > 0) AS non_blocking_pages, + COUNTIF(blocking > 0) / COUNT(0) AS pct_blocking_pages, + (COUNT(DISTINCT page) - COUNTIF(blocking > 0)) / COUNT(0) AS pct_non_blocking_pages, + APPROX_QUANTILES(transfer_size_kib, 1000)[OFFSET(500)] AS p50_transfer_size_kib, + APPROX_QUANTILES(blocking_time, 1000)[OFFSET(500)] AS p50_blocking_time, + RANK() OVER (PARTITION BY client ORDER BY COUNT(DISTINCT page) DESC) AS total_pages_rank + FROM ( + SELECT + client, + JSON_VALUE(third_party_items, '$.entity.url') AS domain, + page, + JSON_VALUE(third_party_items, '$.entity.text') AS category, + COUNTIF(SAFE_CAST(JSON_VALUE(report, '$.audits.third-party-summary.details.summary.wastedMs') AS FLOAT64) > 250) AS blocking, + SUM(SAFE_CAST(JSON_VALUE(third_party_items, '$.blockingTime') AS FLOAT64)) AS blocking_time, + SUM(SAFE_CAST(JSON_VALUE(third_party_items, '$.transferSize') AS FLOAT64) / 1024) AS transfer_size_kib + FROM + ( + SELECT + _TABLE_SUFFIX AS client, + url AS page, + report + FROM + `httparchive.lighthouse.2024_06_01_*` + ), + UNNEST(JSON_QUERY_ARRAY(report, '$.audits.third-party-summary.details.items')) AS third_party_items + GROUP BY + client, + domain, + page, + category + ) + GROUP BY + client, + domain, + category + HAVING + total_pages >= 50 +) +WHERE + total_pages_rank <= 200 +ORDER BY + client, + total_pages DESC diff --git a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql new file mode 100644 index 00000000000..946b9b59bea --- /dev/null +++ b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql @@ -0,0 +1,59 @@ +#standardSQL +# Total of Third-Party domains which block the main thread by percentile +# +# As Lighthouse measures all impact there is no need to do a separate total +# Lighthouse also gives a useable category. So no need to use almanac.third-parties table +# +# Based heavily on research by Houssein Djirdeh: +# https://docs.google.com/spreadsheets/d/1Td-4qFjuBzxp8af_if5iBC0Lkqm_OROb7_2OcbxrU_g/edit?resourcekey=0-ZCfve5cngWxF0-sv5pLRzg#gid=1628564987 + +SELECT + client, + total_pages, + blocking_pages, + percentile, + p50_transfer_size_kib, + p50_blocking_time +FROM ( + SELECT + client, + COUNT(DISTINCT page) AS total_pages, + COUNTIF(blocking > 0) AS blocking_pages, + percentile, + APPROX_QUANTILES(transfer_size_kib, 1000)[OFFSET(percentile * 10)] AS p50_transfer_size_kib, + APPROX_QUANTILES(blocking_time, 1000)[OFFSET(percentile * 10)] AS p50_blocking_time, + RANK() OVER (PARTITION BY client ORDER BY COUNT(DISTINCT page) DESC) AS total_pages_rank + FROM ( + SELECT + client, + page, + COUNTIF(SAFE_CAST(JSON_VALUE(report, '$.audits.third-party-summary.details.summary.wastedMs') AS FLOAT64) > 250) AS blocking, + SUM(SAFE_CAST(JSON_VALUE(third_party_items, '$.blockingTime') AS FLOAT64)) AS blocking_time, + SUM(SAFE_CAST(JSON_VALUE(third_party_items, '$.transferSize') AS FLOAT64) / 1024) AS transfer_size_kib + FROM + ( + SELECT + _TABLE_SUFFIX AS client, + url AS page, + report + FROM + `httparchive.lighthouse.2024_06_01_*` + ), + UNNEST(JSON_QUERY_ARRAY(report, '$.audits.third-party-summary.details.items')) AS third_party_items + GROUP BY + client, + page + ), + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile + GROUP BY + client, + percentile + HAVING + total_pages >= 50 +) +WHERE + total_pages_rank <= 200 +ORDER BY + client, + total_pages DESC, + percentile diff --git a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql new file mode 100644 index 00000000000..2cd8352e437 --- /dev/null +++ b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql @@ -0,0 +1,70 @@ +#standardSQL +# Third-Party domains which block the main thread by percentile +# +# As Lighthouse measures all impact there is no need to do a separate total +# Lighthouse also gives a useable category. So no need to use almanac.third-parties table +# +# Based heavily on research by Houssein Djirdeh: +# https://docs.google.com/spreadsheets/d/1Td-4qFjuBzxp8af_if5iBC0Lkqm_OROb7_2OcbxrU_g/edit?resourcekey=0-ZCfve5cngWxF0-sv5pLRzg#gid=1628564987 + +SELECT + client, + domain, + category, + total_pages, + blocking_pages, + percentile, + p50_transfer_size_kib, + p50_blocking_time +FROM ( + SELECT + client, + domain, + category, + COUNT(DISTINCT page) AS total_pages, + COUNTIF(blocking > 0) AS blocking_pages, + percentile, + APPROX_QUANTILES(transfer_size_kib, 1000)[OFFSET(percentile * 10)] AS p50_transfer_size_kib, + APPROX_QUANTILES(blocking_time, 1000)[OFFSET(percentile * 10)] AS p50_blocking_time, + RANK() OVER (PARTITION BY client ORDER BY COUNT(DISTINCT page) DESC) AS total_pages_rank + FROM ( + SELECT + client, + JSON_VALUE(third_party_items, '$.entity.url') AS domain, + page, + JSON_VALUE(third_party_items, '$.entity.text') AS category, + COUNTIF(SAFE_CAST(JSON_VALUE(report, '$.audits.third-party-summary.details.summary.wastedMs') AS FLOAT64) > 250) AS blocking, + SUM(SAFE_CAST(JSON_VALUE(third_party_items, '$.blockingTime') AS FLOAT64)) AS blocking_time, + SUM(SAFE_CAST(JSON_VALUE(third_party_items, '$.transferSize') AS FLOAT64) / 1024) AS transfer_size_kib + FROM + ( + SELECT + _TABLE_SUFFIX AS client, + url AS page, + report + FROM + `httparchive.lighthouse.2024_06_01_*` + ), + UNNEST(JSON_QUERY_ARRAY(report, '$.audits.third-party-summary.details.items')) AS third_party_items + GROUP BY + client, + domain, + page, + category + ), + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile + GROUP BY + client, + domain, + category, + percentile + HAVING + total_pages >= 50 +) +WHERE + total_pages_rank <= 200 +ORDER BY + client, + total_pages DESC, + category, + percentile diff --git a/sql/2025/third-parties/third_parties_blocking_rendering.sql b/sql/2025/third-parties/third_parties_blocking_rendering.sql new file mode 100644 index 00000000000..ab151e43548 --- /dev/null +++ b/sql/2025/third-parties/third_parties_blocking_rendering.sql @@ -0,0 +1,116 @@ +#standardSQL +# Third-Party domains which render block paint +# +# Unlike the blocking main thread queries, lighthouse only contains details if the +# third-party is render blocking (i.e. wastedMs/total_bytes are never 0) +# And also there are no categories given to each third-party +# So we join to the usual almanac.third_parties table to get those totals and categories +# +# Based heavily on research by Houssein Djirdeh: +# https://docs.google.com/spreadsheets/d/1Td-4qFjuBzxp8af_if5iBC0Lkqm_OROb7_2OcbxrU_g/edit?resourcekey=0-ZCfve5cngWxF0-sv5pLRzg#gid=1628564987 + +WITH total_third_party_usage AS ( + SELECT + _TABLE_SUFFIX AS client, + canonicalDomain, + category, + COUNT(DISTINCT pages.url) AS total_pages + FROM + `httparchive.summary_pages.2024_06_01_*` AS pages + INNER JOIN ( + SELECT + _TABLE_SUFFIX AS client, + pageid, + url + FROM + `httparchive.summary_requests.2024_06_01_*` + ) AS requests + ON ( + pages._TABLE_SUFFIX = requests.client AND + pages.pageid = requests.pageid + ) + INNER JOIN + `httparchive.almanac.third_parties` + ON + NET.HOST(requests.url) = NET.HOST(domain) AND + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + client, + canonicalDomain, + category + HAVING + total_pages >= 50 +) + +SELECT + client, + canonicalDomain, + category, + total_pages, + blocking_pages, + non_blocking_pages, + blocking_pages_pct, + non_blocking_pages_pct, + p50_wastedMs, + p50_total_bytes_kib +FROM ( + SELECT + client, + canonicalDomain, + category, + total_pages, + COUNT(DISTINCT page) AS blocking_pages, + total_pages - COUNT(DISTINCT page) AS non_blocking_pages, + COUNT(DISTINCT page) / total_pages AS blocking_pages_pct, + (total_pages - COUNT(DISTINCT page)) / total_pages AS non_blocking_pages_pct, + APPROX_QUANTILES(wasted_ms, 1000)[OFFSET(500)] AS p50_wastedMs, + APPROX_QUANTILES(total_bytes_kib, 1000)[OFFSET(500)] AS p50_total_bytes_kib, + RANK() OVER (PARTITION BY client ORDER BY COUNT(DISTINCT page) DESC) AS total_pages_rank + FROM ( + SELECT + client, + canonicalDomain, + domain, + page, + category, + SUM(SAFE_CAST(JSON_VALUE(renderBlockingItems, '$.wastedMs') AS FLOAT64)) AS wasted_ms, + SUM(SAFE_CAST(JSON_VALUE(renderBlockingItems, '$.totalBytes') AS FLOAT64) / 1024) AS total_bytes_kib + FROM + ( + SELECT + _TABLE_SUFFIX AS client, + url AS page, + report + FROM + `httparchive.lighthouse.2024_06_01_*` + ), + UNNEST(JSON_QUERY_ARRAY(report, '$.audits.render-blocking-resources.details.items')) AS renderBlockingItems + INNER JOIN + `httparchive.almanac.third_parties` + ON + NET.HOST(JSON_VALUE(renderBlockingItems, '$.url')) = domain + GROUP BY + client, + canonicalDomain, + domain, + page, + category + ) + INNER JOIN + total_third_party_usage + USING (client, canonicalDomain, category) + GROUP BY + client, + canonicalDomain, + category, + total_pages + HAVING + total_pages >= 50 +) +WHERE + total_pages_rank <= 200 +ORDER BY + client, + total_pages DESC, + category diff --git a/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql b/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql new file mode 100644 index 00000000000..89ae5a05665 --- /dev/null +++ b/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql @@ -0,0 +1,114 @@ +#standardSQL +# Third-Party domains which render block paint by percentile +# +# Unlike the blocking main thread queries, lighthouse only contains details if the +# third-party is render blocking (i.e. wastedMs/total_bytes are never 0) +# And also there are no categories given to each third-party +# So we join to the usual almanac.third_parties table to get those totals and categories +# +# Based heavily on research by Houssein Djirdeh: +# https://docs.google.com/spreadsheets/d/1Td-4qFjuBzxp8af_if5iBC0Lkqm_OROb7_2OcbxrU_g/edit?resourcekey=0-ZCfve5cngWxF0-sv5pLRzg#gid=1628564987 + +WITH total_third_party_usage AS ( + SELECT + _TABLE_SUFFIX AS client, + canonicalDomain, + category, + COUNT(DISTINCT pages.url) AS total_pages + FROM + `httparchive.summary_pages.2024_06_01_*` AS pages + INNER JOIN ( + SELECT + _TABLE_SUFFIX AS client, + pageid, + url + FROM + `httparchive.summary_requests.2024_06_01_*` + ) AS requests + ON ( + pages._TABLE_SUFFIX = requests.client AND + pages.pageid = requests.pageid + ) + INNER JOIN + `httparchive.almanac.third_parties` + ON + NET.HOST(requests.url) = NET.HOST(domain) AND + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + client, + canonicalDomain, + category + HAVING + total_pages >= 50 +) + +SELECT + client, + canonicalDomain, + category, + total_pages, + blocking_pages, + percentile, + wasted_ms, + total_bytes_kib +FROM ( + SELECT + client, + canonicalDomain, + category, + total_pages, + COUNT(DISTINCT page) AS blocking_pages, + percentile, + APPROX_QUANTILES(wasted_ms, 1000)[OFFSET(percentile * 10)] AS wasted_ms, + APPROX_QUANTILES(total_bytes_kib, 1000)[OFFSET(percentile * 10)] AS total_bytes_kib, + RANK() OVER (PARTITION BY client ORDER BY COUNT(DISTINCT page) DESC) AS total_pages_rank + FROM ( + SELECT + client, + canonicalDomain, + page, + category, + SUM(SAFE_CAST(JSON_VALUE(render_blocking_items, '$.wastedMs') AS FLOAT64)) AS wasted_ms, + SUM(SAFE_CAST(JSON_VALUE(render_blocking_items, '$.totalBytes') AS FLOAT64) / 1024) AS total_bytes_kib + FROM + ( + SELECT + _TABLE_SUFFIX AS client, + url AS page, + report + FROM + `httparchive.lighthouse.2024_06_01_*` + ), + UNNEST(JSON_QUERY_ARRAY(report, '$.audits.render-blocking-resources.details.items')) AS render_blocking_items + INNER JOIN + `httparchive.almanac.third_parties` + ON + NET.HOST(JSON_VALUE(render_blocking_items, '$.url')) = domain AND + date = '2024-06-01' + GROUP BY + client, + canonicalDomain, + page, + category + ) + INNER JOIN + total_third_party_usage + USING (client, canonicalDomain, category), + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile + GROUP BY + client, + canonicalDomain, + category, + total_pages, + percentile + HAVING + total_pages >= 50 +) +WHERE + total_pages_rank <= 200 +ORDER BY + client, + total_pages DESC, + category, + percentile diff --git a/sql/2025/third-parties/third_parties_using_legacy_javascript.sql b/sql/2025/third-parties/third_parties_using_legacy_javascript.sql new file mode 100644 index 00000000000..937d6a1c665 --- /dev/null +++ b/sql/2025/third-parties/third_parties_using_legacy_javascript.sql @@ -0,0 +1,57 @@ +#standardSQL +# Third-parties that use legacy JavaScript + +CREATE TEMPORARY FUNCTION +getUrls(audit STRING) +RETURNS ARRAY> LANGUAGE js AS ''' +try { + var $ = JSON.parse(audit); + return $.details.items.map(i => ({url: i.url})); +} catch(e) { + return []; +} +'''; + + +WITH third_party_domains AS ( + SELECT DISTINCT + NET.HOST(domain) AS domain + FROM + `httparchive.almanac.third_parties` +), + +base AS ( + SELECT + client, + page, + COUNTIF(third_party_domains.domain IS NULL) / COUNT(0) AS pct_1p_legacy, + COUNTIF(third_party_domains.domain IS NOT NULL) / COUNT(0) AS pct_3p_legacy + FROM + ( + SELECT + _TABLE_SUFFIX AS client, + NET.HOST(data.url) AS domain, + lighthouse.url AS page + FROM + `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['legacy-javascript']"))) AS data + ) AS potential_third_parties + LEFT OUTER JOIN + third_party_domains + ON + potential_third_parties.domain = third_party_domains.domain + GROUP BY + client, + page +) + +SELECT + client, + AVG(pct_1p_legacy) AS avg_pct_1p_legacy, + AVG(pct_3p_legacy) AS avg_pct_3p_legacy +FROM + base +GROUP BY + client +ORDER BY + client diff --git a/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql b/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql new file mode 100644 index 00000000000..a8b84e1ecd2 --- /dev/null +++ b/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql @@ -0,0 +1,87 @@ +#standardSQL +# Top 100 third parties by median response body size, time + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + url, + pageid AS page, + respBodySize AS body_size, + time + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +third_party AS ( + SELECT + domain, + category, + canonicalDomain, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + canonicalDomain, + category + HAVING + page_usage >= 50 +), + +base AS ( + SELECT + client, + category, + canonicalDomain, + APPROX_QUANTILES(body_size, 1000)[OFFSET(500)] / 1024 AS median_body_size_kb, + APPROX_QUANTILES(time, 1000)[OFFSET(500)] / 1000 AS median_time_s -- noqa: L010 + FROM + requests + INNER JOIN + third_party + ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) + GROUP BY + client, + category, + canonicalDomain +) + +SELECT + ranking, + client, + category, + canonicalDomain, + metric, + sorted_order +FROM ( + SELECT + 'median_body_size_kb' AS ranking, + client, + category, + canonicalDomain, + median_body_size_kb AS metric, + DENSE_RANK() OVER (PARTITION BY client ORDER BY median_body_size_kb DESC) AS sorted_order + FROM base + UNION ALL + SELECT + 'median_time_s' AS ranking, + client, + category, + canonicalDomain, + median_time_s AS metric, + DENSE_RANK() OVER (PARTITION BY client ORDER BY median_time_s DESC) AS sorted_order + FROM base +) +WHERE + sorted_order <= 100 +ORDER BY + ranking, + client, + metric DESC diff --git a/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql b/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql new file mode 100644 index 00000000000..2f51ed65949 --- /dev/null +++ b/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql @@ -0,0 +1,76 @@ +#standardSQL +# Top 100 third parties by number of websites + +WITH requests AS ( + SELECT + _TABLE_SUFFIX AS client, + pageid AS page, + url + FROM + `httparchive.summary_requests.2024_06_01_*` +), + +totals AS ( + SELECT + _TABLE_SUFFIX AS client, + COUNT(DISTINCT pageid) AS total_pages, + COUNT(0) AS total_requests + FROM + `httparchive.summary_requests.2024_06_01_*` + GROUP BY + _TABLE_SUFFIX +), + +third_party AS ( + SELECT + domain, + canonicalDomain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + canonicalDomain, + category + HAVING + page_usage >= 50 +) + +SELECT + client, + canonicalDomain, + COUNT(DISTINCT page) AS pages, + total_pages, + COUNT(DISTINCT page) / total_pages AS pct_pages, + COUNT(0) AS requests, + total_requests, + COUNT(0) / total_requests AS pct_requests, + DENSE_RANK() OVER (PARTITION BY client ORDER BY COUNT(DISTINCT page) DESC) AS sorted_order +FROM + requests +LEFT JOIN + third_party +ON + NET.HOST(requests.url) = NET.HOST(third_party.domain) +JOIN + totals +USING (client) +WHERE + canonicalDomain IS NOT NULL +GROUP BY + client, + total_pages, + total_requests, + canonicalDomain +QUALIFY + sorted_order <= 100 +ORDER BY + pct_pages DESC, + client diff --git a/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql b/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql new file mode 100644 index 00000000000..ba6b2cf23bf --- /dev/null +++ b/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql @@ -0,0 +1,106 @@ +#standardSQL +# Top 20 third-parties embedded in mainframe vs. in iframes + +WITH document_frameid AS ( + SELECT + client, + NET.HOST(page) AS page_host, + NET.HOST(url) AS frame_host, + CASE + WHEN is_main_document = true + THEN JSON_EXTRACT_SCALAR(payload, '$._frame_id') + END AS mainframe_id, + JSON_EXTRACT_SCALAR(payload, '$._frame_id') AS frame_id, + is_main_document + FROM `httparchive.all.requests` AS requests + WHERE requests.date = '2024-06-01' AND requests.is_root_page = true +), + +page_frames AS ( + SELECT + client, + page_host, + frame_host, + CASE + WHEN frame_host != page_host + THEN true + ELSE false + END AS tp_flag, + is_main_document, + frame_id, + COALESCE(mainframe_id, FIRST_VALUE(mainframe_id) OVER (PARTITION BY page_host ORDER BY is_main_document DESC)) AS mainframe_id, + CASE + WHEN frame_id = COALESCE(mainframe_id, FIRST_VALUE(mainframe_id) OVER (PARTITION BY page_host ORDER BY is_main_document DESC)) + THEN 'mainframe' + ELSE 'iframe' + END AS frame_type + FROM document_frameid +), + +combined_frame_counts AS ( + SELECT + client, + page_host, + frame_host, + tp_flag, + CASE + WHEN COUNT(DISTINCT frame_type) = 1 AND MAX(CASE WHEN frame_type = 'mainframe' THEN 1 ELSE 0 END) = 1 + THEN 'mainframe-only' + WHEN COUNT(DISTINCT frame_type) = 1 AND MAX(CASE WHEN frame_type = 'iframe' THEN 1 ELSE 0 END) = 1 + THEN 'iframe-only' + WHEN COUNT(DISTINCT frame_id) >= 2 AND COUNT(DISTINCT frame_type) = 2 + THEN 'both' + END AS frame_presence + FROM page_frames + GROUP BY client, page_host, frame_host, tp_flag +), + +grouped_data AS ( + SELECT + client, + frame_host, + COUNT(DISTINCT page_host) AS total_distinct_publisher_count, + COUNT(DISTINCT CASE WHEN frame_presence = 'mainframe-only' AND tp_flag THEN page_host ELSE null END) AS num_distinct_publishers_mainframe_only, + COUNT(DISTINCT CASE WHEN frame_presence = 'iframe-only' AND tp_flag THEN page_host ELSE null END) AS num_distinct_publishers_iframe_only, + COUNT(DISTINCT CASE WHEN frame_presence = 'both' AND tp_flag THEN page_host ELSE null END) AS num_distinct_publishers_both + FROM combined_frame_counts + GROUP BY client, frame_host +), + +ranked_publishers AS ( + SELECT + client, + frame_host, + num_distinct_publishers_mainframe_only, + num_distinct_publishers_iframe_only, + num_distinct_publishers_both, + ROW_NUMBER() OVER (PARTITION BY client ORDER BY num_distinct_publishers_mainframe_only DESC) AS rank_mainframe, + ROW_NUMBER() OVER (PARTITION BY client ORDER BY num_distinct_publishers_iframe_only DESC) AS rank_iframe, + ROW_NUMBER() OVER (PARTITION BY client ORDER BY num_distinct_publishers_both DESC) AS rank_both + FROM grouped_data +) + +SELECT + client, + frame_host, + num_distinct_publishers_mainframe_only AS num_distinct_publishers, + 'mainframe' AS category +FROM ranked_publishers +WHERE rank_mainframe <= 20 AND num_distinct_publishers_mainframe_only > 0 +UNION ALL +SELECT + client, + frame_host, + num_distinct_publishers_iframe_only AS num_distinct_publishers, + 'iframe' AS category +FROM ranked_publishers +WHERE rank_iframe <= 20 AND num_distinct_publishers_iframe_only > 0 +UNION ALL +SELECT + client, + frame_host, + num_distinct_publishers_both AS num_distinct_publishers, + 'both' AS category +FROM ranked_publishers +WHERE rank_both <= 20 AND num_distinct_publishers_both > 0 +ORDER BY client, category, num_distinct_publishers DESC; diff --git a/sql/2025/third-parties/usage_of_lite_youtube_embed.sql b/sql/2025/third-parties/usage_of_lite_youtube_embed.sql new file mode 100644 index 00000000000..39620794722 --- /dev/null +++ b/sql/2025/third-parties/usage_of_lite_youtube_embed.sql @@ -0,0 +1,37 @@ +#standardSQL +# Percent of pages using lite-youtube-embed + +WITH totals AS ( + SELECT + _TABLE_SUFFIX AS client, + COUNT(DISTINCT url) AS total_pages + FROM + `httparchive.summary_pages.2024_06_01_*` + GROUP BY + client +), + +youtube_embed AS ( + SELECT + _TABLE_SUFFIX AS client, + COUNT(DISTINCT url) AS youtube_embed_pages + FROM + `httparchive.technologies.2024_06_01_*` + WHERE + app = 'lite-youtube-embed' + GROUP BY + client +) + +SELECT + client, + youtube_embed_pages, + total_pages, + youtube_embed_pages / total_pages AS pct_youtube_embed_pages +FROM + totals +JOIN + youtube_embed +USING (client) +ORDER BY + client diff --git a/sql/2025/third-parties/usage_of_partytown.sql b/sql/2025/third-parties/usage_of_partytown.sql new file mode 100644 index 00000000000..80a86ecc1aa --- /dev/null +++ b/sql/2025/third-parties/usage_of_partytown.sql @@ -0,0 +1,37 @@ +#standardSQL +# Percent of pages using Partytown + +WITH totals AS ( + SELECT + _TABLE_SUFFIX AS client, + COUNT(DISTINCT url) AS total_pages + FROM + `httparchive.summary_pages.2024_06_01_*` + GROUP BY + client +), + +partytown AS ( + SELECT + _TABLE_SUFFIX AS client, + COUNT(DISTINCT url) AS partytown_pages + FROM + `httparchive.technologies.2024_06_01_*` + WHERE + app = 'Partytown' + GROUP BY + client +) + +SELECT + client, + partytown_pages, + total_pages, + partytown_pages / total_pages AS pct_partytown_pages +FROM + totals +JOIN + partytown +USING (client) +ORDER BY + client From 771fd5f330bb2202b6f07210d5f7f9ea3959f483 Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Wed, 27 Aug 2025 14:40:41 -0700 Subject: [PATCH 02/37] Updated query for 2025 spec --- ...bution_of_websites_by_number_of_third_parties.sql | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql index 94cfc0d7464..1d91f2deae4 100644 --- a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql +++ b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql @@ -1,15 +1,19 @@ #standardSQL # Distribution of websites by number of third party +-- updated for crawl.requests WITH requests AS ( SELECT - _TABLE_SUFFIX AS client, - pageid AS page, + client, + page, url FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.crawl.requests` + WHERE + date = '2025-07-01' ), + third_party AS ( SELECT domain, @@ -22,7 +26,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, From 0f0ed90187c59faff522f2bed1fa5375e40a802a Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Wed, 27 Aug 2025 14:40:50 -0700 Subject: [PATCH 03/37] Updated date --- .../third-parties/a11y_overall_tech_usage_by_rank.sql | 6 +++--- sql/2025/third-parties/a11y_technology_usage.sql | 4 ++-- .../third-parties/a11y_technology_usage_by_rank.sql | 6 +++--- sql/2025/third-parties/compressed_images_by_3p.sql | 4 ++-- sql/2025/third-parties/content_encoding.sql | 4 ++-- .../third-parties/content_encoding_by_content_type.sql | 4 ++-- sql/2025/third-parties/csp_allowed_host_frequency.sql | 4 ++-- sql/2025/third-parties/depth_of_gtm_calls.sql | 2 +- .../distribution_of_3XX_response_body_size.sql | 4 ++-- ...distribution_of_lighthouse_unminified_css_by_3p.sql | 2 +- .../distribution_of_lighthouse_unminified_js_by_3p.sql | 2 +- .../distribution_of_lighthouse_unused_css_by_3p.sql | 2 +- .../distribution_of_lighthouse_unused_js_by_3p.sql | 2 +- ...ution_of_lighthouse_uses_optimized_images_by_3p.sql | 2 +- .../distribution_of_size_and_time_by_third_parties.sql | 4 ++-- .../distribution_of_third_parties_by_frame.sql | 2 +- ...ribution_of_third_parties_by_number_of_websites.sql | 4 ++-- ..._of_websites_by_number_of_third_party_providers.sql | 4 ++-- sql/2025/third-parties/iframe_allow_attribute.sql | 4 ++-- .../third-parties/iframe_attribute_popular_hosts.sql | 4 ++-- .../lighthouse_average_unminified_css_by_3p.sql | 4 ++-- .../lighthouse_average_unminified_js_by_3p.sql | 4 ++-- .../third-parties/lighthouse_third_party_facades.sql | 2 +- .../third-parties/lighthouse_unminified_css_by_3p.sql | 2 +- .../third-parties/lighthouse_unminified_js_by_3p.sql | 2 +- .../lighthouse_unminified_js_by_3p_by_url.sql | 2 +- ...ghthouse_unminified_uses_optimized_images_by_3p.sql | 2 +- .../lighthouse_unused_css_bytes_by_3p.sql | 2 +- .../third-parties/lighthouse_unused_js_bytes_by_3p.sql | 2 +- .../third-parties/number_of_third_parties_by_rank.sql | 6 +++--- .../number_of_third_parties_by_rank_and_category.sql | 6 +++--- .../number_of_third_party_providers_by_rank.sql | 6 +++--- ...r_of_third_party_providers_by_rank_and_category.sql | 6 +++--- .../number_of_third_party_requests_by_rank.sql | 4 ++-- ...number_of_third_party_requests_per_page_by_rank.sql | 4 ++-- .../percent_of_third_parties_by_content_type.sql | 4 ++-- .../percent_of_third_parties_using_document_write.sql | 4 ++-- ...ercent_of_third_parties_using_legacy_javascript.sql | 4 ++-- ...of_third_parties_using_legacy_javascript_by_url.sql | 4 ++-- .../third-parties/percent_of_third_party_cache.sql | 4 ++-- ...requests_and_bytes_by_category_and_content_type.sql | 4 ++-- .../percent_of_third_party_with_security_headers.sql | 4 ++-- .../percent_of_websites_with_third_party.sql | 4 ++-- ...percent_of_websites_with_third_party_by_ranking.sql | 6 +++--- sql/2025/third-parties/scripts_using_async_defer.sql | 2 +- .../third-parties/scripts_using_async_defer_by_3p.sql | 2 +- sql/2025/third-parties/tao_by_third_party.sql | 6 +++--- .../third_parties_blocking_main_thread.sql | 2 +- .../third_parties_blocking_main_thread_percentiles.sql | 2 +- ...arties_blocking_main_thread_percentiles_by_host.sql | 2 +- .../third-parties/third_parties_blocking_rendering.sql | 8 ++++---- .../third_parties_blocking_rendering_percentiles.sql | 10 +++++----- .../third_parties_using_legacy_javascript.sql | 2 +- ...p100_third_parties_by_median_body_size_and_time.sql | 4 ++-- .../top100_third_parties_by_number_of_websites.sql | 6 +++--- ...op20_third_parties_by_client_and_frame_location.sql | 2 +- sql/2025/third-parties/usage_of_lite_youtube_embed.sql | 4 ++-- sql/2025/third-parties/usage_of_partytown.sql | 4 ++-- 58 files changed, 109 insertions(+), 109 deletions(-) diff --git a/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql b/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql index df65fa3359c..a31b1c9ea29 100644 --- a/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql +++ b/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql @@ -6,7 +6,7 @@ WITH a11y_technologies AS ( _TABLE_SUFFIX AS client, url FROM - `httparchive.technologies.2024_06_01_*` + `httparchive.technologies.2025_07_01_*` WHERE category = 'Accessibility' ), @@ -17,7 +17,7 @@ pages AS ( url, rank_grouping FROM - `httparchive.summary_pages.2024_06_01_*`, + `httparchive.summary_pages.2025_07_01_*`, UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE rank <= rank_grouping @@ -29,7 +29,7 @@ rank_totals AS ( rank_grouping, COUNT(0) AS total FROM - `httparchive.summary_pages.2024_06_01_*`, + `httparchive.summary_pages.2025_07_01_*`, UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE rank <= rank_grouping diff --git a/sql/2025/third-parties/a11y_technology_usage.sql b/sql/2025/third-parties/a11y_technology_usage.sql index ca76cdc1053..3aea501592c 100644 --- a/sql/2025/third-parties/a11y_technology_usage.sql +++ b/sql/2025/third-parties/a11y_technology_usage.sql @@ -6,7 +6,7 @@ WITH a11y_technologies AS ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS freq FROM - `httparchive.technologies.2024_06_01_*` + `httparchive.technologies.2025_07_01_*` WHERE category = 'Accessibility' GROUP BY @@ -18,7 +18,7 @@ pages AS ( _TABLE_SUFFIX AS client, COUNT(0) AS total FROM - `httparchive.summary_pages.2024_06_01_*` + `httparchive.summary_pages.2025_07_01_*` GROUP BY client ) diff --git a/sql/2025/third-parties/a11y_technology_usage_by_rank.sql b/sql/2025/third-parties/a11y_technology_usage_by_rank.sql index d2cc1da3c0a..992cb454217 100644 --- a/sql/2025/third-parties/a11y_technology_usage_by_rank.sql +++ b/sql/2025/third-parties/a11y_technology_usage_by_rank.sql @@ -7,7 +7,7 @@ WITH a11y_technologies AS ( app, url FROM - `httparchive.technologies.2024_06_01_*` + `httparchive.technologies.2025_07_01_*` WHERE category = 'Accessibility' ), @@ -18,7 +18,7 @@ pages AS ( url, rank_grouping FROM - `httparchive.summary_pages.2024_06_01_*`, + `httparchive.summary_pages.2025_07_01_*`, UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE rank <= rank_grouping @@ -30,7 +30,7 @@ rank_totals AS ( rank_grouping, COUNT(0) AS total FROM - `httparchive.summary_pages.2024_06_01_*`, + `httparchive.summary_pages.2025_07_01_*`, UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE rank <= rank_grouping diff --git a/sql/2025/third-parties/compressed_images_by_3p.sql b/sql/2025/third-parties/compressed_images_by_3p.sql index a9b4db22682..db3af73c9a2 100644 --- a/sql/2025/third-parties/compressed_images_by_3p.sql +++ b/sql/2025/third-parties/compressed_images_by_3p.sql @@ -10,7 +10,7 @@ WITH requests AS ( type, respBodySize AS size FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` WHERE type = 'image' AND ( resp_content_encoding = 'gzip' OR @@ -31,7 +31,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain diff --git a/sql/2025/third-parties/content_encoding.sql b/sql/2025/third-parties/content_encoding.sql index f79dad7fdbb..31cdb2a691e 100644 --- a/sql/2025/third-parties/content_encoding.sql +++ b/sql/2025/third-parties/content_encoding.sql @@ -8,7 +8,7 @@ WITH requests AS ( url, resp_content_encoding AS content_encoding FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), third_party AS ( @@ -21,7 +21,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain diff --git a/sql/2025/third-parties/content_encoding_by_content_type.sql b/sql/2025/third-parties/content_encoding_by_content_type.sql index efd2a7cdae1..74dd000ea2a 100644 --- a/sql/2025/third-parties/content_encoding_by_content_type.sql +++ b/sql/2025/third-parties/content_encoding_by_content_type.sql @@ -9,7 +9,7 @@ WITH requests AS ( resp_content_encoding AS content_encoding, type FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), third_party AS ( @@ -22,7 +22,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain diff --git a/sql/2025/third-parties/csp_allowed_host_frequency.sql b/sql/2025/third-parties/csp_allowed_host_frequency.sql index 95d169ce290..fbc11b365d1 100644 --- a/sql/2025/third-parties/csp_allowed_host_frequency.sql +++ b/sql/2025/third-parties/csp_allowed_host_frequency.sql @@ -19,7 +19,7 @@ WITH totals AS ( FROM `httparchive.all.requests` WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND is_main_document GROUP BY client @@ -33,7 +33,7 @@ csp_data AS ( FROM `httparchive.all.requests` WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND is_main_document AND response_headers IS NOT NULL ), diff --git a/sql/2025/third-parties/depth_of_gtm_calls.sql b/sql/2025/third-parties/depth_of_gtm_calls.sql index 46cc89546d0..1fd8ab24c94 100644 --- a/sql/2025/third-parties/depth_of_gtm_calls.sql +++ b/sql/2025/third-parties/depth_of_gtm_calls.sql @@ -67,7 +67,7 @@ WITH data AS ( `httparchive.all.requests` WHERE NET.REG_DOMAIN(root_page) != NET.REG_DOMAIN(url) AND - date = '2024-06-01' + date = '2025-07-01' ) WHERE third_party != initiator_etld AND root_page != initiator_etld diff --git a/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql b/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql index 9e4ca7954b3..602b98836be 100644 --- a/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql +++ b/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql @@ -10,7 +10,7 @@ WITH requests AS ( status, respBodySize AS body_size FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), third_party AS ( @@ -24,7 +24,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql index 798d1bd4558..14bcbc043a1 100644 --- a/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql +++ b/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql @@ -33,7 +33,7 @@ base AS ( NET.HOST(data.url) AS domain, data.wastedBytes AS potential_savings FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUnminifiedCssUrls(JSON_EXTRACT(report, "$.audits['unminified-css']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql index 99de8659ec9..e7c6045c8c0 100644 --- a/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql +++ b/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql @@ -34,7 +34,7 @@ base AS ( NET.HOST(data.url) AS domain, data.wastedBytes AS potential_savings FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUnminifiedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql index d760a82e9c2..194bdf93ca0 100644 --- a/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql +++ b/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql @@ -34,7 +34,7 @@ base AS ( NET.HOST(data.url) AS domain, data.wastedBytes AS potential_savings FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUnusedCSSUrls(JSON_EXTRACT(report, "$.audits['unused-css-rules']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql index 60688c36785..dfc7bb12e3a 100644 --- a/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql +++ b/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql @@ -34,7 +34,7 @@ base AS ( NET.HOST(data.url) AS domain, data.wastedBytes AS potential_savings FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUnusedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unused-javascript']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql index f4aebe8b831..5bc6e31108d 100644 --- a/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql +++ b/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql @@ -33,7 +33,7 @@ base AS ( NET.HOST(data.url) AS domain, data.wastedBytes AS potential_savings FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUnminifiedImageUrls(JSON_EXTRACT(report, "$.audits['uses-optimized-images']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql b/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql index 88c7138ecbf..e1cc8e16741 100644 --- a/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql +++ b/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql @@ -9,7 +9,7 @@ WITH requests AS ( respBodySize AS body_size, time FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), third_party AS ( @@ -23,7 +23,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql b/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql index 5b19681ec76..b0959816ee1 100644 --- a/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql +++ b/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql @@ -13,7 +13,7 @@ WITH document_frameid AS ( JSON_EXTRACT_SCALAR(payload, '$._frame_id') AS frame_id, is_main_document FROM `httparchive.all.requests` AS requests - WHERE requests.date = '2024-06-01' AND requests.is_root_page = true + WHERE requests.date = '2025-07-01' AND requests.is_root_page = true ), page_frames AS ( diff --git a/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql b/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql index 5e648f75ec6..9ca4552c45b 100644 --- a/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql +++ b/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql @@ -7,7 +7,7 @@ WITH requests AS ( pageid AS page, url FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), third_party AS ( @@ -22,7 +22,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql index 162b1cc9f71..fe0e9ed890c 100644 --- a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql +++ b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql @@ -7,7 +7,7 @@ WITH requests AS ( pageid AS page, url FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), third_party AS ( @@ -22,7 +22,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/iframe_allow_attribute.sql b/sql/2025/third-parties/iframe_allow_attribute.sql index c412c6455e2..d575d025cf8 100644 --- a/sql/2025/third-parties/iframe_allow_attribute.sql +++ b/sql/2025/third-parties/iframe_allow_attribute.sql @@ -21,7 +21,7 @@ FROM ( _TABLE_SUFFIX AS client, JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox') AS iframeAttrs FROM - `httparchive.pages.2024_06_01_*` + `httparchive.pages.2025_07_01_*` ), UNNEST(iframeAttrs) AS iframeAttr, UNNEST(REGEXP_EXTRACT_ALL(JSON_EXTRACT_SCALAR(iframeAttr, '$.allow'), r'(?i)([^,;]+)')) AS allow_attr @@ -30,7 +30,7 @@ JOIN ( _TABLE_SUFFIX AS client, SUM(getNumWithAllowAttribute(payload)) AS total_iframes_with_allow FROM - `httparchive.pages.2024_06_01_*` + `httparchive.pages.2025_07_01_*` GROUP BY client ) USING (client) diff --git a/sql/2025/third-parties/iframe_attribute_popular_hosts.sql b/sql/2025/third-parties/iframe_attribute_popular_hosts.sql index f7f1d49a1e2..3c03620c9ea 100644 --- a/sql/2025/third-parties/iframe_attribute_popular_hosts.sql +++ b/sql/2025/third-parties/iframe_attribute_popular_hosts.sql @@ -26,7 +26,7 @@ FROM ( _TABLE_SUFFIX AS client, JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox') AS iframeAttrs FROM - `httparchive.pages.2024_06_01_*` + `httparchive.pages.2025_07_01_*` ), UNNEST(iframeAttrs) AS iframeAttr, UNNEST(['allow', 'sandbox']) AS policy_type @@ -36,7 +36,7 @@ JOIN ( _TABLE_SUFFIX AS client, SUM(ARRAY_LENGTH(JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox'))) AS total_iframes FROM - `httparchive.pages.2024_06_01_*` + `httparchive.pages.2025_07_01_*` GROUP BY client ) diff --git a/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql b/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql index dafd87772b5..f2b38666ac2 100644 --- a/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql @@ -27,14 +27,14 @@ FROM ( client, page, NET.HOST(unminified.url) IS NOT NULL AND NET.HOST(unminified.url) IN ( - SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2024-06-01' AND category != 'hosting' + SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2025-07-01' AND category != 'hosting' ) AS is_3p, unminified.wastedBytes AS wasted_bytes FROM `httparchive.all.pages` AS allpages CROSS JOIN UNNEST(getUnminifiedJsUrls(JSON_EXTRACT(allpages.lighthouse, "$.audits['unminified-css']"))) AS unminified - WHERE allpages.date = '2024-06-01' AND allpages.is_root_page = TRUE + WHERE allpages.date = '2025-07-01' AND allpages.is_root_page = TRUE ) GROUP BY client, diff --git a/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql b/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql index 49e36b4a5dc..1256a800f5d 100644 --- a/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql @@ -27,11 +27,11 @@ FROM ( _TABLE_SUFFIX AS client, lighthouse.url AS page, NET.HOST(unminified.url) IS NOT NULL AND NET.HOST(unminified.url) IN ( - SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2024-06-01' AND category != 'hosting' + SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2025-07-01' AND category != 'hosting' ) AS is_3p, unminified.wastedBytes AS wasted_bytes FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUnminifiedJsUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS unminified ) GROUP BY diff --git a/sql/2025/third-parties/lighthouse_third_party_facades.sql b/sql/2025/third-parties/lighthouse_third_party_facades.sql index 725460ce1a5..20c97be4824 100644 --- a/sql/2025/third-parties/lighthouse_third_party_facades.sql +++ b/sql/2025/third-parties/lighthouse_third_party_facades.sql @@ -10,7 +10,7 @@ FROM ( SUM(COUNT(0)) OVER (PARTITION BY _TABLE_SUFFIX) AS total, COUNTIF(SAFE_CAST(JSON_VALUE(report, '$.audits.third-party-facades.score') AS FLOAT64) < 0.9) / SUM(COUNT(0)) OVER (PARTITION BY _TABLE_SUFFIX) AS pct FROM - `httparchive.lighthouse.2024_06_01_*` + `httparchive.lighthouse.2025_07_01_*` GROUP BY client ) diff --git a/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql b/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql index a36e300d222..8784dab8013 100644 --- a/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql @@ -35,7 +35,7 @@ base AS ( data.wastedBytes AS potential_savings, data.totalBytes AS transfer_size FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUnminifiedCssUrls(JSON_EXTRACT(report, "$.audits['unminified-css']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql b/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql index 4cd75c0b3c8..5422879602c 100644 --- a/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql @@ -35,7 +35,7 @@ base AS ( data.wastedBytes AS potential_savings, data.totalBytes AS transfer_size FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUnminifiedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql b/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql index 41b0b19d910..4fa02f8fd12 100644 --- a/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql +++ b/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql @@ -37,7 +37,7 @@ base AS ( data.wastedBytes AS potential_savings, data.totalBytes AS transfer_size FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUnminifiedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql b/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql index 8a4ff88ef38..ff76f827089 100644 --- a/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql @@ -35,7 +35,7 @@ base AS ( data.wastedBytes AS potential_savings, data.totalBytes AS transfer_size FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUnminifiedImageUrls(JSON_EXTRACT(report, "$.audits['uses-optimized-images']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql b/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql index 5ff6f31495f..5bf622a4429 100644 --- a/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql @@ -35,7 +35,7 @@ base AS ( data.wastedBytes AS potential_savings, data.totalBytes AS transfer_size FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUnusedCssUrls(JSON_EXTRACT(report, "$.audits['unused-css-rules']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql b/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql index cce307298b2..134afb835cf 100644 --- a/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql @@ -35,7 +35,7 @@ base AS ( data.wastedBytes AS potential_savings, data.totalBytes AS transfer_size FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUnusedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unused-javascript']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank.sql b/sql/2025/third-parties/number_of_third_parties_by_rank.sql index a97d0e617e0..692e5efc7c6 100644 --- a/sql/2025/third-parties/number_of_third_parties_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_parties_by_rank.sql @@ -8,7 +8,7 @@ WITH requests AS ( FROM `httparchive.all.requests` AS req WHERE - req.date = '2024-06-01' AND + req.date = '2025-07-01' AND req.is_root_page = true ), @@ -20,7 +20,7 @@ pages AS ( FROM `httparchive.all.pages` AS pg WHERE - pg.date = '2024-06-01' AND + pg.date = '2025-07-01' AND pg.is_root_page = true ), @@ -35,7 +35,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql index 55cbbd657c7..a20d0185e84 100644 --- a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql +++ b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql @@ -7,7 +7,7 @@ WITH requests AS ( pageid AS page, url FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), pages AS ( @@ -16,7 +16,7 @@ pages AS ( pageid AS page, rank FROM - `httparchive.summary_pages.2024_06_01_*` + `httparchive.summary_pages.2025_07_01_*` ), third_party AS ( @@ -31,7 +31,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category NOT IN ('hosting') GROUP BY domain, diff --git a/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql b/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql index 15577d1481a..4ab19893591 100644 --- a/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql @@ -6,7 +6,7 @@ WITH requests AS ( pageid AS page, url FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), pages AS ( @@ -15,7 +15,7 @@ pages AS ( pageid AS page, rank FROM - `httparchive.summary_pages.2024_06_01_*` + `httparchive.summary_pages.2025_07_01_*` ), third_party AS ( @@ -30,7 +30,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql index 5bc156dea77..9f1db177d3e 100644 --- a/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql +++ b/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql @@ -7,7 +7,7 @@ WITH requests AS ( pageid AS page, url FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), pages AS ( @@ -16,7 +16,7 @@ pages AS ( pageid AS page, rank FROM - `httparchive.summary_pages.2024_06_01_*` + `httparchive.summary_pages.2025_07_01_*` ), third_party AS ( @@ -31,7 +31,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category NOT IN ('hosting') GROUP BY domain, diff --git a/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql b/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql index 8aa31d57ce4..7a6b00d3c0d 100644 --- a/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql @@ -8,7 +8,7 @@ WITH requests AS ( FROM `httparchive.all.requests` AS req WHERE - req.date = '2024-06-01' AND + req.date = '2025-07-01' AND req.is_root_page = true ), @@ -20,7 +20,7 @@ pages AS ( FROM `httparchive.all.pages` AS pg WHERE - pg.date = '2024-06-01' AND + pg.date = '2025-07-01' AND pg.is_root_page = true ), diff --git a/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql b/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql index dcc609ef8ba..3ac44225cb4 100644 --- a/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql @@ -8,7 +8,7 @@ WITH requests AS ( FROM `httparchive.all.requests` AS req WHERE - req.date = '2024-06-01' AND + req.date = '2025-07-01' AND req.is_root_page = true ), @@ -20,7 +20,7 @@ pages AS ( FROM `httparchive.all.pages` AS pg WHERE - pg.date = '2024-06-01' AND + pg.date = '2025-07-01' AND pg.is_root_page = true ), diff --git a/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql b/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql index 32279f1349f..57a8d0198b3 100644 --- a/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql +++ b/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql @@ -8,7 +8,7 @@ WITH requests AS ( url, type AS contentType FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), third_party AS ( @@ -22,7 +22,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql b/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql index c5c0928b33a..8a3b1f02ed3 100644 --- a/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql +++ b/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql @@ -31,7 +31,7 @@ base AS ( NET.HOST(data.url) AS domain, lighthouse.url AS page FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['no-document-write']"))) AS data ) AS potential_third_parties INNER JOIN @@ -57,7 +57,7 @@ JOIN ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS total FROM - `httparchive.lighthouse.2024_06_01_*` + `httparchive.lighthouse.2025_07_01_*` GROUP BY _TABLE_SUFFIX ) diff --git a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql index a2b91afb054..485075ae460 100644 --- a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql +++ b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql @@ -31,7 +31,7 @@ base AS ( NET.HOST(data.url) AS domain, lighthouse.url AS page FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['legacy-javascript']"))) AS data ) AS potential_third_parties INNER JOIN @@ -57,7 +57,7 @@ JOIN ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS total FROM - `httparchive.lighthouse.2024_06_01_*` + `httparchive.lighthouse.2025_07_01_*` GROUP BY _TABLE_SUFFIX ) diff --git a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql index 80cd3ba707e..4c58e8bb7fa 100644 --- a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql +++ b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql @@ -33,7 +33,7 @@ base AS ( NET.HOST(data.url) AS domain, lighthouse.url AS page FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['legacy-javascript']"))) AS data ) AS potential_third_parties INNER JOIN @@ -70,7 +70,7 @@ FROM ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS total FROM - `httparchive.lighthouse.2024_06_01_*` + `httparchive.lighthouse.2025_07_01_*` GROUP BY _TABLE_SUFFIX ) diff --git a/sql/2025/third-parties/percent_of_third_party_cache.sql b/sql/2025/third-parties/percent_of_third_party_cache.sql index 16bf3f855d0..1d4298da0fe 100644 --- a/sql/2025/third-parties/percent_of_third_party_cache.sql +++ b/sql/2025/third-parties/percent_of_third_party_cache.sql @@ -13,7 +13,7 @@ WITH requests AS ( url, pageid AS page FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), third_party AS ( @@ -27,7 +27,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql b/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql index dd45c790156..6bd022fac72 100644 --- a/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql +++ b/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql @@ -9,7 +9,7 @@ WITH requests AS ( type AS contentType, respBodySize AS body_size FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), third_party AS ( @@ -23,7 +23,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql b/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql index 7701723753a..724b05784c1 100644 --- a/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql +++ b/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql @@ -9,7 +9,7 @@ WITH requests AS ( RTRIM(urlShort, '/') AS origin, respOtherHeaders FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), third_party AS ( @@ -23,7 +23,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_websites_with_third_party.sql b/sql/2025/third-parties/percent_of_websites_with_third_party.sql index 732377fff57..efd8328631a 100644 --- a/sql/2025/third-parties/percent_of_websites_with_third_party.sql +++ b/sql/2025/third-parties/percent_of_websites_with_third_party.sql @@ -8,7 +8,7 @@ WITH requests AS ( url, respBodySize FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), third_party AS ( @@ -23,7 +23,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql b/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql index b6ceadbf8eb..1d5add84a72 100644 --- a/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql +++ b/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql @@ -7,7 +7,7 @@ WITH requests AS ( pageid AS page, url FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), third_party AS ( @@ -21,7 +21,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, @@ -36,7 +36,7 @@ pages AS ( pageid AS page, rank FROM - `httparchive.summary_pages.2024_06_01_*` + `httparchive.summary_pages.2025_07_01_*` ) SELECT diff --git a/sql/2025/third-parties/scripts_using_async_defer.sql b/sql/2025/third-parties/scripts_using_async_defer.sql index cd324dfd581..74edf975d53 100644 --- a/sql/2025/third-parties/scripts_using_async_defer.sql +++ b/sql/2025/third-parties/scripts_using_async_defer.sql @@ -48,7 +48,7 @@ base AS ( data.isDefer AS isDefer, pages.url AS page FROM - `httparchive.pages.2024_06_01_*` AS pages, + `httparchive.pages.2025_07_01_*` AS pages, UNNEST(getScripts(JSON_EXTRACT_SCALAR(payload, '$._almanac'))) AS data ) AS potential_third_parties INNER JOIN diff --git a/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql b/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql index 0bed778f659..2c9cccaea02 100644 --- a/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql +++ b/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql @@ -48,7 +48,7 @@ base AS ( data.isDefer AS isDefer, pages.url AS page FROM - `httparchive.pages.2024_06_01_*` AS pages, + `httparchive.pages.2025_07_01_*` AS pages, UNNEST(getScripts(JSON_EXTRACT_SCALAR(payload, '$._almanac'))) AS data ) AS potential_third_parties INNER JOIN diff --git a/sql/2025/third-parties/tao_by_third_party.sql b/sql/2025/third-parties/tao_by_third_party.sql index d7fdf825816..33066d84410 100644 --- a/sql/2025/third-parties/tao_by_third_party.sql +++ b/sql/2025/third-parties/tao_by_third_party.sql @@ -22,7 +22,7 @@ WITH requests AS ( RTRIM(urlShort, '/') AS origin, respOtherHeaders FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), pages AS ( @@ -32,7 +32,7 @@ pages AS ( pageid AS page, RTRIM(urlShort, '/') AS origin FROM - `httparchive.summary_pages.2024_06_01_*` + `httparchive.summary_pages.2025_07_01_*` ), third_party AS ( @@ -46,7 +46,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/third_parties_blocking_main_thread.sql b/sql/2025/third-parties/third_parties_blocking_main_thread.sql index 77368d280f1..2ae6ac4112d 100644 --- a/sql/2025/third-parties/third_parties_blocking_main_thread.sql +++ b/sql/2025/third-parties/third_parties_blocking_main_thread.sql @@ -48,7 +48,7 @@ FROM ( url AS page, report FROM - `httparchive.lighthouse.2024_06_01_*` + `httparchive.lighthouse.2025_07_01_*` ), UNNEST(JSON_QUERY_ARRAY(report, '$.audits.third-party-summary.details.items')) AS third_party_items GROUP BY diff --git a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql index 946b9b59bea..78974be947c 100644 --- a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql +++ b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql @@ -37,7 +37,7 @@ FROM ( url AS page, report FROM - `httparchive.lighthouse.2024_06_01_*` + `httparchive.lighthouse.2025_07_01_*` ), UNNEST(JSON_QUERY_ARRAY(report, '$.audits.third-party-summary.details.items')) AS third_party_items GROUP BY diff --git a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql index 2cd8352e437..74800807d4a 100644 --- a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql +++ b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql @@ -43,7 +43,7 @@ FROM ( url AS page, report FROM - `httparchive.lighthouse.2024_06_01_*` + `httparchive.lighthouse.2025_07_01_*` ), UNNEST(JSON_QUERY_ARRAY(report, '$.audits.third-party-summary.details.items')) AS third_party_items GROUP BY diff --git a/sql/2025/third-parties/third_parties_blocking_rendering.sql b/sql/2025/third-parties/third_parties_blocking_rendering.sql index ab151e43548..cac9e2832da 100644 --- a/sql/2025/third-parties/third_parties_blocking_rendering.sql +++ b/sql/2025/third-parties/third_parties_blocking_rendering.sql @@ -16,14 +16,14 @@ WITH total_third_party_usage AS ( category, COUNT(DISTINCT pages.url) AS total_pages FROM - `httparchive.summary_pages.2024_06_01_*` AS pages + `httparchive.summary_pages.2025_07_01_*` AS pages INNER JOIN ( SELECT _TABLE_SUFFIX AS client, pageid, url FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ) AS requests ON ( pages._TABLE_SUFFIX = requests.client AND @@ -33,7 +33,7 @@ WITH total_third_party_usage AS ( `httparchive.almanac.third_parties` ON NET.HOST(requests.url) = NET.HOST(domain) AND - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY client, @@ -83,7 +83,7 @@ FROM ( url AS page, report FROM - `httparchive.lighthouse.2024_06_01_*` + `httparchive.lighthouse.2025_07_01_*` ), UNNEST(JSON_QUERY_ARRAY(report, '$.audits.render-blocking-resources.details.items')) AS renderBlockingItems INNER JOIN diff --git a/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql b/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql index 89ae5a05665..d6c70dd20d1 100644 --- a/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql +++ b/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql @@ -16,14 +16,14 @@ WITH total_third_party_usage AS ( category, COUNT(DISTINCT pages.url) AS total_pages FROM - `httparchive.summary_pages.2024_06_01_*` AS pages + `httparchive.summary_pages.2025_07_01_*` AS pages INNER JOIN ( SELECT _TABLE_SUFFIX AS client, pageid, url FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ) AS requests ON ( pages._TABLE_SUFFIX = requests.client AND @@ -33,7 +33,7 @@ WITH total_third_party_usage AS ( `httparchive.almanac.third_parties` ON NET.HOST(requests.url) = NET.HOST(domain) AND - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY client, @@ -78,14 +78,14 @@ FROM ( url AS page, report FROM - `httparchive.lighthouse.2024_06_01_*` + `httparchive.lighthouse.2025_07_01_*` ), UNNEST(JSON_QUERY_ARRAY(report, '$.audits.render-blocking-resources.details.items')) AS render_blocking_items INNER JOIN `httparchive.almanac.third_parties` ON NET.HOST(JSON_VALUE(render_blocking_items, '$.url')) = domain AND - date = '2024-06-01' + date = '2025-07-01' GROUP BY client, canonicalDomain, diff --git a/sql/2025/third-parties/third_parties_using_legacy_javascript.sql b/sql/2025/third-parties/third_parties_using_legacy_javascript.sql index 937d6a1c665..453bd82ca51 100644 --- a/sql/2025/third-parties/third_parties_using_legacy_javascript.sql +++ b/sql/2025/third-parties/third_parties_using_legacy_javascript.sql @@ -33,7 +33,7 @@ base AS ( NET.HOST(data.url) AS domain, lighthouse.url AS page FROM - `httparchive.lighthouse.2024_06_01_*` AS lighthouse, + `httparchive.lighthouse.2025_07_01_*` AS lighthouse, UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['legacy-javascript']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql b/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql index a8b84e1ecd2..c1c292d881b 100644 --- a/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql +++ b/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql @@ -9,7 +9,7 @@ WITH requests AS ( respBodySize AS body_size, time FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), third_party AS ( @@ -24,7 +24,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql b/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql index 2f51ed65949..8e7315e02af 100644 --- a/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql +++ b/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql @@ -7,7 +7,7 @@ WITH requests AS ( pageid AS page, url FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` ), totals AS ( @@ -16,7 +16,7 @@ totals AS ( COUNT(DISTINCT pageid) AS total_pages, COUNT(0) AS total_requests FROM - `httparchive.summary_requests.2024_06_01_*` + `httparchive.summary_requests.2025_07_01_*` GROUP BY _TABLE_SUFFIX ), @@ -33,7 +33,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql b/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql index ba6b2cf23bf..548aa20da24 100644 --- a/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql +++ b/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql @@ -13,7 +13,7 @@ WITH document_frameid AS ( JSON_EXTRACT_SCALAR(payload, '$._frame_id') AS frame_id, is_main_document FROM `httparchive.all.requests` AS requests - WHERE requests.date = '2024-06-01' AND requests.is_root_page = true + WHERE requests.date = '2025-07-01' AND requests.is_root_page = true ), page_frames AS ( diff --git a/sql/2025/third-parties/usage_of_lite_youtube_embed.sql b/sql/2025/third-parties/usage_of_lite_youtube_embed.sql index 39620794722..eba5f08c535 100644 --- a/sql/2025/third-parties/usage_of_lite_youtube_embed.sql +++ b/sql/2025/third-parties/usage_of_lite_youtube_embed.sql @@ -6,7 +6,7 @@ WITH totals AS ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS total_pages FROM - `httparchive.summary_pages.2024_06_01_*` + `httparchive.summary_pages.2025_07_01_*` GROUP BY client ), @@ -16,7 +16,7 @@ youtube_embed AS ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS youtube_embed_pages FROM - `httparchive.technologies.2024_06_01_*` + `httparchive.technologies.2025_07_01_*` WHERE app = 'lite-youtube-embed' GROUP BY diff --git a/sql/2025/third-parties/usage_of_partytown.sql b/sql/2025/third-parties/usage_of_partytown.sql index 80a86ecc1aa..4957d1ca847 100644 --- a/sql/2025/third-parties/usage_of_partytown.sql +++ b/sql/2025/third-parties/usage_of_partytown.sql @@ -6,7 +6,7 @@ WITH totals AS ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS total_pages FROM - `httparchive.summary_pages.2024_06_01_*` + `httparchive.summary_pages.2025_07_01_*` GROUP BY client ), @@ -16,7 +16,7 @@ partytown AS ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS partytown_pages FROM - `httparchive.technologies.2024_06_01_*` + `httparchive.technologies.2025_07_01_*` WHERE app = 'Partytown' GROUP BY From a9da756e09fae38bafd8a48cf69aa886a96e76ba Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Wed, 27 Aug 2025 18:11:14 -0700 Subject: [PATCH 04/37] Temp changes to dates, 2025-06 instead of 2025-07 --- .../a11y_overall_tech_usage_by_rank.sql | 6 +++--- .../third-parties/a11y_technology_usage.sql | 4 ++-- .../a11y_technology_usage_by_rank.sql | 6 +++--- .../third-parties/compressed_images_by_3p.sql | 4 ++-- sql/2025/third-parties/content_encoding.sql | 4 ++-- .../content_encoding_by_content_type.sql | 4 ++-- .../csp_allowed_host_frequency.sql | 4 ++-- sql/2025/third-parties/depth_of_gtm_calls.sql | 2 +- ...distribution_of_3XX_response_body_size.sql | 4 ++-- ...ion_of_lighthouse_unminified_css_by_3p.sql | 2 +- ...tion_of_lighthouse_unminified_js_by_3p.sql | 2 +- ...ibution_of_lighthouse_unused_css_by_3p.sql | 2 +- ...ribution_of_lighthouse_unused_js_by_3p.sql | 2 +- ...lighthouse_uses_optimized_images_by_3p.sql | 2 +- ...tion_of_size_and_time_by_third_parties.sql | 4 ++-- ...distribution_of_third_parties_by_frame.sql | 2 +- ...of_third_parties_by_number_of_websites.sql | 4 ++-- ...of_websites_by_number_of_third_parties.sql | 4 ++-- ...tes_by_number_of_third_party_providers.sql | 4 ++-- .../third-parties/iframe_allow_attribute.sql | 4 ++-- .../iframe_attribute_popular_hosts.sql | 4 ++-- ...ighthouse_average_unminified_css_by_3p.sql | 4 ++-- ...lighthouse_average_unminified_js_by_3p.sql | 4 ++-- .../lighthouse_third_party_facades.sql | 2 +- .../lighthouse_unminified_css_by_3p.sql | 2 +- .../lighthouse_unminified_js_by_3p.sql | 2 +- .../lighthouse_unminified_js_by_3p_by_url.sql | 2 +- ...unminified_uses_optimized_images_by_3p.sql | 2 +- .../lighthouse_unused_css_bytes_by_3p.sql | 2 +- .../lighthouse_unused_js_bytes_by_3p.sql | 2 +- .../number_of_third_parties_by_rank.sql | 6 +++--- ..._of_third_parties_by_rank_and_category.sql | 6 +++--- ...umber_of_third_party_providers_by_rank.sql | 6 +++--- ...d_party_providers_by_rank_and_category.sql | 6 +++--- ...number_of_third_party_requests_by_rank.sql | 4 ++-- ..._third_party_requests_per_page_by_rank.sql | 4 ++-- ...rcent_of_third_parties_by_content_type.sql | 4 ++-- ..._of_third_parties_using_document_write.sql | 4 ++-- ..._third_parties_using_legacy_javascript.sql | 4 ++-- ...parties_using_legacy_javascript_by_url.sql | 4 ++-- .../percent_of_third_party_cache.sql | 4 ++-- ...and_bytes_by_category_and_content_type.sql | 4 ++-- ...t_of_third_party_with_security_headers.sql | 4 ++-- .../percent_of_websites_with_third_party.sql | 4 ++-- ...f_websites_with_third_party_by_ranking.sql | 19 ++++++++++++------- .../scripts_using_async_defer.sql | 2 +- .../scripts_using_async_defer_by_3p.sql | 2 +- sql/2025/third-parties/tao_by_third_party.sql | 6 +++--- .../third_parties_blocking_main_thread.sql | 2 +- ...rties_blocking_main_thread_percentiles.sql | 2 +- ...ocking_main_thread_percentiles_by_host.sql | 2 +- .../third_parties_blocking_rendering.sql | 8 ++++---- ...parties_blocking_rendering_percentiles.sql | 10 +++++----- .../third_parties_using_legacy_javascript.sql | 2 +- ...d_parties_by_median_body_size_and_time.sql | 4 ++-- ...00_third_parties_by_number_of_websites.sql | 6 +++--- ...d_parties_by_client_and_frame_location.sql | 2 +- .../usage_of_lite_youtube_embed.sql | 4 ++-- sql/2025/third-parties/usage_of_partytown.sql | 4 ++-- 59 files changed, 120 insertions(+), 115 deletions(-) diff --git a/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql b/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql index a31b1c9ea29..f3882c6ad08 100644 --- a/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql +++ b/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql @@ -6,7 +6,7 @@ WITH a11y_technologies AS ( _TABLE_SUFFIX AS client, url FROM - `httparchive.technologies.2025_07_01_*` + `httparchive.technologies.2025_06_01_*` WHERE category = 'Accessibility' ), @@ -17,7 +17,7 @@ pages AS ( url, rank_grouping FROM - `httparchive.summary_pages.2025_07_01_*`, + `httparchive.summary_pages.2025_06_01_*`, UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE rank <= rank_grouping @@ -29,7 +29,7 @@ rank_totals AS ( rank_grouping, COUNT(0) AS total FROM - `httparchive.summary_pages.2025_07_01_*`, + `httparchive.summary_pages.2025_06_01_*`, UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE rank <= rank_grouping diff --git a/sql/2025/third-parties/a11y_technology_usage.sql b/sql/2025/third-parties/a11y_technology_usage.sql index 3aea501592c..988478c30bb 100644 --- a/sql/2025/third-parties/a11y_technology_usage.sql +++ b/sql/2025/third-parties/a11y_technology_usage.sql @@ -6,7 +6,7 @@ WITH a11y_technologies AS ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS freq FROM - `httparchive.technologies.2025_07_01_*` + `httparchive.technologies.2025_06_01_*` WHERE category = 'Accessibility' GROUP BY @@ -18,7 +18,7 @@ pages AS ( _TABLE_SUFFIX AS client, COUNT(0) AS total FROM - `httparchive.summary_pages.2025_07_01_*` + `httparchive.summary_pages.2025_06_01_*` GROUP BY client ) diff --git a/sql/2025/third-parties/a11y_technology_usage_by_rank.sql b/sql/2025/third-parties/a11y_technology_usage_by_rank.sql index 992cb454217..cecc18fd76f 100644 --- a/sql/2025/third-parties/a11y_technology_usage_by_rank.sql +++ b/sql/2025/third-parties/a11y_technology_usage_by_rank.sql @@ -7,7 +7,7 @@ WITH a11y_technologies AS ( app, url FROM - `httparchive.technologies.2025_07_01_*` + `httparchive.technologies.2025_06_01_*` WHERE category = 'Accessibility' ), @@ -18,7 +18,7 @@ pages AS ( url, rank_grouping FROM - `httparchive.summary_pages.2025_07_01_*`, + `httparchive.summary_pages.2025_06_01_*`, UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE rank <= rank_grouping @@ -30,7 +30,7 @@ rank_totals AS ( rank_grouping, COUNT(0) AS total FROM - `httparchive.summary_pages.2025_07_01_*`, + `httparchive.summary_pages.2025_06_01_*`, UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE rank <= rank_grouping diff --git a/sql/2025/third-parties/compressed_images_by_3p.sql b/sql/2025/third-parties/compressed_images_by_3p.sql index db3af73c9a2..36f36b9e20a 100644 --- a/sql/2025/third-parties/compressed_images_by_3p.sql +++ b/sql/2025/third-parties/compressed_images_by_3p.sql @@ -10,7 +10,7 @@ WITH requests AS ( type, respBodySize AS size FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` WHERE type = 'image' AND ( resp_content_encoding = 'gzip' OR @@ -31,7 +31,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain diff --git a/sql/2025/third-parties/content_encoding.sql b/sql/2025/third-parties/content_encoding.sql index 31cdb2a691e..9da7ce700a1 100644 --- a/sql/2025/third-parties/content_encoding.sql +++ b/sql/2025/third-parties/content_encoding.sql @@ -8,7 +8,7 @@ WITH requests AS ( url, resp_content_encoding AS content_encoding FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), third_party AS ( @@ -21,7 +21,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain diff --git a/sql/2025/third-parties/content_encoding_by_content_type.sql b/sql/2025/third-parties/content_encoding_by_content_type.sql index 74dd000ea2a..2364ff65454 100644 --- a/sql/2025/third-parties/content_encoding_by_content_type.sql +++ b/sql/2025/third-parties/content_encoding_by_content_type.sql @@ -9,7 +9,7 @@ WITH requests AS ( resp_content_encoding AS content_encoding, type FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), third_party AS ( @@ -22,7 +22,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain diff --git a/sql/2025/third-parties/csp_allowed_host_frequency.sql b/sql/2025/third-parties/csp_allowed_host_frequency.sql index fbc11b365d1..3132a51e317 100644 --- a/sql/2025/third-parties/csp_allowed_host_frequency.sql +++ b/sql/2025/third-parties/csp_allowed_host_frequency.sql @@ -19,7 +19,7 @@ WITH totals AS ( FROM `httparchive.all.requests` WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND is_main_document GROUP BY client @@ -33,7 +33,7 @@ csp_data AS ( FROM `httparchive.all.requests` WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND is_main_document AND response_headers IS NOT NULL ), diff --git a/sql/2025/third-parties/depth_of_gtm_calls.sql b/sql/2025/third-parties/depth_of_gtm_calls.sql index 1fd8ab24c94..845267ab712 100644 --- a/sql/2025/third-parties/depth_of_gtm_calls.sql +++ b/sql/2025/third-parties/depth_of_gtm_calls.sql @@ -67,7 +67,7 @@ WITH data AS ( `httparchive.all.requests` WHERE NET.REG_DOMAIN(root_page) != NET.REG_DOMAIN(url) AND - date = '2025-07-01' + date = '2025-06-01' ) WHERE third_party != initiator_etld AND root_page != initiator_etld diff --git a/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql b/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql index 602b98836be..928ae266500 100644 --- a/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql +++ b/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql @@ -10,7 +10,7 @@ WITH requests AS ( status, respBodySize AS body_size FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), third_party AS ( @@ -24,7 +24,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql index 14bcbc043a1..a66b4cc4224 100644 --- a/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql +++ b/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql @@ -33,7 +33,7 @@ base AS ( NET.HOST(data.url) AS domain, data.wastedBytes AS potential_savings FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUnminifiedCssUrls(JSON_EXTRACT(report, "$.audits['unminified-css']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql index e7c6045c8c0..6bdfd03217e 100644 --- a/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql +++ b/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql @@ -34,7 +34,7 @@ base AS ( NET.HOST(data.url) AS domain, data.wastedBytes AS potential_savings FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUnminifiedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql index 194bdf93ca0..5d1b2c41854 100644 --- a/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql +++ b/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql @@ -34,7 +34,7 @@ base AS ( NET.HOST(data.url) AS domain, data.wastedBytes AS potential_savings FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUnusedCSSUrls(JSON_EXTRACT(report, "$.audits['unused-css-rules']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql index dfc7bb12e3a..ffbc9fb3f90 100644 --- a/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql +++ b/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql @@ -34,7 +34,7 @@ base AS ( NET.HOST(data.url) AS domain, data.wastedBytes AS potential_savings FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUnusedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unused-javascript']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql index 5bc6e31108d..4d09f451b32 100644 --- a/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql +++ b/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql @@ -33,7 +33,7 @@ base AS ( NET.HOST(data.url) AS domain, data.wastedBytes AS potential_savings FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUnminifiedImageUrls(JSON_EXTRACT(report, "$.audits['uses-optimized-images']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql b/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql index e1cc8e16741..e30e4c01b51 100644 --- a/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql +++ b/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql @@ -9,7 +9,7 @@ WITH requests AS ( respBodySize AS body_size, time FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), third_party AS ( @@ -23,7 +23,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql b/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql index b0959816ee1..9c6e0d37513 100644 --- a/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql +++ b/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql @@ -13,7 +13,7 @@ WITH document_frameid AS ( JSON_EXTRACT_SCALAR(payload, '$._frame_id') AS frame_id, is_main_document FROM `httparchive.all.requests` AS requests - WHERE requests.date = '2025-07-01' AND requests.is_root_page = true + WHERE requests.date = '2025-06-01' AND requests.is_root_page = true ), page_frames AS ( diff --git a/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql b/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql index 9ca4552c45b..fefcac1e569 100644 --- a/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql +++ b/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql @@ -7,7 +7,7 @@ WITH requests AS ( pageid AS page, url FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), third_party AS ( @@ -22,7 +22,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql index 1d91f2deae4..b0a094bafba 100644 --- a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql +++ b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql @@ -10,7 +10,7 @@ WITH requests AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-07-01' + date = '2025-06-01' ), @@ -26,7 +26,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2024-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql index fe0e9ed890c..503818667f3 100644 --- a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql +++ b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql @@ -7,7 +7,7 @@ WITH requests AS ( pageid AS page, url FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), third_party AS ( @@ -22,7 +22,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/iframe_allow_attribute.sql b/sql/2025/third-parties/iframe_allow_attribute.sql index d575d025cf8..745cd3ee255 100644 --- a/sql/2025/third-parties/iframe_allow_attribute.sql +++ b/sql/2025/third-parties/iframe_allow_attribute.sql @@ -21,7 +21,7 @@ FROM ( _TABLE_SUFFIX AS client, JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox') AS iframeAttrs FROM - `httparchive.pages.2025_07_01_*` + `httparchive.pages.2025_06_01_*` ), UNNEST(iframeAttrs) AS iframeAttr, UNNEST(REGEXP_EXTRACT_ALL(JSON_EXTRACT_SCALAR(iframeAttr, '$.allow'), r'(?i)([^,;]+)')) AS allow_attr @@ -30,7 +30,7 @@ JOIN ( _TABLE_SUFFIX AS client, SUM(getNumWithAllowAttribute(payload)) AS total_iframes_with_allow FROM - `httparchive.pages.2025_07_01_*` + `httparchive.pages.2025_06_01_*` GROUP BY client ) USING (client) diff --git a/sql/2025/third-parties/iframe_attribute_popular_hosts.sql b/sql/2025/third-parties/iframe_attribute_popular_hosts.sql index 3c03620c9ea..9a9de6cd71e 100644 --- a/sql/2025/third-parties/iframe_attribute_popular_hosts.sql +++ b/sql/2025/third-parties/iframe_attribute_popular_hosts.sql @@ -26,7 +26,7 @@ FROM ( _TABLE_SUFFIX AS client, JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox') AS iframeAttrs FROM - `httparchive.pages.2025_07_01_*` + `httparchive.pages.2025_06_01_*` ), UNNEST(iframeAttrs) AS iframeAttr, UNNEST(['allow', 'sandbox']) AS policy_type @@ -36,7 +36,7 @@ JOIN ( _TABLE_SUFFIX AS client, SUM(ARRAY_LENGTH(JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox'))) AS total_iframes FROM - `httparchive.pages.2025_07_01_*` + `httparchive.pages.2025_06_01_*` GROUP BY client ) diff --git a/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql b/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql index f2b38666ac2..de202c6180c 100644 --- a/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql @@ -27,14 +27,14 @@ FROM ( client, page, NET.HOST(unminified.url) IS NOT NULL AND NET.HOST(unminified.url) IN ( - SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2025-07-01' AND category != 'hosting' + SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2025-06-01' AND category != 'hosting' ) AS is_3p, unminified.wastedBytes AS wasted_bytes FROM `httparchive.all.pages` AS allpages CROSS JOIN UNNEST(getUnminifiedJsUrls(JSON_EXTRACT(allpages.lighthouse, "$.audits['unminified-css']"))) AS unminified - WHERE allpages.date = '2025-07-01' AND allpages.is_root_page = TRUE + WHERE allpages.date = '2025-06-01' AND allpages.is_root_page = TRUE ) GROUP BY client, diff --git a/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql b/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql index 1256a800f5d..cc1ef92efae 100644 --- a/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql @@ -27,11 +27,11 @@ FROM ( _TABLE_SUFFIX AS client, lighthouse.url AS page, NET.HOST(unminified.url) IS NOT NULL AND NET.HOST(unminified.url) IN ( - SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2025-07-01' AND category != 'hosting' + SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2025-06-01' AND category != 'hosting' ) AS is_3p, unminified.wastedBytes AS wasted_bytes FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUnminifiedJsUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS unminified ) GROUP BY diff --git a/sql/2025/third-parties/lighthouse_third_party_facades.sql b/sql/2025/third-parties/lighthouse_third_party_facades.sql index 20c97be4824..c3884dd3208 100644 --- a/sql/2025/third-parties/lighthouse_third_party_facades.sql +++ b/sql/2025/third-parties/lighthouse_third_party_facades.sql @@ -10,7 +10,7 @@ FROM ( SUM(COUNT(0)) OVER (PARTITION BY _TABLE_SUFFIX) AS total, COUNTIF(SAFE_CAST(JSON_VALUE(report, '$.audits.third-party-facades.score') AS FLOAT64) < 0.9) / SUM(COUNT(0)) OVER (PARTITION BY _TABLE_SUFFIX) AS pct FROM - `httparchive.lighthouse.2025_07_01_*` + `httparchive.lighthouse.2025_06_01_*` GROUP BY client ) diff --git a/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql b/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql index 8784dab8013..3e14aa6394c 100644 --- a/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql @@ -35,7 +35,7 @@ base AS ( data.wastedBytes AS potential_savings, data.totalBytes AS transfer_size FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUnminifiedCssUrls(JSON_EXTRACT(report, "$.audits['unminified-css']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql b/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql index 5422879602c..652b5ebc784 100644 --- a/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql @@ -35,7 +35,7 @@ base AS ( data.wastedBytes AS potential_savings, data.totalBytes AS transfer_size FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUnminifiedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql b/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql index 4fa02f8fd12..4398e6c290b 100644 --- a/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql +++ b/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql @@ -37,7 +37,7 @@ base AS ( data.wastedBytes AS potential_savings, data.totalBytes AS transfer_size FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUnminifiedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql b/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql index ff76f827089..3bd90aa259f 100644 --- a/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql @@ -35,7 +35,7 @@ base AS ( data.wastedBytes AS potential_savings, data.totalBytes AS transfer_size FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUnminifiedImageUrls(JSON_EXTRACT(report, "$.audits['uses-optimized-images']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql b/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql index 5bf622a4429..e31fe6af59e 100644 --- a/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql @@ -35,7 +35,7 @@ base AS ( data.wastedBytes AS potential_savings, data.totalBytes AS transfer_size FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUnusedCssUrls(JSON_EXTRACT(report, "$.audits['unused-css-rules']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql b/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql index 134afb835cf..fe45635652e 100644 --- a/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql @@ -35,7 +35,7 @@ base AS ( data.wastedBytes AS potential_savings, data.totalBytes AS transfer_size FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUnusedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unused-javascript']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank.sql b/sql/2025/third-parties/number_of_third_parties_by_rank.sql index 692e5efc7c6..a449765b346 100644 --- a/sql/2025/third-parties/number_of_third_parties_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_parties_by_rank.sql @@ -8,7 +8,7 @@ WITH requests AS ( FROM `httparchive.all.requests` AS req WHERE - req.date = '2025-07-01' AND + req.date = '2025-06-01' AND req.is_root_page = true ), @@ -20,7 +20,7 @@ pages AS ( FROM `httparchive.all.pages` AS pg WHERE - pg.date = '2025-07-01' AND + pg.date = '2025-06-01' AND pg.is_root_page = true ), @@ -35,7 +35,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql index a20d0185e84..be79bc7536f 100644 --- a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql +++ b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql @@ -7,7 +7,7 @@ WITH requests AS ( pageid AS page, url FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), pages AS ( @@ -16,7 +16,7 @@ pages AS ( pageid AS page, rank FROM - `httparchive.summary_pages.2025_07_01_*` + `httparchive.summary_pages.2025_06_01_*` ), third_party AS ( @@ -31,7 +31,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category NOT IN ('hosting') GROUP BY domain, diff --git a/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql b/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql index 4ab19893591..132271e46b3 100644 --- a/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql @@ -6,7 +6,7 @@ WITH requests AS ( pageid AS page, url FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), pages AS ( @@ -15,7 +15,7 @@ pages AS ( pageid AS page, rank FROM - `httparchive.summary_pages.2025_07_01_*` + `httparchive.summary_pages.2025_06_01_*` ), third_party AS ( @@ -30,7 +30,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql index 9f1db177d3e..e0119ad1d64 100644 --- a/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql +++ b/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql @@ -7,7 +7,7 @@ WITH requests AS ( pageid AS page, url FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), pages AS ( @@ -16,7 +16,7 @@ pages AS ( pageid AS page, rank FROM - `httparchive.summary_pages.2025_07_01_*` + `httparchive.summary_pages.2025_06_01_*` ), third_party AS ( @@ -31,7 +31,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category NOT IN ('hosting') GROUP BY domain, diff --git a/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql b/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql index 7a6b00d3c0d..e78af3902a5 100644 --- a/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql @@ -8,7 +8,7 @@ WITH requests AS ( FROM `httparchive.all.requests` AS req WHERE - req.date = '2025-07-01' AND + req.date = '2025-06-01' AND req.is_root_page = true ), @@ -20,7 +20,7 @@ pages AS ( FROM `httparchive.all.pages` AS pg WHERE - pg.date = '2025-07-01' AND + pg.date = '2025-06-01' AND pg.is_root_page = true ), diff --git a/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql b/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql index 3ac44225cb4..137c86864f5 100644 --- a/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql @@ -8,7 +8,7 @@ WITH requests AS ( FROM `httparchive.all.requests` AS req WHERE - req.date = '2025-07-01' AND + req.date = '2025-06-01' AND req.is_root_page = true ), @@ -20,7 +20,7 @@ pages AS ( FROM `httparchive.all.pages` AS pg WHERE - pg.date = '2025-07-01' AND + pg.date = '2025-06-01' AND pg.is_root_page = true ), diff --git a/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql b/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql index 57a8d0198b3..1dc05fe58c7 100644 --- a/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql +++ b/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql @@ -8,7 +8,7 @@ WITH requests AS ( url, type AS contentType FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), third_party AS ( @@ -22,7 +22,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql b/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql index 8a3b1f02ed3..6684f52575b 100644 --- a/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql +++ b/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql @@ -31,7 +31,7 @@ base AS ( NET.HOST(data.url) AS domain, lighthouse.url AS page FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['no-document-write']"))) AS data ) AS potential_third_parties INNER JOIN @@ -57,7 +57,7 @@ JOIN ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS total FROM - `httparchive.lighthouse.2025_07_01_*` + `httparchive.lighthouse.2025_06_01_*` GROUP BY _TABLE_SUFFIX ) diff --git a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql index 485075ae460..a1a821420eb 100644 --- a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql +++ b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql @@ -31,7 +31,7 @@ base AS ( NET.HOST(data.url) AS domain, lighthouse.url AS page FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['legacy-javascript']"))) AS data ) AS potential_third_parties INNER JOIN @@ -57,7 +57,7 @@ JOIN ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS total FROM - `httparchive.lighthouse.2025_07_01_*` + `httparchive.lighthouse.2025_06_01_*` GROUP BY _TABLE_SUFFIX ) diff --git a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql index 4c58e8bb7fa..6dc4152465c 100644 --- a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql +++ b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql @@ -33,7 +33,7 @@ base AS ( NET.HOST(data.url) AS domain, lighthouse.url AS page FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['legacy-javascript']"))) AS data ) AS potential_third_parties INNER JOIN @@ -70,7 +70,7 @@ FROM ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS total FROM - `httparchive.lighthouse.2025_07_01_*` + `httparchive.lighthouse.2025_06_01_*` GROUP BY _TABLE_SUFFIX ) diff --git a/sql/2025/third-parties/percent_of_third_party_cache.sql b/sql/2025/third-parties/percent_of_third_party_cache.sql index 1d4298da0fe..ba7375cf9cd 100644 --- a/sql/2025/third-parties/percent_of_third_party_cache.sql +++ b/sql/2025/third-parties/percent_of_third_party_cache.sql @@ -13,7 +13,7 @@ WITH requests AS ( url, pageid AS page FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), third_party AS ( @@ -27,7 +27,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql b/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql index 6bd022fac72..e0496e976cf 100644 --- a/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql +++ b/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql @@ -9,7 +9,7 @@ WITH requests AS ( type AS contentType, respBodySize AS body_size FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), third_party AS ( @@ -23,7 +23,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql b/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql index 724b05784c1..51e75c658b2 100644 --- a/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql +++ b/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql @@ -9,7 +9,7 @@ WITH requests AS ( RTRIM(urlShort, '/') AS origin, respOtherHeaders FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), third_party AS ( @@ -23,7 +23,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_websites_with_third_party.sql b/sql/2025/third-parties/percent_of_websites_with_third_party.sql index efd8328631a..9a7164016c6 100644 --- a/sql/2025/third-parties/percent_of_websites_with_third_party.sql +++ b/sql/2025/third-parties/percent_of_websites_with_third_party.sql @@ -8,7 +8,7 @@ WITH requests AS ( url, respBodySize FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), third_party AS ( @@ -23,7 +23,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql b/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql index 1d5add84a72..f859510694f 100644 --- a/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql +++ b/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql @@ -3,13 +3,16 @@ WITH requests AS ( SELECT - _TABLE_SUFFIX AS client, - pageid AS page, + client, + page, url FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.crawl.requests` + WHERE + date = '2025-06-01' ), + third_party AS ( SELECT domain, @@ -21,7 +24,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2024-06-01' AND category != 'hosting' GROUP BY domain, @@ -32,11 +35,13 @@ third_party AS ( pages AS ( SELECT - _TABLE_SUFFIX AS client, - pageid AS page, + client, + page, rank FROM - `httparchive.summary_pages.2025_07_01_*` + `httparchive.crawl.pages` + WHERE + date = '2025-06-01' ) SELECT diff --git a/sql/2025/third-parties/scripts_using_async_defer.sql b/sql/2025/third-parties/scripts_using_async_defer.sql index 74edf975d53..26c32fb8416 100644 --- a/sql/2025/third-parties/scripts_using_async_defer.sql +++ b/sql/2025/third-parties/scripts_using_async_defer.sql @@ -48,7 +48,7 @@ base AS ( data.isDefer AS isDefer, pages.url AS page FROM - `httparchive.pages.2025_07_01_*` AS pages, + `httparchive.pages.2025_06_01_*` AS pages, UNNEST(getScripts(JSON_EXTRACT_SCALAR(payload, '$._almanac'))) AS data ) AS potential_third_parties INNER JOIN diff --git a/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql b/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql index 2c9cccaea02..950bfdc7d27 100644 --- a/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql +++ b/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql @@ -48,7 +48,7 @@ base AS ( data.isDefer AS isDefer, pages.url AS page FROM - `httparchive.pages.2025_07_01_*` AS pages, + `httparchive.pages.2025_06_01_*` AS pages, UNNEST(getScripts(JSON_EXTRACT_SCALAR(payload, '$._almanac'))) AS data ) AS potential_third_parties INNER JOIN diff --git a/sql/2025/third-parties/tao_by_third_party.sql b/sql/2025/third-parties/tao_by_third_party.sql index 33066d84410..7cbef01455f 100644 --- a/sql/2025/third-parties/tao_by_third_party.sql +++ b/sql/2025/third-parties/tao_by_third_party.sql @@ -22,7 +22,7 @@ WITH requests AS ( RTRIM(urlShort, '/') AS origin, respOtherHeaders FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), pages AS ( @@ -32,7 +32,7 @@ pages AS ( pageid AS page, RTRIM(urlShort, '/') AS origin FROM - `httparchive.summary_pages.2025_07_01_*` + `httparchive.summary_pages.2025_06_01_*` ), third_party AS ( @@ -46,7 +46,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/third_parties_blocking_main_thread.sql b/sql/2025/third-parties/third_parties_blocking_main_thread.sql index 2ae6ac4112d..58dbc45ffa3 100644 --- a/sql/2025/third-parties/third_parties_blocking_main_thread.sql +++ b/sql/2025/third-parties/third_parties_blocking_main_thread.sql @@ -48,7 +48,7 @@ FROM ( url AS page, report FROM - `httparchive.lighthouse.2025_07_01_*` + `httparchive.lighthouse.2025_06_01_*` ), UNNEST(JSON_QUERY_ARRAY(report, '$.audits.third-party-summary.details.items')) AS third_party_items GROUP BY diff --git a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql index 78974be947c..81b1777c678 100644 --- a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql +++ b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql @@ -37,7 +37,7 @@ FROM ( url AS page, report FROM - `httparchive.lighthouse.2025_07_01_*` + `httparchive.lighthouse.2025_06_01_*` ), UNNEST(JSON_QUERY_ARRAY(report, '$.audits.third-party-summary.details.items')) AS third_party_items GROUP BY diff --git a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql index 74800807d4a..56905b2577d 100644 --- a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql +++ b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql @@ -43,7 +43,7 @@ FROM ( url AS page, report FROM - `httparchive.lighthouse.2025_07_01_*` + `httparchive.lighthouse.2025_06_01_*` ), UNNEST(JSON_QUERY_ARRAY(report, '$.audits.third-party-summary.details.items')) AS third_party_items GROUP BY diff --git a/sql/2025/third-parties/third_parties_blocking_rendering.sql b/sql/2025/third-parties/third_parties_blocking_rendering.sql index cac9e2832da..51c4628afbe 100644 --- a/sql/2025/third-parties/third_parties_blocking_rendering.sql +++ b/sql/2025/third-parties/third_parties_blocking_rendering.sql @@ -16,14 +16,14 @@ WITH total_third_party_usage AS ( category, COUNT(DISTINCT pages.url) AS total_pages FROM - `httparchive.summary_pages.2025_07_01_*` AS pages + `httparchive.summary_pages.2025_06_01_*` AS pages INNER JOIN ( SELECT _TABLE_SUFFIX AS client, pageid, url FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ) AS requests ON ( pages._TABLE_SUFFIX = requests.client AND @@ -33,7 +33,7 @@ WITH total_third_party_usage AS ( `httparchive.almanac.third_parties` ON NET.HOST(requests.url) = NET.HOST(domain) AND - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY client, @@ -83,7 +83,7 @@ FROM ( url AS page, report FROM - `httparchive.lighthouse.2025_07_01_*` + `httparchive.lighthouse.2025_06_01_*` ), UNNEST(JSON_QUERY_ARRAY(report, '$.audits.render-blocking-resources.details.items')) AS renderBlockingItems INNER JOIN diff --git a/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql b/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql index d6c70dd20d1..cdc0e1db3e9 100644 --- a/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql +++ b/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql @@ -16,14 +16,14 @@ WITH total_third_party_usage AS ( category, COUNT(DISTINCT pages.url) AS total_pages FROM - `httparchive.summary_pages.2025_07_01_*` AS pages + `httparchive.summary_pages.2025_06_01_*` AS pages INNER JOIN ( SELECT _TABLE_SUFFIX AS client, pageid, url FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ) AS requests ON ( pages._TABLE_SUFFIX = requests.client AND @@ -33,7 +33,7 @@ WITH total_third_party_usage AS ( `httparchive.almanac.third_parties` ON NET.HOST(requests.url) = NET.HOST(domain) AND - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY client, @@ -78,14 +78,14 @@ FROM ( url AS page, report FROM - `httparchive.lighthouse.2025_07_01_*` + `httparchive.lighthouse.2025_06_01_*` ), UNNEST(JSON_QUERY_ARRAY(report, '$.audits.render-blocking-resources.details.items')) AS render_blocking_items INNER JOIN `httparchive.almanac.third_parties` ON NET.HOST(JSON_VALUE(render_blocking_items, '$.url')) = domain AND - date = '2025-07-01' + date = '2025-06-01' GROUP BY client, canonicalDomain, diff --git a/sql/2025/third-parties/third_parties_using_legacy_javascript.sql b/sql/2025/third-parties/third_parties_using_legacy_javascript.sql index 453bd82ca51..7dbe24800c3 100644 --- a/sql/2025/third-parties/third_parties_using_legacy_javascript.sql +++ b/sql/2025/third-parties/third_parties_using_legacy_javascript.sql @@ -33,7 +33,7 @@ base AS ( NET.HOST(data.url) AS domain, lighthouse.url AS page FROM - `httparchive.lighthouse.2025_07_01_*` AS lighthouse, + `httparchive.lighthouse.2025_06_01_*` AS lighthouse, UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['legacy-javascript']"))) AS data ) AS potential_third_parties LEFT OUTER JOIN diff --git a/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql b/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql index c1c292d881b..053a4e809cc 100644 --- a/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql +++ b/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql @@ -9,7 +9,7 @@ WITH requests AS ( respBodySize AS body_size, time FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), third_party AS ( @@ -24,7 +24,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql b/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql index 8e7315e02af..e25a0474d2f 100644 --- a/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql +++ b/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql @@ -7,7 +7,7 @@ WITH requests AS ( pageid AS page, url FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` ), totals AS ( @@ -16,7 +16,7 @@ totals AS ( COUNT(DISTINCT pageid) AS total_pages, COUNT(0) AS total_requests FROM - `httparchive.summary_requests.2025_07_01_*` + `httparchive.summary_requests.2025_06_01_*` GROUP BY _TABLE_SUFFIX ), @@ -33,7 +33,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-07-01' AND + date = '2025-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql b/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql index 548aa20da24..8ee47c5f168 100644 --- a/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql +++ b/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql @@ -13,7 +13,7 @@ WITH document_frameid AS ( JSON_EXTRACT_SCALAR(payload, '$._frame_id') AS frame_id, is_main_document FROM `httparchive.all.requests` AS requests - WHERE requests.date = '2025-07-01' AND requests.is_root_page = true + WHERE requests.date = '2025-06-01' AND requests.is_root_page = true ), page_frames AS ( diff --git a/sql/2025/third-parties/usage_of_lite_youtube_embed.sql b/sql/2025/third-parties/usage_of_lite_youtube_embed.sql index eba5f08c535..fa70b5741b4 100644 --- a/sql/2025/third-parties/usage_of_lite_youtube_embed.sql +++ b/sql/2025/third-parties/usage_of_lite_youtube_embed.sql @@ -6,7 +6,7 @@ WITH totals AS ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS total_pages FROM - `httparchive.summary_pages.2025_07_01_*` + `httparchive.summary_pages.2025_06_01_*` GROUP BY client ), @@ -16,7 +16,7 @@ youtube_embed AS ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS youtube_embed_pages FROM - `httparchive.technologies.2025_07_01_*` + `httparchive.technologies.2025_06_01_*` WHERE app = 'lite-youtube-embed' GROUP BY diff --git a/sql/2025/third-parties/usage_of_partytown.sql b/sql/2025/third-parties/usage_of_partytown.sql index 4957d1ca847..251830b1870 100644 --- a/sql/2025/third-parties/usage_of_partytown.sql +++ b/sql/2025/third-parties/usage_of_partytown.sql @@ -6,7 +6,7 @@ WITH totals AS ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS total_pages FROM - `httparchive.summary_pages.2025_07_01_*` + `httparchive.summary_pages.2025_06_01_*` GROUP BY client ), @@ -16,7 +16,7 @@ partytown AS ( _TABLE_SUFFIX AS client, COUNT(DISTINCT url) AS partytown_pages FROM - `httparchive.technologies.2025_07_01_*` + `httparchive.technologies.2025_06_01_*` WHERE app = 'Partytown' GROUP BY From ee7b00e713fe59e6078aa81224a6d4167c98d190 Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Wed, 27 Aug 2025 18:30:18 -0700 Subject: [PATCH 05/37] Updated median number of third parties by rank query --- .../third-parties/number_of_third_parties_by_rank.sql | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank.sql b/sql/2025/third-parties/number_of_third_parties_by_rank.sql index a449765b346..9dd9b89b743 100644 --- a/sql/2025/third-parties/number_of_third_parties_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_parties_by_rank.sql @@ -1,15 +1,16 @@ #standardSQL # Number of third-parties per websites by rank + WITH requests AS ( SELECT client, page, url FROM - `httparchive.all.requests` AS req + `httparchive.crawl.requests` AS req WHERE - req.date = '2025-06-01' AND - req.is_root_page = true + date = '2025-06-01' AND + is_root_page = true ), pages AS ( @@ -18,7 +19,7 @@ pages AS ( page, rank FROM - `httparchive.all.pages` AS pg + `httparchive.crawl.pages` AS pg WHERE pg.date = '2025-06-01' AND pg.is_root_page = true From bde3438ff6dc19b4f6550fceda667500232b23ff Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Thu, 28 Aug 2025 16:38:30 -0700 Subject: [PATCH 06/37] Updated top 100 third parties by number of websites query --- ...00_third_parties_by_number_of_websites.sql | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql b/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql index e25a0474d2f..ab4aea2141d 100644 --- a/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql +++ b/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql @@ -3,22 +3,26 @@ WITH requests AS ( SELECT - _TABLE_SUFFIX AS client, - pageid AS page, + client, + page, url FROM - `httparchive.summary_requests.2025_06_01_*` + `httparchive.crawl.requests` + WHERE + date = '2025-06-01' ), totals AS ( SELECT - _TABLE_SUFFIX AS client, - COUNT(DISTINCT pageid) AS total_pages, + client, + COUNT(DISTINCT page) AS total_pages, COUNT(0) AS total_requests FROM - `httparchive.summary_requests.2025_06_01_*` + `httparchive.crawl.requests` + WHERE + date = '2025-06-01' GROUP BY - _TABLE_SUFFIX + client ), third_party AS ( @@ -33,7 +37,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2024-06-01' AND category != 'hosting' GROUP BY domain, From a0c17f782f2d2345e03b61c5e3cfaeefe43c839c Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Thu, 28 Aug 2025 16:39:35 -0700 Subject: [PATCH 07/37] Updated third party domains per page by rank --- ...rd_party_providers_by_rank_and_category.sql | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql index e0119ad1d64..7dbf1aa7960 100644 --- a/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql +++ b/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql @@ -3,20 +3,24 @@ WITH requests AS ( SELECT - _TABLE_SUFFIX AS client, - pageid AS page, + client, + page, url FROM - `httparchive.summary_requests.2025_06_01_*` + `httparchive.crawl.requests` + WHERE + date = '2025-06-01' ), pages AS ( SELECT - _TABLE_SUFFIX AS client, - pageid AS page, + client, + page, rank FROM - `httparchive.summary_pages.2025_06_01_*` + `httparchive.crawl.pages` + WHERE + date = '2025-06-01' ), third_party AS ( @@ -31,7 +35,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2024-06-01' AND category NOT IN ('hosting') GROUP BY domain, From 3c8519d8dfc1bb7a3f06f178a27eb538ad3bcb2d Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Thu, 28 Aug 2025 16:40:13 -0700 Subject: [PATCH 08/37] Updated percent of third parties by content type --- .../percent_of_third_parties_by_content_type.sql | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql b/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql index 1dc05fe58c7..386dfe5cda8 100644 --- a/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql +++ b/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql @@ -3,12 +3,14 @@ WITH requests AS ( SELECT - _TABLE_SUFFIX AS client, - pageid AS page, + client, + page, url, type AS contentType FROM - `httparchive.summary_requests.2025_06_01_*` + `httparchive.crawl.requests` + WHERE + date = '2025-06-01' ), third_party AS ( @@ -22,7 +24,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2024-06-01' AND category != 'hosting' GROUP BY domain, From 8976952baaff2fae514cebdd53a19cfc63d7cc96 Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Thu, 28 Aug 2025 16:43:01 -0700 Subject: [PATCH 09/37] Added prevalence of consent signals in third party requests --- ...nals_in_third_party_requests_optimized.sql | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql diff --git a/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql b/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql new file mode 100644 index 00000000000..17d1c265761 --- /dev/null +++ b/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql @@ -0,0 +1,193 @@ +#standardSQL +# Prevalence of specific consent signals (USP, TCF, GPP) in third-party requests + +WITH pages AS ( + SELECT + client, + page, + rank + FROM + `httparchive.crawl.pages` + WHERE + date = '2025-06-01' +), + +requests AS ( + SELECT + client, + page, + url + FROM + `httparchive.crawl.requests` + WHERE + date = '2025-06-01' +), + +third_party AS ( + SELECT + domain, + canonicalDomain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + canonicalDomain, + category + HAVING + page_usage >= 50 +), + +totals AS ( + SELECT + r.client, + rank_grouping, + COUNT(DISTINCT r.page) AS total_pages, + COUNT(0) AS total_requests + FROM + requests r + INNER JOIN + pages p + ON + r.client = p.client AND r.page = p.page, + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + WHERE + p.rank <= rank_grouping + GROUP BY + r.client, + rank_grouping +), + +third_party_requests AS ( + SELECT + r.client, + r.page, + r.url, + canonicalDomain, + category, + rank_grouping + FROM + requests r + INNER JOIN + pages p + ON + r.client = p.client AND r.page = p.page + INNER JOIN + third_party tp + ON + NET.HOST(r.url) = NET.HOST(tp.domain), + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + WHERE + p.rank <= rank_grouping +), + +-- Single-pass consent signal detection using one comprehensive regex +consent_signals AS ( + SELECT + client, + page, + url, + canonicalDomain, + category, + rank_grouping, + + -- Extract all consent parameters in one pass + REGEXP_EXTRACT_ALL(url, r'[?&](us_privacy|ccpa|usp_consent|uspString|sst\.us_privacy|uspConsent|ccpa_consent|AV_CCPA|usp|usprivacy|_fw_us_privacy|D9v\.us_privacy|cnsnt|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') AS found_params, + + -- Boolean flags derived from the extracted parameters (computed once) + REGEXP_CONTAINS(url, r'[?&]us_privacy=') AS has_usp_standard, + REGEXP_CONTAINS(url, r'[?&](ccpa|usp_consent|uspString|sst\.us_privacy|uspConsent|ccpa_consent|AV_CCPA|usp|usprivacy|_fw_us_privacy|D9v\.us_privacy|cnsnt|ccpaconsent|usp_string)=') AS has_usp_nonstandard, + REGEXP_CONTAINS(url, r'[?&](gdpr|gdpr_consent|gdpr_pd)=') AS has_tcf_standard, + REGEXP_CONTAINS(url, r'[?&](gpp|gpp_sid)=') AS has_gpp_standard + FROM + third_party_requests + WHERE + -- Pre-filter to reduce data processing + REGEXP_CONTAINS(url, r'[?&](us_privacy|ccpa|usp_consent|uspString|sst\.us_privacy|uspConsent|ccpa_consent|AV_CCPA|usp|usprivacy|_fw_us_privacy|D9v\.us_privacy|cnsnt|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') +), + +-- Add computed flag for any consent signal +signals_with_any AS ( + SELECT + *, + (has_usp_standard OR has_usp_nonstandard OR has_tcf_standard OR has_gpp_standard) AS has_any_consent_signal + FROM + consent_signals +), + +-- Create a single aggregation table to avoid repetitive calculations +signal_aggregates AS ( + SELECT + client, + rank_grouping, + -- USP Standard metrics + COUNTIF(has_usp_standard) AS usp_standard_requests, + COUNT(DISTINCT CASE WHEN has_usp_standard THEN page END) AS usp_standard_pages, + COUNT(DISTINCT CASE WHEN has_usp_standard THEN canonicalDomain END) AS usp_standard_domains, + + -- USP Non-Standard metrics + COUNTIF(has_usp_nonstandard) AS usp_nonstandard_requests, + COUNT(DISTINCT CASE WHEN has_usp_nonstandard THEN page END) AS usp_nonstandard_pages, + COUNT(DISTINCT CASE WHEN has_usp_nonstandard THEN canonicalDomain END) AS usp_nonstandard_domains, + + -- TCF Standard metrics + COUNTIF(has_tcf_standard) AS tcf_standard_requests, + COUNT(DISTINCT CASE WHEN has_tcf_standard THEN page END) AS tcf_standard_pages, + COUNT(DISTINCT CASE WHEN has_tcf_standard THEN canonicalDomain END) AS tcf_standard_domains, + + -- GPP Standard metrics + COUNTIF(has_gpp_standard) AS gpp_standard_requests, + COUNT(DISTINCT CASE WHEN has_gpp_standard THEN page END) AS gpp_standard_pages, + COUNT(DISTINCT CASE WHEN has_gpp_standard THEN canonicalDomain END) AS gpp_standard_domains, + + -- Any consent signal metrics + COUNTIF(has_any_consent_signal) AS any_consent_requests, + COUNT(DISTINCT CASE WHEN has_any_consent_signal THEN page END) AS any_consent_pages, + COUNT(DISTINCT CASE WHEN has_any_consent_signal THEN canonicalDomain END) AS any_consent_domains, + + -- Totals for this filtered dataset + COUNT(0) AS total_third_party_requests + FROM + signals_with_any + GROUP BY + client, + rank_grouping +) + +-- Final output using UNNEST to avoid repetitive UNION ALL +SELECT + agg.client, + agg.rank_grouping, + signal_data.signal_type, + signal_data.requests_with_signal, + agg.total_third_party_requests, + signal_data.requests_with_signal / agg.total_third_party_requests AS pct_requests_with_signal, + signal_data.pages_with_signal, + totals.total_pages, + signal_data.pages_with_signal / totals.total_pages AS pct_pages_with_signal, + signal_data.domains_with_signal +FROM + signal_aggregates agg +JOIN + totals +USING (client, rank_grouping) +CROSS JOIN + UNNEST([ + STRUCT('USP Standard' AS signal_type, usp_standard_requests AS requests_with_signal, usp_standard_pages AS pages_with_signal, usp_standard_domains AS domains_with_signal), + STRUCT('USP Non-Standard' AS signal_type, usp_nonstandard_requests AS requests_with_signal, usp_nonstandard_pages AS pages_with_signal, usp_nonstandard_domains AS domains_with_signal), + STRUCT('TCF Standard' AS signal_type, tcf_standard_requests AS requests_with_signal, tcf_standard_pages AS pages_with_signal, tcf_standard_domains AS domains_with_signal), + STRUCT('GPP Standard' AS signal_type, gpp_standard_requests AS requests_with_signal, gpp_standard_pages AS pages_with_signal, gpp_standard_domains AS domains_with_signal), + STRUCT('Any Consent Signal' AS signal_type, any_consent_requests AS requests_with_signal, any_consent_pages AS pages_with_signal, any_consent_domains AS domains_with_signal) + ]) AS signal_data + +ORDER BY + client, + rank_grouping, + signal_type From 9a9f90806d5723ee124eccd1b315591a4d499cdf Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Thu, 28 Aug 2025 16:43:31 -0700 Subject: [PATCH 10/37] Updated queries for 2025 --- .../consent_signal_basic_analysis.sql | 90 +++++++ ...nal_prevalence_by_third_party_category.sql | 195 +++++++++++++++ ...survival_rate_through_chains_optimized.sql | 214 +++++++++++++++++ ...signal_survival_rate_through_redirects.sql | 225 ++++++++++++++++++ ...vival_rate_through_redirects_optimized.sql | 225 ++++++++++++++++++ ...urvival_rate_through_redirects_working.sql | 168 +++++++++++++ ...nals_by_parameter_and_domain_optimized.sql | 201 ++++++++++++++++ sql/2025/third-parties/depth_of_gtm_calls.sql | 2 +- ...ribution_of_length_of_inclusion_chains.sql | 65 +++++ ...distribution_of_third_parties_by_frame.sql | 2 +- .../third-parties/iframe_allow_attribute.sql | 2 +- .../length_of_chain_by_intiator.sql | 72 ++++++ .../number_of_third_parties_by_rank.sql | 4 +- ..._third_party_requests_per_page_by_rank.sql | 13 +- 14 files changed, 1467 insertions(+), 11 deletions(-) create mode 100644 sql/2025/third-parties/consent_signal_basic_analysis.sql create mode 100644 sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql create mode 100644 sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql create mode 100644 sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql create mode 100644 sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql create mode 100644 sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql create mode 100644 sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql create mode 100644 sql/2025/third-parties/distribution_of_length_of_inclusion_chains.sql create mode 100644 sql/2025/third-parties/length_of_chain_by_intiator.sql diff --git a/sql/2025/third-parties/consent_signal_basic_analysis.sql b/sql/2025/third-parties/consent_signal_basic_analysis.sql new file mode 100644 index 00000000000..9ca789bcf11 --- /dev/null +++ b/sql/2025/third-parties/consent_signal_basic_analysis.sql @@ -0,0 +1,90 @@ +#standardSQL +# Basic consent signal analysis (simplified version to ensure data returns) + +WITH pages AS ( + SELECT + client, + page, + rank + FROM + `httparchive.crawl.pages` + WHERE + date = '2025-06-01' + AND rank <= 50000 -- Expand to top 50K sites +), + +-- Find requests with consent signals (no redirect filtering) +consent_requests AS ( + SELECT + r.client, + r.page, + r.url, + NET.REG_DOMAIN(r.page) AS page_domain, + NET.REG_DOMAIN(r.url) AS url_domain, + + -- Extract consent signals + REGEXP_CONTAINS(r.url, r'[?&]us_privacy=') AS has_usp_standard, + REGEXP_CONTAINS(r.url, r'[?&](ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string)=') AS has_usp_nonstandard, + REGEXP_CONTAINS(r.url, r'[?&](gdpr|gdpr_consent|gdpr_pd)=') AS has_tcf_standard, + REGEXP_CONTAINS(r.url, r'[?&](gpp|gpp_sid)=') AS has_gpp_standard, + + -- Check if request has redirects + JSON_EXTRACT(r.summary, '$.redirects') IS NOT NULL AND + TO_JSON_STRING(JSON_EXTRACT(r.summary, '$.redirects')) != '[]' AS has_redirects + FROM + `httparchive.crawl.requests` r + INNER JOIN + pages p + ON + r.client = p.client AND r.page = p.page + WHERE + r.date = '2025-06-01' + AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only + AND ( + REGEXP_CONTAINS(r.url, r'[?&]us_privacy=') OR + REGEXP_CONTAINS(r.url, r'[?&](ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string)=') OR + REGEXP_CONTAINS(r.url, r'[?&](gdpr|gdpr_consent|gdpr_pd)=') OR + REGEXP_CONTAINS(r.url, r'[?&](gpp|gpp_sid)=') + ) +), + +-- Add any consent signal flag +requests_with_signals AS ( + SELECT + *, + (has_usp_standard OR has_usp_nonstandard OR has_tcf_standard OR has_gpp_standard) AS has_any_signal + FROM + consent_requests +) + +-- Basic analysis +SELECT + client, + + -- Overall counts + COUNT(*) AS total_requests_with_consent_signals, + COUNT(DISTINCT page) AS total_pages_with_consent_signals, + COUNT(DISTINCT url_domain) AS total_domains_with_consent_signals, + + -- Signal type breakdown + COUNTIF(has_usp_standard) AS usp_standard_requests, + COUNTIF(has_usp_nonstandard) AS usp_nonstandard_requests, + COUNTIF(has_tcf_standard) AS tcf_standard_requests, + COUNTIF(has_gpp_standard) AS gpp_standard_requests, + + -- Percentage breakdown + COUNTIF(has_usp_standard) / COUNT(*) AS pct_usp_standard, + COUNTIF(has_usp_nonstandard) / COUNT(*) AS pct_usp_nonstandard, + COUNTIF(has_tcf_standard) / COUNT(*) AS pct_tcf_standard, + COUNTIF(has_gpp_standard) / COUNT(*) AS pct_gpp_standard, + + -- Redirect availability + COUNTIF(has_redirects) AS requests_with_redirects, + COUNTIF(has_redirects) / COUNT(*) AS pct_requests_with_redirects + +FROM + requests_with_signals +GROUP BY + client +ORDER BY + client diff --git a/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql b/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql new file mode 100644 index 00000000000..656507d7bce --- /dev/null +++ b/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql @@ -0,0 +1,195 @@ +#standardSQL +# Consent signal prevalence broken down by third-party category + +WITH pages AS ( + SELECT + client, + page, + rank + FROM + `httparchive.crawl.pages` + WHERE + date = '2025-06-01' +), + +requests AS ( + SELECT + client, + page, + url + FROM + `httparchive.crawl.requests` + WHERE + date = '2025-06-01' +), + +third_party AS ( + SELECT + domain, + canonicalDomain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + canonicalDomain, + category + HAVING + page_usage >= 50 +), + +-- Get total requests per category and rank grouping for percentage calculations +category_totals AS ( + SELECT + r.client, + rank_grouping, + tp.category, + COUNT(*) AS total_category_requests, + COUNT(DISTINCT r.page) AS total_category_pages, + COUNT(DISTINCT tp.canonicalDomain) AS total_category_domains + FROM + requests r + INNER JOIN + pages p + ON + r.client = p.client AND r.page = p.page + INNER JOIN + third_party tp + ON + NET.HOST(r.url) = NET.HOST(tp.domain), + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + WHERE + p.rank <= rank_grouping + GROUP BY + r.client, + rank_grouping, + tp.category +), + +-- Extract consent signals from third-party requests +consent_signals_by_category AS ( + SELECT + r.client, + rank_grouping, + tp.category, + tp.canonicalDomain, + r.page, + r.url, + + -- Single-pass consent signal detection + REGEXP_CONTAINS(r.url, r'[?&]us_privacy=') AS has_usp_standard, + REGEXP_CONTAINS(r.url, r'[?&](ccpa|usp_consent|uspString|sst\.us_privacy|uspConsent|ccpa_consent|AV_CCPA|usp|usprivacy|_fw_us_privacy|D9v\.us_privacy|cnsnt|ccpaconsent|usp_string)=') AS has_usp_nonstandard, + REGEXP_CONTAINS(r.url, r'[?&](gdpr|gdpr_consent|gdpr_pd)=') AS has_tcf_standard, + REGEXP_CONTAINS(r.url, r'[?&](gpp|gpp_sid)=') AS has_gpp_standard + + FROM + requests r + INNER JOIN + pages p + ON + r.client = p.client AND r.page = p.page + INNER JOIN + third_party tp + ON + NET.HOST(r.url) = NET.HOST(tp.domain), + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + WHERE + p.rank <= rank_grouping + -- Pre-filter: only process URLs that might contain consent-related parameters + AND REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|sst\.us_privacy|uspConsent|ccpa_consent|AV_CCPA|usp|usprivacy|_fw_us_privacy|D9v\.us_privacy|cnsnt|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') +), + +-- Add computed flag for any consent signal +signals_with_any AS ( + SELECT + *, + (has_usp_standard OR has_usp_nonstandard OR has_tcf_standard OR has_gpp_standard) AS has_any_consent_signal + FROM + consent_signals_by_category +), + +-- Aggregate consent signals by category +category_signal_aggregates AS ( + SELECT + client, + rank_grouping, + category, + + -- USP Standard metrics + COUNTIF(has_usp_standard) AS usp_standard_requests, + COUNT(DISTINCT CASE WHEN has_usp_standard THEN page END) AS usp_standard_pages, + COUNT(DISTINCT CASE WHEN has_usp_standard THEN canonicalDomain END) AS usp_standard_domains, + + -- USP Non-Standard metrics + COUNTIF(has_usp_nonstandard) AS usp_nonstandard_requests, + COUNT(DISTINCT CASE WHEN has_usp_nonstandard THEN page END) AS usp_nonstandard_pages, + COUNT(DISTINCT CASE WHEN has_usp_nonstandard THEN canonicalDomain END) AS usp_nonstandard_domains, + + -- TCF Standard metrics + COUNTIF(has_tcf_standard) AS tcf_standard_requests, + COUNT(DISTINCT CASE WHEN has_tcf_standard THEN page END) AS tcf_standard_pages, + COUNT(DISTINCT CASE WHEN has_tcf_standard THEN canonicalDomain END) AS tcf_standard_domains, + + -- GPP Standard metrics + COUNTIF(has_gpp_standard) AS gpp_standard_requests, + COUNT(DISTINCT CASE WHEN has_gpp_standard THEN page END) AS gpp_standard_pages, + COUNT(DISTINCT CASE WHEN has_gpp_standard THEN canonicalDomain END) AS gpp_standard_domains, + + -- Any consent signal metrics + COUNTIF(has_any_consent_signal) AS any_consent_requests, + COUNT(DISTINCT CASE WHEN has_any_consent_signal THEN page END) AS any_consent_pages, + COUNT(DISTINCT CASE WHEN has_any_consent_signal THEN canonicalDomain END) AS any_consent_domains, + + -- Totals for this filtered dataset + COUNT(0) AS total_filtered_requests + FROM + signals_with_any + GROUP BY + client, + rank_grouping, + category +) + +-- Final output using UNNEST to avoid repetitive UNION ALL +SELECT + agg.client, + agg.rank_grouping, + agg.category, + signal_data.signal_type, + signal_data.requests_with_signal, + totals.total_category_requests, + signal_data.requests_with_signal / totals.total_category_requests AS pct_requests_with_signal, + signal_data.pages_with_signal, + totals.total_category_pages, + signal_data.pages_with_signal / totals.total_category_pages AS pct_pages_with_signal, + signal_data.domains_with_signal, + totals.total_category_domains, + signal_data.domains_with_signal / totals.total_category_domains AS pct_domains_with_signal +FROM + category_signal_aggregates agg +JOIN + category_totals totals +USING (client, rank_grouping, category) +CROSS JOIN + UNNEST([ + STRUCT('USP Standard' AS signal_type, usp_standard_requests AS requests_with_signal, usp_standard_pages AS pages_with_signal, usp_standard_domains AS domains_with_signal), + STRUCT('USP Non-Standard' AS signal_type, usp_nonstandard_requests AS requests_with_signal, usp_nonstandard_pages AS pages_with_signal, usp_nonstandard_domains AS domains_with_signal), + STRUCT('TCF Standard' AS signal_type, tcf_standard_requests AS requests_with_signal, tcf_standard_pages AS pages_with_signal, tcf_standard_domains AS domains_with_signal), + STRUCT('GPP Standard' AS signal_type, gpp_standard_requests AS requests_with_signal, gpp_standard_pages AS pages_with_signal, gpp_standard_domains AS domains_with_signal), + STRUCT('Any Consent Signal' AS signal_type, any_consent_requests AS requests_with_signal, any_consent_pages AS pages_with_signal, any_consent_domains AS domains_with_signal) + ]) AS signal_data +WHERE + signal_data.requests_with_signal > 0 -- Only show categories with consent signals + +ORDER BY + client, + rank_grouping, + category, + signal_type diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql new file mode 100644 index 00000000000..69447ea5273 --- /dev/null +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql @@ -0,0 +1,214 @@ +#standardSQL +# Optimized: Consent signal survival rate through inclusion chains (memory-efficient) + +CREATE TEMP FUNCTION extractConsentSignals(url STRING) +RETURNS STRUCT< + has_usp_standard BOOL, + has_usp_nonstandard BOOL, + has_tcf_standard BOOL, + has_gpp_standard BOOL, + has_any_signal BOOL +> +LANGUAGE js AS """ + try { + const signals = { + has_usp_standard: /[?&]us_privacy=/.test(url), + has_usp_nonstandard: /[?&](ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string)=/.test(url), + has_tcf_standard: /[?&](gdpr|gdpr_consent|gdpr_pd)=/.test(url), + has_gpp_standard: /[?&](gpp|gpp_sid)=/.test(url) + }; + + signals.has_any_signal = signals.has_usp_standard || + signals.has_usp_nonstandard || + signals.has_tcf_standard || + signals.has_gpp_standard; + + return signals; + } catch (e) { + return { + has_usp_standard: false, + has_usp_nonstandard: false, + has_tcf_standard: false, + has_gpp_standard: false, + has_any_signal: false + }; + } +"""; + +WITH pages AS ( + SELECT + client, + page, + rank + FROM + `httparchive.crawl.pages` + WHERE + date = '2025-06-01' + AND rank <= 10000 -- Aggressive filtering: top 10K only +), + +-- Pre-filter to only requests with consent signals or initiator info +filtered_requests AS ( + SELECT + r.client, + r.page, + r.url, + NET.REG_DOMAIN(r.page) AS root_page, + NET.REG_DOMAIN(r.url) AS third_party, + NET.REG_DOMAIN(JSON_VALUE(r.payload, '$._initiator')) AS initiator_etld, + extractConsentSignals(r.url) AS consent_signals + FROM + `httparchive.crawl.requests` r + INNER JOIN + pages p + ON + r.client = p.client AND r.page = p.page + WHERE + r.date = '2025-06-01' + AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only + AND ( + -- Only process requests with consent signals OR that are part of chains + REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') + OR JSON_VALUE(r.payload, '$._initiator') IS NOT NULL + ) +), + +-- Simplified two-step chain analysis (avoid complex recursion) +step_1_requests AS ( + SELECT + client, + root_page, + third_party, + consent_signals, + COUNT(*) as step1_count + FROM + filtered_requests + WHERE + initiator_etld = root_page -- Direct first-party to third-party requests + AND consent_signals.has_any_signal = true + GROUP BY + client, + root_page, + third_party, + consent_signals +), + +step_2_requests AS ( + SELECT + fr.client, + s1.root_page, + s1.third_party AS step1_party, + fr.third_party AS step2_party, + s1.consent_signals AS step1_signals, + fr.consent_signals AS step2_signals, + COUNT(*) as step2_count + FROM + filtered_requests fr + INNER JOIN + step_1_requests s1 + ON + fr.client = s1.client + AND fr.root_page = s1.root_page + AND fr.initiator_etld = s1.third_party -- Third-party chain + GROUP BY + fr.client, + s1.root_page, + s1.third_party, + fr.third_party, + s1.consent_signals, + fr.consent_signals +), + +-- Calculate survival stats by step +step_1_stats AS ( + SELECT + client, + 1 AS step_number, + + COUNTIF(consent_signals.has_usp_standard) AS usp_standard_count, + COUNTIF(consent_signals.has_usp_nonstandard) AS usp_nonstandard_count, + COUNTIF(consent_signals.has_tcf_standard) AS tcf_standard_count, + COUNTIF(consent_signals.has_gpp_standard) AS gpp_standard_count, + COUNTIF(consent_signals.has_any_signal) AS any_signal_count, + + COUNT(*) AS total_requests, + COUNT(DISTINCT root_page) AS total_pages + FROM + step_1_requests + GROUP BY + client +), + +step_2_stats AS ( + SELECT + client, + 2 AS step_number, + + COUNTIF(step2_signals.has_usp_standard) AS usp_standard_count, + COUNTIF(step2_signals.has_usp_nonstandard) AS usp_nonstandard_count, + COUNTIF(step2_signals.has_tcf_standard) AS tcf_standard_count, + COUNTIF(step2_signals.has_gpp_standard) AS gpp_standard_count, + COUNTIF(step2_signals.has_any_signal) AS any_signal_count, + + COUNT(*) AS total_requests, + COUNT(DISTINCT root_page) AS total_pages + FROM + step_2_requests + GROUP BY + client +), + +-- Combine step statistics +combined_stats AS ( + SELECT * FROM step_1_stats + UNION ALL + SELECT * FROM step_2_stats +), + +-- Get baselines for survival rate calculation +baselines AS ( + SELECT + client, + usp_standard_count AS usp_standard_baseline, + usp_nonstandard_count AS usp_nonstandard_baseline, + tcf_standard_count AS tcf_standard_baseline, + gpp_standard_count AS gpp_standard_baseline, + any_signal_count AS any_signal_baseline + FROM + combined_stats + WHERE + step_number = 1 +) + +-- Final survival rate output (simplified) +SELECT + cs.client, + cs.step_number, + cs.total_requests, + cs.total_pages, + + -- Signal counts and survival rates + cs.usp_standard_count, + SAFE_DIVIDE(cs.usp_standard_count, b.usp_standard_baseline) AS usp_standard_survival_rate, + + cs.usp_nonstandard_count, + SAFE_DIVIDE(cs.usp_nonstandard_count, b.usp_nonstandard_baseline) AS usp_nonstandard_survival_rate, + + cs.tcf_standard_count, + SAFE_DIVIDE(cs.tcf_standard_count, b.tcf_standard_baseline) AS tcf_standard_survival_rate, + + cs.gpp_standard_count, + SAFE_DIVIDE(cs.gpp_standard_count, b.gpp_standard_baseline) AS gpp_standard_survival_rate, + + cs.any_signal_count, + SAFE_DIVIDE(cs.any_signal_count, b.any_signal_baseline) AS any_signal_survival_rate + +FROM + combined_stats cs +JOIN + baselines b +USING (client) + +ORDER BY + client, + step_number diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql new file mode 100644 index 00000000000..342341c0325 --- /dev/null +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql @@ -0,0 +1,225 @@ +#standardSQL +# Consent signal survival rate through HTTP redirects + +CREATE TEMP FUNCTION extractConsentSignals(url STRING) +RETURNS STRUCT< + has_usp_standard BOOL, + has_usp_nonstandard BOOL, + has_tcf_standard BOOL, + has_gpp_standard BOOL, + has_any_signal BOOL, + signal_count INT64 +> +LANGUAGE js AS """ + try { + const signals = { + has_usp_standard: /[?&]us_privacy=/.test(url), + has_usp_nonstandard: /[?&](ccpa|usp_consent|uspString|sst\\.us_privacy|uspConsent|ccpa_consent|AV_CCPA|usp|usprivacy|_fw_us_privacy|D9v\\.us_privacy|cnsnt|ccpaconsent|usp_string)=/.test(url), + has_tcf_standard: /[?&](gdpr|gdpr_consent|gdpr_pd)=/.test(url), + has_gpp_standard: /[?&](gpp|gpp_sid)=/.test(url) + }; + + signals.signal_count = [ + signals.has_usp_standard, + signals.has_usp_nonstandard, + signals.has_tcf_standard, + signals.has_gpp_standard + ].filter(Boolean).length; + + signals.has_any_signal = signals.signal_count > 0; + + return signals; + } catch (e) { + return { + has_usp_standard: false, + has_usp_nonstandard: false, + has_tcf_standard: false, + has_gpp_standard: false, + has_any_signal: false, + signal_count: 0 + }; + } +"""; + +WITH pages AS ( + SELECT + client, + page, + rank + FROM + `httparchive.crawl.pages` + WHERE + date = '2025-06-01' +), + +-- Get redirect chains from crawl.requests summary column +redirect_chains AS ( + SELECT + r.client, + r.page, + r.url AS final_url, + JSON_EXTRACT(r.summary, '$.redirects') AS redirects, + JSON_EXTRACT_SCALAR(r.summary, '$.startedDateTime') AS startedDateTime, + JSON_EXTRACT_SCALAR(r.summary, '$.endedDateTime') AS endedDateTime, + NET.REG_DOMAIN(r.url) AS final_domain + FROM + `httparchive.crawl.requests` r + INNER JOIN + pages p + ON + r.client = p.client AND r.page = p.page + WHERE + r.date = '2025-06-01' + AND JSON_EXTRACT(r.summary, '$.redirects') IS NOT NULL + AND JSON_EXTRACT(r.summary, '$.redirects') != '[]' + -- AND p.rank <= 100000 -- Limit to top 100K sites + AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only +), + +-- Parse redirect chains and extract consent signals at each step +parsed_redirects AS ( + SELECT + client, + page, + final_url, + final_domain, + redirect_step.url AS step_url, + redirect_step.redirectURL AS next_url, + ROW_NUMBER() OVER ( + PARTITION BY client, page, final_url + ORDER BY COALESCE(redirect_step.startedDateTime, redirect_chains.startedDateTime) + ) AS redirect_step_number, + extractConsentSignals(redirect_step.url) AS step_signals, + extractConsentSignals(COALESCE(redirect_step.redirectURL, final_url)) AS next_signals + FROM + redirect_chains, + UNNEST(JSON_EXTRACT_ARRAY(redirects)) AS redirect_step_json, + UNNEST([STRUCT( + JSON_EXTRACT_SCALAR(redirect_step_json, '$.url') AS url, + JSON_EXTRACT_SCALAR(redirect_step_json, '$.redirectURL') AS redirectURL, + JSON_EXTRACT_SCALAR(redirect_step_json, '$.startedDateTime') AS startedDateTime + )]) AS redirect_step + WHERE + redirect_step.url IS NOT NULL +), + +-- Add final URL as the last step +redirect_chains_with_final AS ( + -- Intermediate redirect steps + SELECT + client, + page, + final_url, + final_domain, + redirect_step_number, + step_url AS url_at_step, + step_signals AS signals_at_step, + 'redirect' AS step_type + FROM + parsed_redirects + + UNION ALL + + -- Final destination URL + SELECT + client, + page, + final_url, + final_domain, + MAX(redirect_step_number) + 1 AS redirect_step_number, + final_url AS url_at_step, + extractConsentSignals(final_url) AS signals_at_step, + 'final' AS step_type + FROM + parsed_redirects + GROUP BY + client, + page, + final_url, + final_domain +), + +-- Calculate signal preservation statistics by step +step_survival_stats AS ( + SELECT + client, + redirect_step_number, + step_type, + + -- Signals present at this step + COUNTIF(signals_at_step.has_usp_standard) AS usp_standard_at_step, + COUNTIF(signals_at_step.has_usp_nonstandard) AS usp_nonstandard_at_step, + COUNTIF(signals_at_step.has_tcf_standard) AS tcf_standard_at_step, + COUNTIF(signals_at_step.has_gpp_standard) AS gpp_standard_at_step, + COUNTIF(signals_at_step.has_any_signal) AS any_signal_at_step, + + -- Average signal count per URL + AVG(signals_at_step.signal_count) AS avg_signal_count_at_step, + + COUNT(*) AS total_urls_at_step, + COUNT(DISTINCT page) AS total_pages_at_step + FROM + redirect_chains_with_final + WHERE + redirect_step_number <= 6 -- Limit to first 6 steps (most redirects are short) + GROUP BY + client, + redirect_step_number, + step_type +), + +-- Get baseline (first step) for survival rate calculation +baseline_stats AS ( + SELECT + client, + usp_standard_at_step AS usp_standard_baseline, + usp_nonstandard_at_step AS usp_nonstandard_baseline, + tcf_standard_at_step AS tcf_standard_baseline, + gpp_standard_at_step AS gpp_standard_baseline, + any_signal_at_step AS any_signal_baseline, + avg_signal_count_at_step AS avg_signal_count_baseline + FROM + step_survival_stats + WHERE + redirect_step_number = 1 +) + +-- Final output with survival rates +SELECT + ss.client, + ss.redirect_step_number, + ss.step_type, + ss.total_urls_at_step, + ss.total_pages_at_step, + + -- Signal counts and survival rates + ss.usp_standard_at_step, + SAFE_DIVIDE(ss.usp_standard_at_step, bs.usp_standard_baseline) AS usp_standard_survival_rate, + + ss.usp_nonstandard_at_step, + SAFE_DIVIDE(ss.usp_nonstandard_at_step, bs.usp_nonstandard_baseline) AS usp_nonstandard_survival_rate, + + ss.tcf_standard_at_step, + SAFE_DIVIDE(ss.tcf_standard_at_step, bs.tcf_standard_baseline) AS tcf_standard_survival_rate, + + ss.gpp_standard_at_step, + SAFE_DIVIDE(ss.gpp_standard_at_step, bs.gpp_standard_baseline) AS gpp_standard_survival_rate, + + ss.any_signal_at_step, + SAFE_DIVIDE(ss.any_signal_at_step, bs.any_signal_baseline) AS any_signal_survival_rate, + + -- Average signal degradation + ss.avg_signal_count_at_step, + bs.avg_signal_count_baseline, + ss.avg_signal_count_at_step - bs.avg_signal_count_baseline AS signal_count_change, + SAFE_DIVIDE(ss.avg_signal_count_at_step, bs.avg_signal_count_baseline) AS signal_count_retention_rate + +FROM + step_survival_stats ss +JOIN + baseline_stats bs +USING (client) + +ORDER BY + client, + redirect_step_number diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql new file mode 100644 index 00000000000..304fe587ac3 --- /dev/null +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql @@ -0,0 +1,225 @@ +#standardSQL +# Optimized: Consent signal survival rate through HTTP redirects (memory-efficient) + +CREATE TEMP FUNCTION extractConsentSignals(url STRING) +RETURNS STRUCT< + has_usp_standard BOOL, + has_usp_nonstandard BOOL, + has_tcf_standard BOOL, + has_gpp_standard BOOL, + has_any_signal BOOL, + signal_count INT64 +> +LANGUAGE js AS """ + try { + if (!url || typeof url !== 'string') return { + has_usp_standard: false, has_usp_nonstandard: false, + has_tcf_standard: false, has_gpp_standard: false, + has_any_signal: false, signal_count: 0 + }; + + const signals = { + has_usp_standard: /[?&]us_privacy=/.test(url), + has_usp_nonstandard: /[?&](ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string)=/.test(url), + has_tcf_standard: /[?&](gdpr|gdpr_consent|gdpr_pd)=/.test(url), + has_gpp_standard: /[?&](gpp|gpp_sid)=/.test(url) + }; + + signals.signal_count = [ + signals.has_usp_standard, signals.has_usp_nonstandard, + signals.has_tcf_standard, signals.has_gpp_standard + ].filter(Boolean).length; + + signals.has_any_signal = signals.signal_count > 0; + return signals; + } catch (e) { + return { + has_usp_standard: false, has_usp_nonstandard: false, + has_tcf_standard: false, has_gpp_standard: false, + has_any_signal: false, signal_count: 0 + }; + } +"""; + +WITH pages AS ( + SELECT + client, + page, + rank + FROM + `httparchive.crawl.pages` + WHERE + date = '2025-06-01' + AND rank <= 100000 -- Expanded to top 100K sites +), + +-- Pre-filter requests with redirects and potential consent signals +requests_with_redirects AS ( + SELECT + r.client, + r.page, + r.url AS final_url, + JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') AS redirect_url, + NET.REG_DOMAIN(r.url) AS final_domain + FROM + `httparchive.crawl.requests` r + INNER JOIN + pages p + ON + r.client = p.client AND r.page = p.page + WHERE + r.date = '2025-06-01' + AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only + AND JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') IS NOT NULL + AND JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') != '' + AND ( + -- Pre-filter: only URLs with consent signals in final URL or redirect URL + REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') + OR REGEXP_CONTAINS(JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl'), r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') + ) +), + +-- Simplified redirect parsing - 2 step analysis +redirect_steps AS ( + SELECT + client, + page, + final_url, + final_domain, + + -- Step 1: Original redirect URL (before redirect) + redirect_url AS step1_url, + + -- Step 2: Final URL (after redirect) + final_url AS step2_url + FROM + requests_with_redirects + WHERE + redirect_url IS NOT NULL + AND redirect_url != '' +), + +-- Extract consent signals for each step +signals_by_step AS ( + SELECT + client, + page, + final_domain, + + -- Step 1 signals (original redirect URL) + step1_url, + extractConsentSignals(step1_url) AS step1_signals, + + -- Step 2 signals (final URL after redirect) + step2_url, + extractConsentSignals(step2_url) AS step2_signals + FROM + redirect_steps + WHERE + step1_url IS NOT NULL +), + +-- Calculate step-wise aggregations (memory efficient) +step_aggregations AS ( + -- Step 1 stats (original redirect URL) + SELECT + client, + 1 AS redirect_step, + 'original' AS step_type, + + COUNTIF(step1_signals.has_usp_standard) AS usp_standard_count, + COUNTIF(step1_signals.has_usp_nonstandard) AS usp_nonstandard_count, + COUNTIF(step1_signals.has_tcf_standard) AS tcf_standard_count, + COUNTIF(step1_signals.has_gpp_standard) AS gpp_standard_count, + COUNTIF(step1_signals.has_any_signal) AS any_signal_count, + + AVG(step1_signals.signal_count) AS avg_signal_count, + COUNT(*) AS total_urls, + COUNT(DISTINCT page) AS total_pages + FROM + signals_by_step + WHERE + step1_signals.has_any_signal = true -- Only analyze chains that start with signals + GROUP BY + client + + UNION ALL + + -- Step 2 stats (final URL after redirect) + SELECT + client, + 2 AS redirect_step, + 'final' AS step_type, + + COUNTIF(step2_signals.has_usp_standard) AS usp_standard_count, + COUNTIF(step2_signals.has_usp_nonstandard) AS usp_nonstandard_count, + COUNTIF(step2_signals.has_tcf_standard) AS tcf_standard_count, + COUNTIF(step2_signals.has_gpp_standard) AS gpp_standard_count, + COUNTIF(step2_signals.has_any_signal) AS any_signal_count, + + AVG(step2_signals.signal_count) AS avg_signal_count, + COUNT(*) AS total_urls, + COUNT(DISTINCT page) AS total_pages + FROM + signals_by_step + WHERE + step1_signals.has_any_signal = true -- Same baseline + GROUP BY + client +), + +-- Calculate baselines (step 1) +baselines AS ( + SELECT + client, + usp_standard_count AS usp_standard_baseline, + usp_nonstandard_count AS usp_nonstandard_baseline, + tcf_standard_count AS tcf_standard_baseline, + gpp_standard_count AS gpp_standard_baseline, + any_signal_count AS any_signal_baseline, + avg_signal_count AS avg_signal_count_baseline + FROM + step_aggregations + WHERE + redirect_step = 1 +) + +-- Final output with survival rates +SELECT + sa.client, + sa.redirect_step, + sa.step_type, + sa.total_urls, + sa.total_pages, + + -- Signal survival rates + sa.usp_standard_count, + SAFE_DIVIDE(sa.usp_standard_count, b.usp_standard_baseline) AS usp_standard_survival_rate, + + sa.usp_nonstandard_count, + SAFE_DIVIDE(sa.usp_nonstandard_count, b.usp_nonstandard_baseline) AS usp_nonstandard_survival_rate, + + sa.tcf_standard_count, + SAFE_DIVIDE(sa.tcf_standard_count, b.tcf_standard_baseline) AS tcf_standard_survival_rate, + + sa.gpp_standard_count, + SAFE_DIVIDE(sa.gpp_standard_count, b.gpp_standard_baseline) AS gpp_standard_survival_rate, + + sa.any_signal_count, + SAFE_DIVIDE(sa.any_signal_count, b.any_signal_baseline) AS any_signal_survival_rate, + + -- Signal count preservation + sa.avg_signal_count, + b.avg_signal_count_baseline, + sa.avg_signal_count - b.avg_signal_count_baseline AS signal_count_change, + SAFE_DIVIDE(sa.avg_signal_count, b.avg_signal_count_baseline) AS signal_count_retention_rate + +FROM + step_aggregations sa +JOIN + baselines b +USING (client) + +ORDER BY + client, + redirect_step diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql new file mode 100644 index 00000000000..7b810104590 --- /dev/null +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql @@ -0,0 +1,168 @@ +#standardSQL +# Working version: Consent signal survival rate through HTTP redirects + +CREATE TEMP FUNCTION extractConsentSignals(url STRING) +RETURNS STRUCT< + has_usp_standard BOOL, + has_usp_nonstandard BOOL, + has_tcf_standard BOOL, + has_gpp_standard BOOL, + has_any_signal BOOL, + signal_count INT64 +> +LANGUAGE js AS """ + try { + if (!url || typeof url !== 'string') return { + has_usp_standard: false, has_usp_nonstandard: false, + has_tcf_standard: false, has_gpp_standard: false, + has_any_signal: false, signal_count: 0 + }; + + const signals = { + has_usp_standard: /[?&]us_privacy=/.test(url), + has_usp_nonstandard: /[?&](ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string)=/.test(url), + has_tcf_standard: /[?&](gdpr|gdpr_consent|gdpr_pd)=/.test(url), + has_gpp_standard: /[?&](gpp|gpp_sid)=/.test(url) + }; + + signals.signal_count = [ + signals.has_usp_standard, signals.has_usp_nonstandard, + signals.has_tcf_standard, signals.has_gpp_standard + ].filter(Boolean).length; + + signals.has_any_signal = signals.signal_count > 0; + return signals; + } catch (e) { + return { + has_usp_standard: false, has_usp_nonstandard: false, + has_tcf_standard: false, has_gpp_standard: false, + has_any_signal: false, signal_count: 0 + }; + } +"""; + +WITH pages AS ( + SELECT + client, + page, + rank + FROM + `httparchive.crawl.pages` + WHERE + date = '2025-06-01' + AND rank <= 100000 -- Expanded to top 100K sites +), + +-- First, find all third-party requests with consent signals (regardless of redirects) +initial_consent_requests AS ( + SELECT + r.client, + r.page, + r.url, + extractConsentSignals(r.url) AS url_signals + FROM + `httparchive.crawl.requests` r + INNER JOIN + pages p + ON + r.client = p.client AND r.page = p.page + WHERE + r.date = '2025-06-01' + AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only + AND REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') +), + +-- Now look for those same requests that ALSO have redirect chains +requests_with_redirects AS ( + SELECT + icr.client, + icr.page, + icr.url, + icr.url_signals, + r.summary, + JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') AS redirect_url + FROM + initial_consent_requests icr + INNER JOIN + `httparchive.crawl.requests` r + ON + icr.client = r.client + AND icr.page = r.page + AND icr.url = r.url + WHERE + r.date = '2025-06-01' + AND JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') IS NOT NULL + AND JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') != '' +), + +-- Parse redirect chains (simplified - just look at redirect URL and final URL) +parsed_redirects AS ( + SELECT + client, + page, + url AS final_url, + url_signals AS final_signals, + + -- Extract redirect URL + redirect_url AS redirect_url, + + -- Simple redirect count (1 = single redirect) + CASE WHEN redirect_url IS NOT NULL AND redirect_url != '' THEN 1 ELSE 0 END AS redirect_count + FROM + requests_with_redirects + WHERE + redirect_url IS NOT NULL + AND redirect_url != '' +), + +-- Extract signals from redirect steps +redirect_analysis AS ( + SELECT + client, + page, + final_url, + final_signals, + redirect_url, + redirect_count, + extractConsentSignals(redirect_url) AS redirect_signals + FROM + parsed_redirects + WHERE + redirect_url IS NOT NULL +) + +-- Final analysis comparing signals across redirect steps +SELECT + client, + + -- Overall statistics + COUNT(*) AS total_redirect_chains_with_consent, + COUNT(DISTINCT page) AS pages_with_redirect_chains, + AVG(redirect_count) AS avg_redirect_count, + + -- Step 1 (redirect URL) signal analysis + COUNTIF(redirect_signals.has_any_signal) AS step1_requests_with_signals, + COUNTIF(redirect_signals.has_usp_standard) AS step1_usp_standard, + COUNTIF(redirect_signals.has_usp_nonstandard) AS step1_usp_nonstandard, + COUNTIF(redirect_signals.has_tcf_standard) AS step1_tcf_standard, + COUNTIF(redirect_signals.has_gpp_standard) AS step1_gpp_standard, + + -- Final URL signal analysis + COUNTIF(final_signals.has_any_signal) AS final_requests_with_signals, + COUNTIF(final_signals.has_usp_standard) AS final_usp_standard, + COUNTIF(final_signals.has_usp_nonstandard) AS final_usp_nonstandard, + COUNTIF(final_signals.has_tcf_standard) AS final_tcf_standard, + COUNTIF(final_signals.has_gpp_standard) AS final_gpp_standard, + + -- Survival rates (what percentage of signals make it to final URL) + SAFE_DIVIDE(COUNTIF(final_signals.has_any_signal), COUNT(*)) AS overall_signal_survival_rate, + SAFE_DIVIDE(COUNTIF(final_signals.has_usp_standard), COUNTIF(redirect_signals.has_usp_standard)) AS usp_standard_survival_rate, + SAFE_DIVIDE(COUNTIF(final_signals.has_tcf_standard), COUNTIF(redirect_signals.has_tcf_standard)) AS tcf_standard_survival_rate, + SAFE_DIVIDE(COUNTIF(final_signals.has_gpp_standard), COUNTIF(redirect_signals.has_gpp_standard)) AS gpp_standard_survival_rate + +FROM + redirect_analysis +GROUP BY + client +ORDER BY + client diff --git a/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql b/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql new file mode 100644 index 00000000000..ce615af26ca --- /dev/null +++ b/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql @@ -0,0 +1,201 @@ +#standardSQL +# Optimized: Detailed breakdown of consent signals by individual parameters and top domains + +WITH pages AS ( + SELECT + client, + page, + rank + FROM + `httparchive.crawl.pages` + WHERE + date = '2025-06-01' +), + +requests AS ( + SELECT + client, + page, + url + FROM + `httparchive.crawl.requests` + WHERE + date = '2025-06-01' + -- Pre-filter: only process URLs that contain consent-related parameters + AND REGEXP_CONTAINS(url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') +), + +third_party AS ( + SELECT + domain, + canonicalDomain, + category, + COUNT(DISTINCT page) AS page_usage + FROM + `httparchive.almanac.third_parties` tp + JOIN + requests r + ON NET.HOST(r.url) = NET.HOST(tp.domain) + WHERE + date = '2024-06-01' AND + category != 'hosting' + GROUP BY + domain, + canonicalDomain, + category + HAVING + page_usage >= 50 +), + +-- Single-pass parameter extraction using one comprehensive regex +parameter_extraction AS ( + SELECT + r.client, + canonicalDomain, + category, + rank_grouping, + -- Extract all relevant parameters in one pass using REGEXP_EXTRACT_ALL + REGEXP_EXTRACT_ALL(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') AS found_parameters + FROM + requests r + INNER JOIN + pages p + ON + r.client = p.client AND r.page = p.page + INNER JOIN + third_party tp + ON + NET.HOST(r.url) = NET.HOST(tp.domain), + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + WHERE + p.rank <= rank_grouping +), + +-- Flatten parameters and count occurrences +flattened_parameters AS ( + SELECT + client, + canonicalDomain, + category, + rank_grouping, + param + FROM + parameter_extraction, + UNNEST(found_parameters) AS param +), + +-- Aggregate parameter counts +parameter_counts AS ( + SELECT + client, + canonicalDomain, + category, + rank_grouping, + param, + COUNT(*) AS param_count, + COUNT(DISTINCT CONCAT(client, canonicalDomain)) AS domain_count + FROM + flattened_parameters + GROUP BY + client, + canonicalDomain, + category, + rank_grouping, + param +), + +-- Get total request counts for percentage calculations (from ALL third-party requests, not pre-filtered) +totals AS ( + SELECT + r.client, + rank_grouping, + COUNT(*) AS total_all_requests + FROM + `httparchive.crawl.requests` r + INNER JOIN + pages p + ON + r.client = p.client AND r.page = p.page + INNER JOIN + third_party tp + ON + NET.HOST(r.url) = NET.HOST(tp.domain), + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + WHERE + r.date = '2025-06-01' + AND p.rank <= rank_grouping + GROUP BY + r.client, + rank_grouping +), + +-- Categorize parameters +categorized_params AS ( + SELECT + client, + rank_grouping, + param, + CASE + WHEN param = 'us_privacy' THEN 'USP Standard' + WHEN param IN ('ccpa', 'usp_consent', 'uspString', 'uspConsent', 'ccpa_consent', 'usp', 'usprivacy', 'ccpaconsent', 'usp_string') THEN 'USP Non-Standard' + WHEN param IN ('gdpr', 'gdpr_consent', 'gdpr_pd') THEN 'TCF Standard' + WHEN param IN ('gpp', 'gpp_sid') THEN 'GPP Standard' + END AS signal_category, + SUM(param_count) AS total_requests, + COUNT(DISTINCT canonicalDomain) AS domains_using + FROM + parameter_counts + GROUP BY + client, + rank_grouping, + param, + signal_category +) + +-- Parameter frequency analysis +SELECT + 'Parameter Frequency' AS analysis_type, + client, + rank_grouping, + param AS parameter_name, + signal_category, + total_requests, + domains_using, + total_requests / totals.total_all_requests AS pct_of_all_requests +FROM + categorized_params +JOIN + totals +USING (client, rank_grouping) + +UNION ALL + +-- Top domains analysis (simplified) +SELECT + 'Top Domains' AS analysis_type, + client, + rank_grouping, + canonicalDomain AS parameter_name, + category AS signal_category, + SUM(param_count) AS total_requests, + COUNT(DISTINCT param) AS domains_using, + SUM(param_count) / MAX(totals.total_all_requests) AS pct_of_all_requests +FROM + parameter_counts +JOIN + totals +USING (client, rank_grouping) +GROUP BY + client, + rank_grouping, + canonicalDomain, + category +HAVING + SUM(param_count) > 0 + +ORDER BY + analysis_type, + client, + rank_grouping, + total_requests DESC +LIMIT 1000 \ No newline at end of file diff --git a/sql/2025/third-parties/depth_of_gtm_calls.sql b/sql/2025/third-parties/depth_of_gtm_calls.sql index 845267ab712..75404eb2dde 100644 --- a/sql/2025/third-parties/depth_of_gtm_calls.sql +++ b/sql/2025/third-parties/depth_of_gtm_calls.sql @@ -64,7 +64,7 @@ WITH data AS ( NET.REG_DOMAIN(url) AS third_party, NET.REG_DOMAIN(JSON_VALUE(payload, '$._initiator')) AS initiator_etld FROM - `httparchive.all.requests` + `httparchive.crawl.requests` WHERE NET.REG_DOMAIN(root_page) != NET.REG_DOMAIN(url) AND date = '2025-06-01' diff --git a/sql/2025/third-parties/distribution_of_length_of_inclusion_chains.sql b/sql/2025/third-parties/distribution_of_length_of_inclusion_chains.sql new file mode 100644 index 00000000000..ace78271aad --- /dev/null +++ b/sql/2025/third-parties/distribution_of_length_of_inclusion_chains.sql @@ -0,0 +1,65 @@ +CREATE TEMP FUNCTION findAllInitiators(rootPage STRING, data ARRAY>) +RETURNS ARRAY +LANGUAGE js AS """ + // Helper function to find all initiator_etlds for a given root_page + function findInitiators(page, visited, data) { + // Find all entries where the root_page matches and the initiator_etld hasn't been visited + const initiators = data + .filter(row => row.root_page === page && !visited.includes(row.initiator_etld)) + .map(row => row.initiator_etld); + + // Add the newly found initiators to the visited list + visited = visited.concat(initiators); + + // Recursively process all new initiators + initiators.forEach(initiator => { + visited = findInitiators(initiator, visited, data); + }); + + return visited; + } + + // Main call: Start recursion from the rootPage + // Use a Set to ensure that all returned values are distinct + return Array.from(new Set(findInitiators(rootPage, [], data))); +"""; + +WITH data AS ( + -- TP interact with other tps - only extract necessary fields + SELECT + client, + root_page, + third_party, + initiator_etld + FROM ( + SELECT + client, + NET.REG_DOMAIN(root_page) AS root_page, + NET.REG_DOMAIN(url) AS third_party, + NET.REG_DOMAIN(JSON_VALUE(payload, '$._initiator')) AS initiator_etld + FROM + `httparchive.crawl.requests` + WHERE + date = '2025-06-01' AND + NET.REG_DOMAIN(root_page) != NET.REG_DOMAIN(url) + ) + WHERE third_party != initiator_etld AND + root_page != initiator_etld + GROUP BY client, root_page, third_party, initiator_etld +) + +SELECT + client, + ARRAY_LENGTH(all_initiators) AS chain_length, + COUNT(*) AS pages_with_this_length +FROM ( + SELECT + root_page, + client, + findAllInitiators(root_page, ARRAY_AGG(STRUCT(root_page, third_party, initiator_etld))) AS all_initiators + FROM data + GROUP BY root_page, client +) +WHERE ARRAY_LENGTH(all_initiators) > 0 +GROUP BY client, chain_length +ORDER BY client, chain_length; \ No newline at end of file diff --git a/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql b/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql index 9c6e0d37513..690e18dd4d1 100644 --- a/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql +++ b/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql @@ -12,7 +12,7 @@ WITH document_frameid AS ( END AS mainframe_id, JSON_EXTRACT_SCALAR(payload, '$._frame_id') AS frame_id, is_main_document - FROM `httparchive.all.requests` AS requests + FROM `httparchive.crawl.requests` AS requests WHERE requests.date = '2025-06-01' AND requests.is_root_page = true ), diff --git a/sql/2025/third-parties/iframe_allow_attribute.sql b/sql/2025/third-parties/iframe_allow_attribute.sql index 745cd3ee255..0d9e2aa47b9 100644 --- a/sql/2025/third-parties/iframe_allow_attribute.sql +++ b/sql/2025/third-parties/iframe_allow_attribute.sql @@ -1,4 +1,4 @@ -#standardSQL +# standardSQL # usage of different directives for allow attribute on iframes CREATE TEMP FUNCTION getNumWithAllowAttribute(payload STRING) AS (( diff --git a/sql/2025/third-parties/length_of_chain_by_intiator.sql b/sql/2025/third-parties/length_of_chain_by_intiator.sql new file mode 100644 index 00000000000..fa36b7b0224 --- /dev/null +++ b/sql/2025/third-parties/length_of_chain_by_intiator.sql @@ -0,0 +1,72 @@ +CREATE TEMP FUNCTION findAllInitiators(rootPage STRING, data ARRAY>) +RETURNS ARRAY +LANGUAGE js AS """ + // Helper function to find all initiator_etlds for a given root_page + function findInitiators(page, visited, data) { + // Find all entries where the root_page matches and the initiator_etld hasn't been visited + const initiators = data + .filter(row => row.root_page === page && !visited.includes(row.initiator_etld)) + .map(row => row.initiator_etld); + + // Add the newly found initiators to the visited list + visited = visited.concat(initiators); + + // Recursively process all new initiators + initiators.forEach(initiator => { + visited = findInitiators(initiator, visited, data); + }); + + return visited; + } + + // Main call: Start recursion from the rootPage + // Use a Set to ensure that all returned values are distinct + return Array.from(new Set(findInitiators(rootPage, [], data))); +"""; + +WITH data AS ( + -- TP interact with other tps + SELECT + * + FROM ( + SELECT + client, + NET.REG_DOMAIN(root_page) AS root_page, + NET.REG_DOMAIN(url) AS third_party, + NET.REG_DOMAIN(JSON_VALUE(payload, '$._initiator')) AS initiator_etld + FROM + `httparchive.crawl.requests` + WHERE + NET.REG_DOMAIN(root_page) != NET.REG_DOMAIN(url) AND + date = '2025-06-01' + ) + WHERE third_party != initiator_etld AND + root_page != initiator_etld + GROUP BY client, root_page, third_party, initiator_etld +) + +-- Add this to the final SELECT to see top initiators by chain length +SELECT + client, + first_initiator, + AVG(ARRAY_LENGTH(all_initiators)) AS avg_chain_length, + MAX(ARRAY_LENGTH(all_initiators)) AS max_chain_length, + COUNT(*) AS pages +FROM ( + SELECT + root_page, + client, + all_initiators, + all_initiators[OFFSET(0)] AS first_initiator -- First third-party in chain + FROM ( + SELECT + root_page, + client, + findAllInitiators(root_page, ARRAY_AGG(STRUCT(root_page, third_party, initiator_etld))) AS all_initiators + FROM data + GROUP BY root_page, client + ) + WHERE ARRAY_LENGTH(all_initiators) > 0 +) +GROUP BY client, first_initiator +ORDER BY avg_chain_length DESC; \ No newline at end of file diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank.sql b/sql/2025/third-parties/number_of_third_parties_by_rank.sql index 9dd9b89b743..110d9fa27a4 100644 --- a/sql/2025/third-parties/number_of_third_parties_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_parties_by_rank.sql @@ -7,7 +7,7 @@ WITH requests AS ( page, url FROM - `httparchive.crawl.requests` AS req + `httparchive.crawl.requests` WHERE date = '2025-06-01' AND is_root_page = true @@ -36,7 +36,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2024-06-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql b/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql index 137c86864f5..b626d197a07 100644 --- a/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql @@ -1,15 +1,16 @@ #standardSQL # Number of third-party requests per page by rank + WITH requests AS ( SELECT client, page, url FROM - `httparchive.all.requests` AS req + `httparchive.crawl.requests` WHERE - req.date = '2025-06-01' AND - req.is_root_page = true + date = '2025-06-01' AND + is_root_page = true ), pages AS ( @@ -18,10 +19,10 @@ pages AS ( page, rank FROM - `httparchive.all.pages` AS pg + `httparchive.crawl.pages` WHERE - pg.date = '2025-06-01' AND - pg.is_root_page = true + date = '2025-06-01' AND + is_root_page = true ), third_party AS ( From beff2c6298660ef94491ea1b28a677a924ae15c2 Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Sun, 14 Sep 2025 23:08:36 -0700 Subject: [PATCH 11/37] Updated third parties table date --- .../consent_signal_prevalence_by_third_party_category.sql | 2 +- .../consent_signals_by_parameter_and_domain_optimized.sql | 2 +- .../distribution_of_websites_by_number_of_third_parties.sql | 2 +- sql/2025/third-parties/number_of_third_parties_by_rank.sql | 2 +- .../number_of_third_party_providers_by_rank_and_category.sql | 2 +- .../third-parties/percent_of_third_parties_by_content_type.sql | 2 +- .../percent_of_websites_with_third_party_by_ranking.sql | 2 +- ...nce_of_consent_signals_in_third_party_requests_optimized.sql | 2 +- .../top100_third_parties_by_number_of_websites.sql | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql b/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql index 656507d7bce..1d6e9eca25a 100644 --- a/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql +++ b/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql @@ -35,7 +35,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql b/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql index ce615af26ca..14da4eaf493 100644 --- a/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql +++ b/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql @@ -37,7 +37,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql index b0a094bafba..9b1165ac09e 100644 --- a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql +++ b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql @@ -26,7 +26,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank.sql b/sql/2025/third-parties/number_of_third_parties_by_rank.sql index 110d9fa27a4..c7559524cba 100644 --- a/sql/2025/third-parties/number_of_third_parties_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_parties_by_rank.sql @@ -36,7 +36,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql index 7dbf1aa7960..d96e77931c6 100644 --- a/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql +++ b/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql @@ -35,7 +35,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category NOT IN ('hosting') GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql b/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql index 386dfe5cda8..f6a0a333b9d 100644 --- a/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql +++ b/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql @@ -24,7 +24,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql b/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql index f859510694f..4558d541b09 100644 --- a/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql +++ b/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql @@ -24,7 +24,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql b/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql index 17d1c265761..0a5334ddd83 100644 --- a/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql +++ b/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql @@ -35,7 +35,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql b/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql index ab4aea2141d..f54d3cb220e 100644 --- a/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql +++ b/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql @@ -37,7 +37,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2024-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, From 87d7c8fff8c19a1b9d4ceaa9ecd6bffa1e5d09df Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Sat, 27 Dec 2025 18:26:12 +0500 Subject: [PATCH 12/37] Updated third-parties.md with the content --- src/content/en/2025/third-parties.md | 159 ++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 3 deletions(-) diff --git a/src/content/en/2025/third-parties.md b/src/content/en/2025/third-parties.md index 6deb792779e..1677f20129d 100644 --- a/src/content/en/2025/third-parties.md +++ b/src/content/en/2025/third-parties.md @@ -3,10 +3,10 @@ title: Third Parties description: Third Parties chapter of the 2025 Web Almanac covering data of what third parties are used on the web and an analysis of inclusion chains of third parties. hero_alt: Hero image of Web Almanac characters plugging various things into a web page. -authors: [] +authors: [jazlan, aziz] reviewers: [] -analysts: [] -editors: [] +analysts: [jazlan] +editors: [jazlan] translators: [] results: https://docs.google.com/spreadsheets/d/1FPssodcLgX8iFWFXDrthWVkBCUTl5_IJon2cyaZVudU/edit featured_quote: ... @@ -18,3 +18,156 @@ featured_stat_3: ... featured_stat_label_3: ... doi: ... --- + +## Introduction + +Third parties are ubiquitous on the web. Website developers rely on them to implement key features such as advertising, analytics, social media integration, payment processing, and content delivery. This modular approach enables efficient, rapid deployment of rich functionality. However, it introduces potential privacy, security, and performance concerns. + +In this chapter, we conduct an empirical analysis of third-party usage patterns on the web. We examine: + +* **Prevalence:** How many websites use third parties and in what proportions +* **Resource types:** The forms third parties take (images, JavaScript, fonts, etc.) +* **Functional categories:** Ad networks, analytics, CDNs, video providers, tag managers, and others +* **Integration methods:** How third parties are loaded directly or indirectly on pages +* **Consent infrastructure:** Which third parties handle consent signals and how those exchanges occur in practice + +## Definitions + +First, we establish some definitions and terminology that are used throughout our analysis. + +### Sites and pages + +In this chapter, like previous years, we use the term site to depict the registerable part of a given domain which is often referred to as *extended Top Level Domain plus one* (eTLD+1). For example, given the URL https://www.bar.com/ the eTLD+1 is bar.com and for the URL https://foo.co.uk the eTLD+1 is foo.co.uk. By page (or web page), we mean a unique URL or, more specifically, the document (for example HTML or JavaScript) located at the particular URL. + +### What is a Third Party + +We stick to the definition of a third party used in previous editions of the Web Almanac to allow for comparison between this and the previous editions. + +A third party is an entity different from the site owner (aka first party). It involves the aspects of the site not directly implemented and served by the site owner. More precisely, third-party content is loaded from a different site (i.e., the third party) rather than the one originally visited by the user. Assume that the user visits example.com (the first party) and example.com includes silly cat images from awesome-cats.edu (for example using an \ tag). In that scenario, awesome-cats.edu is the third party, as it was not originally visited by the user. However, if the user directly visits awesome-cats.edu, awesome-cats.edu is the first party. + +For our analysis, only third parties originating from a domain whose resources can be found on at least 5 unique pages in the HTTP Archive dataset were included to match the definition. + +When third-party content is directly served from a first-party domain, it is counted as first-party content. For example, self-hosted analytics scripts, CSS, or fonts are counted as first-party content. Similarly, first-party content served from a third-party domain is counted as third-party content. Some third parties serve content from different subdomains. However, regardless of the number of subdomains, they are counted as a single third party. + +Further, it is becoming increasingly common for third parties to be masqueraded as a first party. Two key techniques enable this: + +**CNAME cloaking** involves using a CNAME record to make a third party's content appear to come from the first-party domain. We consider CNAME-cloaked services as first parties in this analysis. + +**Server-side tracking** is an emerging trend where the site owner embeds the tracker as a first party and routes all requests through the first-party domain, making the tracker appear as a first party. For example, a website www.example.com may embed server-side Google Tag Manager with Google Analytics and cloak the subdomain sst.example.com to send requests to a Google Tag Manager Container. In this way, requests to third parties originate from the tag manager's server rather than the user's browser. + +In our analysis, we treat such cases as first-party interactions because the third-party communication occurs server-to-server and is not directly observable in the client-side HTTP Archive data. As a result, our measurements represent a **lower bound** on the actual prevalence of third parties on the web + +## Categories + +As previously indicated, third parties can be used for various use cases—for example, to include videos, to serve ads, or to include content from social media sites. Similar to the previous year, to categorize the observed third parties in our dataset, we rely on the [third-party Web](https://github.com/patrickhulce/third-party-web/#third-parties-by-category) repository from [Patrick Hulce](https://x.com/patrickhulce). The repository breaks down third parties along the following categories: + +* **Ad:** These scripts are part of advertising networks, either serving or measuring. +* **Analytics:** These scripts measure or track users and their actions. There’s a wide range of impact here, depending on what’s being tracked. +* **CDN:** These are a mixture of publicly hosted open source libraries (for example jQuery) served over different public CDNs and private CDN usage. +* **Content:** These scripts are from content providers or publishing-specific affiliate tracking. +* **Customer Success:** These scripts are from customer support/marketing providers that offer chat and contact solutions. These scripts are generally heavier in weight. +* **Hosting:** These scripts are from web hosting platforms (WordPress, Wix, Squarespace, etc.). +* **Marketing:** These scripts are from marketing tools that add popups/newsletters/etc. +* **Social:** These scripts enable social features. +* **Tag Manager:** These scripts tend to load many other scripts and initiate many tasks. +* **Utility:** These scripts are developer utilities (API clients, site monitoring, fraud detection, etc.). +* **Video:** These scripts enable video player and streaming functionality. +* **Consent provider:** These scripts allow sites to manage the user consent (e.g. for the [General Data Protection Regulation](https://wikipedia.org/wiki/General_Data_Protection_Regulation) compliance). They are also known as the ’Cookie Consent’ popups and are usually loaded on the critical path. +* **Other:** These are miscellaneous scripts delivered via a shared origin with no precise category or attribution. + +### **Content Type** + +We use the [Content-Type](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Type) HTTP header to categorize third-party resources into different types, such as scripts, HTML content, JSON data, plain text, and images. This allows us to analyze the composition of third-party resources served across websites + +## Prevalence + +![][image1] + +Compared to the previous year, we observe a slight decrease in the percentage of pages that use one or more third parties across websites. However, despite this decrease, the percentage of pages with one or more third parties remain greater or equal to 90%. + +![][image2] + +Compared to the previous year, we observe a decrease in the median number of third-party domains across all website ranks. + +This decline may be due to several factors. First, third parties are increasingly obscured through CNAME cloaking and server-side tracking, which can reduce their visibility in client-side measurements. Second, our crawlers do not interact with cookie banners during the crawl, which may not generate consent signals. As a result, third parties may not run on a page, resulting in fewer observable third-party requests. + +We also observe that desktop pages generally include more third parties than mobile pages. + +![][image3] + +Higher-ranked websites load more third-party requests. The top 1,000 have a median of 129 requests on desktop and 106 on mobile, compared to 83 on desktop and 79 on mobile across all sites. + +Year-over-year, third-party requests have increased across all ranks. The top 1,000 sites show an increase of 15 requests on desktop and 15 on mobile compared to 2024, while the broader dataset increased by 5 requests on desktop and 5 on mobile. This upward trend occurs despite the decrease in the number of unique third-party domains we observed earlier, suggesting that individual third parties are sending more requests per page. + +![][image4] + +The top categories include ad, analytics consent-provider and other categories. + +![][image5] + +The top 3 types include script, image, and other + +![][image6] + +The top 10 third-party domains are dominated by Google-owned services, including fonts.googleapis.com, googletagmanager.com, google-analytics.com, accounts.google.com, and adservice.google.com. Meta's facebook.com is the only non-Google domain in the top 10, appearing at rank 7 with 21% of pages. + +## Consent Propagation Among Third Parties + +In this section, we examine how different third parties transmit user consent across the web. [Previous research](https://petsymposium.org/popets/2024/popets-2024-0120.pdf) has shown that third parties often rely on industry-standard frameworks to communicate consent information. In our analysis, we focus primarily on the IAB’s consent standards, including the Transparency and Consent Framework (TCF), the CCPA Compliance Framework, and the Global Privacy Platform (GPP). + +These frameworks define how consent information should be encoded and shared. For example, the IAB CCPA framework specifies that consent strings should be transmitted using the `us_privacy` parameter in network requests. We begin by identifying which consent standards are most prevalent among third parties observed in our dataset. + +We examine consent signal prevalence in three dimensions: across different website ranks, among different third-party categories, and among the third parties receiving the highest volume of consent signals + +### Prevalence of Consent Signals Across Different Ranks + +![][image7] + +We find that TCF is the dominant consent standard, particularly among higher-ranked sites where it reaches 36% compared to 18% across all sites. This aligns with GDPR's stricter opt-in requirements for European users. The USP Standard is the second most prevalent, ranging from 9-17% across ranks, reflecting CCPA and state-level US privacy laws. GPP adoption remains minimal at 3-6%, despite being designed to unify consent frameworks across different jurisdictions and regulatory regimes. + +### Consent Standard Distribution Across Different Categories + +![][image8] + +We observe different consent standard preferences across third-party categories. Social services show the highest TCF adoption, while advertising vendors employ a more balanced mix of GPP, USP Standard, and smaller TCF shares. Analytics vendors predominantly adopt GPP. Marketing services show balanced adoption of GPP and USP Standard, with minimal TCF. Content and utility services rely primarily on GPP and USP Standard rather than TCF. Tag managers and video services diverge significantly from other categories, showing minimal adoption of TCF, GPP, and USP Standard in favor of USP Non-Standard signals—with video vendors relying on non-standard mechanisms almost exclusively. + +### Top Third Parties Receiving Consent + +![][image9] + +Among top-ranked websites, Pubmatic.com receives the highest volume of consent signals, with adservice.google.com in second place. The majority of domains receiving the most consent signals are advertising and ad tech vendors—ad exchanges, DSPs, and ad servers. + +## Inclusion + +Recall from our earlier example that example.com (a first party) can include an image from awesome-cats.edu (a third party via an \ tag). This inclusion of an image would be considered direct inclusion. However, if the image was loaded by a third-party script on the site via the XMLHttpRequest, then the inclusion of the image would be considered indirect inclusion. The indirectly included third parties can further include additional third parties. For example, a third-party script that is directly included on the site may further include another third-party script. In this chapter, we do basic analysis of the depths of inclusion chains of the third parties. + +![][image10] +The median depth of the inclusion chain is 3 which means the majority of the third party include at least another third party on a web page. The maximum depth of the inclusion chain is 2285\. + +## Conclusion + +Our findings show the ubiquitous and increasingly concentrated nature of third parties on the web. More than nine-in-ten web pages include one or more third parties. While the median number of unique third-party domains has decreased compared to the previous year, we observe a significant increase in the total number of requests from third parties, suggesting individual vendors are sending more requests per page. + +In terms of consent standards, TCF is the dominant consent standard across all website ranks. Among individual third parties, [pubmatic.com](http://Pubmatic.com), [adservice.google.com](http://adservice.google.com) and other ad tech domains receive the highest volume of consent signals. + +Finally, the increasing use of obfuscation techniques such as CNAME cloaking and server-side tracking reduces visibility of third parties in client-side measurements, suggesting our findings represent a lower bound on actual prevalence. The privacy, security, and performance implications of third-party use remain important considerations for web developers. + +[image1]: + +[image2]: + +[image3]: + +[image4]: + +[image5]: + +[image6]: + +[image7]: + +[image8]: + +[image9]: + +[image10]: \ No newline at end of file From 8139992fce16beae7f0e2fa66765b16cb3f5fb2b Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sat, 3 Jan 2026 12:05:46 +0000 Subject: [PATCH 13/37] Enable chapter --- src/config/2025.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/config/2025.json b/src/config/2025.json index 8133238303b..28b53f42f85 100644 --- a/src/config/2025.json +++ b/src/config/2025.json @@ -45,8 +45,7 @@ "part": "I", "chapter_number": "5", "title": "Third Parties", - "slug": "third-parties", - "todo": true + "slug": "third-parties" }, { "part": "I", From 59fef6d06d93cc2df85d32a7b9732171beb3df05 Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sat, 3 Jan 2026 12:51:06 +0000 Subject: [PATCH 14/37] Technical edit --- src/config/contributors.json | 18 ++ src/content/en/2025/third-parties.md | 229 +++++++++++------- .../3p-req-categories-by-rank.png | Bin 0 -> 27075 bytes .../third-parties/3p-req-types-by-rank.png | Bin 0 -> 24045 bytes .../consent-signal-prevalence-by-category.png | Bin 0 -> 23750 bytes .../consent-signal-prevalence-by-domain.png | Bin 0 -> 27036 bytes .../consent-signal-prevalence-by-rank.png | Bin 0 -> 21233 bytes .../median-depth-tp-inclusion-chains.png | Bin 0 -> 17931 bytes .../2025/third-parties/num-3p-by-rank.png | Bin 0 -> 19435 bytes .../num-3p-req-per-page-by-rank.png | Bin 0 -> 22339 bytes .../pages-using-at-least-one-3p.png | Bin 0 -> 19064 bytes .../third-parties/top-3p-by-num-pages.png | Bin 0 -> 35470 bytes 12 files changed, 165 insertions(+), 82 deletions(-) create mode 100644 src/static/images/2025/third-parties/3p-req-categories-by-rank.png create mode 100644 src/static/images/2025/third-parties/3p-req-types-by-rank.png create mode 100644 src/static/images/2025/third-parties/consent-signal-prevalence-by-category.png create mode 100644 src/static/images/2025/third-parties/consent-signal-prevalence-by-domain.png create mode 100644 src/static/images/2025/third-parties/consent-signal-prevalence-by-rank.png create mode 100644 src/static/images/2025/third-parties/median-depth-tp-inclusion-chains.png create mode 100644 src/static/images/2025/third-parties/num-3p-by-rank.png create mode 100644 src/static/images/2025/third-parties/num-3p-req-per-page-by-rank.png create mode 100644 src/static/images/2025/third-parties/pages-using-at-least-one-3p.png create mode 100644 src/static/images/2025/third-parties/top-3p-by-num-pages.png diff --git a/src/config/contributors.json b/src/config/contributors.json index 302967136be..d8f5d410a9c 100644 --- a/src/config/contributors.json +++ b/src/config/contributors.json @@ -647,6 +647,14 @@ "twitter": "LoukilAymen", "website": "http://www.aymen-loukil.com/en/" }, + "aziz": { + "name": "Aziz", + "teams": { + "2025": [ + "authors" + ] + } + }, "tunetheweb": { "avatar_url": "10931297", "bluesky": "tunetheweb.com", @@ -3381,6 +3389,16 @@ ] } }, + "jazlan01": { + "github": "jazlan01", + "name": "Muhammad Jazlan", + "teams": { + "2025": [ + "analysts", + "authors" + ] + } + }, "natedame": { "avatar_url": "67608345", "github": "natedame", diff --git a/src/content/en/2025/third-parties.md b/src/content/en/2025/third-parties.md index 1677f20129d..1eab6067a69 100644 --- a/src/content/en/2025/third-parties.md +++ b/src/content/en/2025/third-parties.md @@ -3,20 +3,22 @@ title: Third Parties description: Third Parties chapter of the 2025 Web Almanac covering data of what third parties are used on the web and an analysis of inclusion chains of third parties. hero_alt: Hero image of Web Almanac characters plugging various things into a web page. -authors: [jazlan, aziz] -reviewers: [] -analysts: [jazlan] -editors: [jazlan] +authors: [jazlan01, aziz] +reviewers: [tunetheweb] +analysts: [jazlan01] +editors: [tunetheweb] translators: [] +jazlan01_bio: Muhammad Jazlan is a Computer Science senior at Lahore University of Management Sciences, School of Science and Engineering. He works as a research assistant in the domain of Networks, AI Safety and Next Generation Cellular Devices. His work experience also includes working as a research intern at Biomedical Informatics & Engineering Research Laboratory. +aziz_bio: TODO results: https://docs.google.com/spreadsheets/d/1FPssodcLgX8iFWFXDrthWVkBCUTl5_IJon2cyaZVudU/edit -featured_quote: ... -featured_stat_1: ... -featured_stat_label_1: ... -featured_stat_2: ... -featured_stat_label_2: ... -featured_stat_3: ... -featured_stat_label_3: ... -doi: ... +featured_quote: TODO +featured_stat_1: TODO +featured_stat_label_1: TODO +featured_stat_2: TODO +featured_stat_label_2: TODO +featured_stat_3: TODO +featured_stat_label_3: TODO +doi: TODO --- ## Introduction @@ -25,11 +27,11 @@ Third parties are ubiquitous on the web. Website developers rely on them to impl In this chapter, we conduct an empirical analysis of third-party usage patterns on the web. We examine: -* **Prevalence:** How many websites use third parties and in what proportions -* **Resource types:** The forms third parties take (images, JavaScript, fonts, etc.) -* **Functional categories:** Ad networks, analytics, CDNs, video providers, tag managers, and others -* **Integration methods:** How third parties are loaded directly or indirectly on pages -* **Consent infrastructure:** Which third parties handle consent signals and how those exchanges occur in practice +- **Prevalence:** How many websites use third parties and in what proportions +- **Resource types:** The forms third parties take (images, JavaScript, fonts, etc.) +- **Functional categories:** Ad networks, analytics, CDNs, video providers, tag managers, and others +- **Integration methods:** How third parties are loaded directly or indirectly on pages +- **Consent infrastructure:** Which third parties handle consent signals and how those exchanges occur in practice ## Definitions @@ -37,137 +39,200 @@ First, we establish some definitions and terminology that are used throughout ou ### Sites and pages -In this chapter, like previous years, we use the term site to depict the registerable part of a given domain which is often referred to as *extended Top Level Domain plus one* (eTLD+1). For example, given the URL https://www.bar.com/ the eTLD+1 is bar.com and for the URL https://foo.co.uk the eTLD+1 is foo.co.uk. By page (or web page), we mean a unique URL or, more specifically, the document (for example HTML or JavaScript) located at the particular URL. +In this chapter, like previous years, we use the term site to depict the registerable part of a given domain which is often referred to as *extended Top Level Domain plus one* (eTLD+1). For example, given the URL `https://www.bar.com/` the eTLD+1 is `bar.com` and for the URL `https://foo.co.uk` the eTLD+1 is `foo.co.uk`. By page (or web page), we mean a unique URL or, more specifically, the document (for example HTML or JavaScript) located at the particular URL. -### What is a Third Party +### What is a Third Party? We stick to the definition of a third party used in previous editions of the Web Almanac to allow for comparison between this and the previous editions. -A third party is an entity different from the site owner (aka first party). It involves the aspects of the site not directly implemented and served by the site owner. More precisely, third-party content is loaded from a different site (i.e., the third party) rather than the one originally visited by the user. Assume that the user visits example.com (the first party) and example.com includes silly cat images from awesome-cats.edu (for example using an \ tag). In that scenario, awesome-cats.edu is the third party, as it was not originally visited by the user. However, if the user directly visits awesome-cats.edu, awesome-cats.edu is the first party. +A third party is an entity different from the site owner (aka first party). It involves the aspects of the site not directly implemented and served by the site owner. More precisely, third-party content is loaded from a different site (i.e., the third party) rather than the one originally visited by the user. Assume that the user visits example.com (the first party) and example.com includes silly cat images from `awesome-cats.edu` (for example using an `` tag). In that scenario, `awesome-cats.edu` is the third party, as it was not originally visited by the user. However, if the user directly visits `awesome-cats.edu`, `awesome-cats.edu` is the first party. -For our analysis, only third parties originating from a domain whose resources can be found on at least 5 unique pages in the HTTP Archive dataset were included to match the definition. +For our analysis, only third parties originating from a domain whose resources can be found on at least 5 unique pages in the HTTP Archive dataset were included to match the definition. -When third-party content is directly served from a first-party domain, it is counted as first-party content. For example, self-hosted analytics scripts, CSS, or fonts are counted as first-party content. Similarly, first-party content served from a third-party domain is counted as third-party content. Some third parties serve content from different subdomains. However, regardless of the number of subdomains, they are counted as a single third party. +When third-party content is directly served from a first-party domain, it is counted as first-party content. For example, self-hosted analytics scripts, CSS, or fonts are counted as first-party content. Similarly, first-party content served from a third-party domain is counted as third-party content. Some third parties serve content from different subdomains. However, regardless of the number of subdomains, they are counted as a single third party. Further, it is becoming increasingly common for third parties to be masqueraded as a first party. Two key techniques enable this: **CNAME cloaking** involves using a CNAME record to make a third party's content appear to come from the first-party domain. We consider CNAME-cloaked services as first parties in this analysis. -**Server-side tracking** is an emerging trend where the site owner embeds the tracker as a first party and routes all requests through the first-party domain, making the tracker appear as a first party. For example, a website www.example.com may embed server-side Google Tag Manager with Google Analytics and cloak the subdomain sst.example.com to send requests to a Google Tag Manager Container. In this way, requests to third parties originate from the tag manager's server rather than the user's browser. +**Server-side tracking** is an emerging trend where the site owner embeds the tracker as a first party and routes all requests through the first-party domain, making the tracker appear as a first party. For example, a website `www.example.com` may embed server-side Google Tag Manager with Google Analytics and cloak the subdomain sst.example.com to send requests to a Google Tag Manager Container. In this way, requests to third parties originate from the tag manager's server rather than the user's browser. In our analysis, we treat such cases as first-party interactions because the third-party communication occurs server-to-server and is not directly observable in the client-side HTTP Archive data. As a result, our measurements represent a **lower bound** on the actual prevalence of third parties on the web ## Categories -As previously indicated, third parties can be used for various use cases—for example, to include videos, to serve ads, or to include content from social media sites. Similar to the previous year, to categorize the observed third parties in our dataset, we rely on the [third-party Web](https://github.com/patrickhulce/third-party-web/#third-parties-by-category) repository from [Patrick Hulce](https://x.com/patrickhulce). The repository breaks down third parties along the following categories: +As previously indicated, third parties can be used for various use cases—for example, to include videos, to serve ads, or to include content from social media sites. Similar to the previous year, to categorize the observed third parties in our dataset, we rely on the Third-Party Web repository from Patrick Hulce. The repository breaks down third parties along the following categories: -* **Ad:** These scripts are part of advertising networks, either serving or measuring. -* **Analytics:** These scripts measure or track users and their actions. There’s a wide range of impact here, depending on what’s being tracked. -* **CDN:** These are a mixture of publicly hosted open source libraries (for example jQuery) served over different public CDNs and private CDN usage. -* **Content:** These scripts are from content providers or publishing-specific affiliate tracking. -* **Customer Success:** These scripts are from customer support/marketing providers that offer chat and contact solutions. These scripts are generally heavier in weight. -* **Hosting:** These scripts are from web hosting platforms (WordPress, Wix, Squarespace, etc.). -* **Marketing:** These scripts are from marketing tools that add popups/newsletters/etc. -* **Social:** These scripts enable social features. -* **Tag Manager:** These scripts tend to load many other scripts and initiate many tasks. -* **Utility:** These scripts are developer utilities (API clients, site monitoring, fraud detection, etc.). -* **Video:** These scripts enable video player and streaming functionality. -* **Consent provider:** These scripts allow sites to manage the user consent (e.g. for the [General Data Protection Regulation](https://wikipedia.org/wiki/General_Data_Protection_Regulation) compliance). They are also known as the ’Cookie Consent’ popups and are usually loaded on the critical path. -* **Other:** These are miscellaneous scripts delivered via a shared origin with no precise category or attribution. +- **Ad:** These scripts are part of advertising networks, either serving or measuring. +- **Analytics:** These scripts measure or track users and their actions. There’s a wide range of impact here, depending on what’s being tracked. +- **CDN:** These are a mixture of publicly hosted open source libraries (for example jQuery) served over different public CDNs and private CDN usage. +- **Content:** These scripts are from content providers or publishing-specific affiliate tracking. +- **Customer Success:** These scripts are from customer support/marketing providers that offer chat and contact solutions. These scripts are generally heavier in weight. +- **Hosting:** These scripts are from web hosting platforms (WordPress, Wix, Squarespace, etc.). +- **Marketing:** These scripts are from marketing tools that add popups/newsletters/etc. +- **Social:** These scripts enable social features. +- **Tag Manager:** These scripts tend to load many other scripts and initiate many tasks. +- **Utility:** These scripts are developer utilities (API clients, site monitoring, fraud detection, etc.). +- **Video:** These scripts enable video player and streaming functionality. +- **Consent provider:** These scripts allow sites to manage the user consent (e.g. for the [General Data Protection Regulation](https://wikipedia.org/wiki/General_Data_Protection_Regulation) compliance). They are also known as the ’Cookie Consent’ popups and are usually loaded on the critical path. +- **Other:** These are miscellaneous scripts delivered via a shared origin with no precise category or attribution. -### **Content Type** +### `Content-Type` -We use the [Content-Type](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Type) HTTP header to categorize third-party resources into different types, such as scripts, HTML content, JSON data, plain text, and images. This allows us to analyze the composition of third-party resources served across websites +We use the [`Content-Type`](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Type) HTTP header to categorize third-party resources into different types, such as scripts, HTML content, JSON data, plain text, and images. This allows us to analyze the composition of third-party resources served across websites ## Prevalence -![][image1] +{{ figure_markup( + image="pages-using-at-least-one-3p.png", + caption="Percentage of pages that use one or more third parties.", + description="Bar chart showing percentage of pages across different rank groups that are using at least one third-party. Around 90%-92% pages use third-parties across different rank groups.", + chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=249114645&format=interactive", + sheets_gid="1741089577", + sql_file="percent_of_websites_with_third_party_by_ranking.sql" + ) +}} Compared to the previous year, we observe a slight decrease in the percentage of pages that use one or more third parties across websites. However, despite this decrease, the percentage of pages with one or more third parties remain greater or equal to 90%. -![][image2] +{{ figure_markup( + image="num-3p-by-rank.png", + caption="Distribution of the number of third parties by rank.", + description="Bar chart showing distribution of number of third parties by rank groups. Number of third parties decrease with increasing rank groups.", + chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=211745165&format=interactive", + sheets_gid="199539546", + sql_file="number_of_third_parties_by_rank.sql" + ) +}} -Compared to the previous year, we observe a decrease in the median number of third-party domains across all website ranks. +Compared to the previous year, we observe a decrease in the median number of third-party domains across all website ranks. This decline may be due to several factors. First, third parties are increasingly obscured through CNAME cloaking and server-side tracking, which can reduce their visibility in client-side measurements. Second, our crawlers do not interact with cookie banners during the crawl, which may not generate consent signals. As a result, third parties may not run on a page, resulting in fewer observable third-party requests. We also observe that desktop pages generally include more third parties than mobile pages. -![][image3] +{{ figure_markup( + image="num-3p-req-per-page-by-rank.png", + caption="Distribution of the number of third party requests per page by rank.", + description="Bar chart displaying the median number of third party requests per page by rank. Number of third party requests per page increases from top 1K to top 10K rank groups and then decreases for higher rank groups.", + chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=1763082827&format=interactive", + sheets_gid="641162136", + sql_file="number_of_third_party_requests_per_page_by_rank.sql" + ) +}} -Higher-ranked websites load more third-party requests. The top 1,000 have a median of 129 requests on desktop and 106 on mobile, compared to 83 on desktop and 79 on mobile across all sites. +Higher-ranked websites load more third-party requests. The top 1,000 have a median of 129 requests on desktop and 106 on mobile, compared to 83 on desktop and 79 on mobile across all sites. Year-over-year, third-party requests have increased across all ranks. The top 1,000 sites show an increase of 15 requests on desktop and 15 on mobile compared to 2024, while the broader dataset increased by 5 requests on desktop and 5 on mobile. This upward trend occurs despite the decrease in the number of unique third-party domains we observed earlier, suggesting that individual third parties are sending more requests per page. -![][image4] +{{ figure_markup( + image="3p-req-categories-by-rank.png", + caption="Distribution of the third party request categories by rank.", + description="Bar chart showing distribution of third party categories by rank groups. Top categories are consent provider, video, and customer success.", + chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=1133634663&format=interactive", + sheets_gid="445864775", + sql_file="number_of_third_parties_by_rank_and_category.sql" + ) +}} The top categories include ad, analytics consent-provider and other categories. -![][image5] +{{ figure_markup( + image="3p-req-types-by-rank.png", + caption="Distribution of the third party request types by rank.", + description="Pie chart showing percentage distribution of third party requests by content type. The top 3 content types are `script` (24.8%), `image` (19.9%), and other (13.9%)", + chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=1309978891&format=interactive", + sheets_gid="418010554", + sql_file="percent_of_third_parties_by_content_type.sql" + ) +}} -The top 3 types include script, image, and other +The top 3 types include script, image, and other. -![][image6] +{{ figure_markup( + image="top-3p-by-num-pages.png", + caption="Top third parties by the number of pages.", + description="Bar chart showing top third parties by the percentage of pages with their presence.", + chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=194077318&format=interactive", + sheets_gid="803451847", + sql_file="top100_third_parties_by_number_of_websites.sql", + width=600, + height=498 + ) +}} -The top 10 third-party domains are dominated by Google-owned services, including fonts.googleapis.com, googletagmanager.com, google-analytics.com, accounts.google.com, and adservice.google.com. Meta's facebook.com is the only non-Google domain in the top 10, appearing at rank 7 with 21% of pages. +The top 10 third-party domains are dominated by Google-owned services, including `fonts.googleapis.com`, `googletagmanager.com`, `google-analytics.com`, `accounts.google.com`, and `adservice.google.com`. Meta's `facebook.com` is the only non-Google domain in the top 10, appearing at rank 7 with 21% of pages. -## Consent Propagation Among Third Parties +## Consent propagation among third parties -In this section, we examine how different third parties transmit user consent across the web. [Previous research](https://petsymposium.org/popets/2024/popets-2024-0120.pdf) has shown that third parties often rely on industry-standard frameworks to communicate consent information. In our analysis, we focus primarily on the IAB’s consent standards, including the Transparency and Consent Framework (TCF), the CCPA Compliance Framework, and the Global Privacy Platform (GPP). +In this section, we examine how different third parties transmit user consent across the web. Previous research has shown that third parties often rely on industry-standard frameworks to communicate consent information. In our analysis, we focus primarily on the IAB’s consent standards, including the Transparency and Consent Framework (TCF), the CCPA Compliance Framework, and the Global Privacy Platform (GPP). These frameworks define how consent information should be encoded and shared. For example, the IAB CCPA framework specifies that consent strings should be transmitted using the `us_privacy` parameter in network requests. We begin by identifying which consent standards are most prevalent among third parties observed in our dataset. We examine consent signal prevalence in three dimensions: across different website ranks, among different third-party categories, and among the third parties receiving the highest volume of consent signals -### Prevalence of Consent Signals Across Different Ranks +### Prevalence of consent signals across different ranks -![][image7] +{{ figure_markup( + image="consent-signal-prevalence-by-rank.png", + caption="Consent signal prevalence by rank.", + description="Bar chart showing... TODO.", + chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=2066656520&format=interactive", + sheets_gid="1614774531", + sql_file="TODO.sql" + ) +}} We find that TCF is the dominant consent standard, particularly among higher-ranked sites where it reaches 36% compared to 18% across all sites. This aligns with GDPR's stricter opt-in requirements for European users. The USP Standard is the second most prevalent, ranging from 9-17% across ranks, reflecting CCPA and state-level US privacy laws. GPP adoption remains minimal at 3-6%, despite being designed to unify consent frameworks across different jurisdictions and regulatory regimes. -### Consent Standard Distribution Across Different Categories +### Consent standard distribution across different categories -![][image8] +{{ figure_markup( + image="consent-signal-prevalence-by-category.png", + caption="Consent signal prevalence by category.", + description="Stacked bar chart showing.. TODO.", + chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=828032137&format=interactive", + sheets_gid="1614774531", + sql_file="TODO.sql" + ) +}} We observe different consent standard preferences across third-party categories. Social services show the highest TCF adoption, while advertising vendors employ a more balanced mix of GPP, USP Standard, and smaller TCF shares. Analytics vendors predominantly adopt GPP. Marketing services show balanced adoption of GPP and USP Standard, with minimal TCF. Content and utility services rely primarily on GPP and USP Standard rather than TCF. Tag managers and video services diverge significantly from other categories, showing minimal adoption of TCF, GPP, and USP Standard in favor of USP Non-Standard signals—with video vendors relying on non-standard mechanisms almost exclusively. -### Top Third Parties Receiving Consent +### Top third parties receiving consent -![][image9] +{{ figure_markup( + image="consent-signal-prevalence-by-domain.png", + caption="Consent signal prevalence by domain.", + description="Bar chart showing... TODO.", + chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=1262795614&format=interactive", + sheets_gid="1788947788", + sql_file="TODO.sql" + ) +}} -Among top-ranked websites, Pubmatic.com receives the highest volume of consent signals, with adservice.google.com in second place. The majority of domains receiving the most consent signals are advertising and ad tech vendors—ad exchanges, DSPs, and ad servers. +Among top-ranked websites, `pubmatic.com` receives the highest volume of consent signals, with `adservice.google.com` in second place. The majority of domains receiving the most consent signals are advertising and ad tech vendors—ad exchanges, DSPs, and ad servers. ## Inclusion -Recall from our earlier example that example.com (a first party) can include an image from awesome-cats.edu (a third party via an \ tag). This inclusion of an image would be considered direct inclusion. However, if the image was loaded by a third-party script on the site via the XMLHttpRequest, then the inclusion of the image would be considered indirect inclusion. The indirectly included third parties can further include additional third parties. For example, a third-party script that is directly included on the site may further include another third-party script. In this chapter, we do basic analysis of the depths of inclusion chains of the third parties. +Recall from our earlier example that example.com (a first party) can include an image from awesome-cats.edu (a third party via an `` tag). This inclusion of an image would be considered direct inclusion. However, if the image was loaded by a third-party script on the site via the XMLHttpRequest, then the inclusion of the image would be considered indirect inclusion. The indirectly included third parties can further include additional third parties. For example, a third-party script that is directly included on the site may further include another third-party script. In this chapter, we do basic analysis of the depths of inclusion chains of the third parties. -![][image10] -The median depth of the inclusion chain is 3 which means the majority of the third party include at least another third party on a web page. The maximum depth of the inclusion chain is 2285\. +{{ figure_markup( + image="median-depth-tp-inclusion-chains.png", + caption="Median depth of third-party inclusion chains.", + description="Bar chart showing the median depth from inclusion chain.", + chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=692408075&format=interactive", + sheets_gid="1518420053", + sql_file="inclusion_chain.sql" + ) +}} + +The median depth of the inclusion chain is 3 which means the majority of the third party include at least another third party on a web page. The maximum depth of the inclusion chain is 2,285. ## Conclusion Our findings show the ubiquitous and increasingly concentrated nature of third parties on the web. More than nine-in-ten web pages include one or more third parties. While the median number of unique third-party domains has decreased compared to the previous year, we observe a significant increase in the total number of requests from third parties, suggesting individual vendors are sending more requests per page. -In terms of consent standards, TCF is the dominant consent standard across all website ranks. Among individual third parties, [pubmatic.com](http://Pubmatic.com), [adservice.google.com](http://adservice.google.com) and other ad tech domains receive the highest volume of consent signals. +In terms of consent standards, TCF is the dominant consent standard across all website ranks. Among individual third parties, `pubmatic.com`, `adservice.google.com` and other ad tech domains receive the highest volume of consent signals. Finally, the increasing use of obfuscation techniques such as CNAME cloaking and server-side tracking reduces visibility of third parties in client-side measurements, suggesting our findings represent a lower bound on actual prevalence. The privacy, security, and performance implications of third-party use remain important considerations for web developers. - -[image1]: - -[image2]: - -[image3]: - -[image4]: - -[image5]: - -[image6]: - -[image7]: - -[image8]: - -[image9]: - -[image10]: \ No newline at end of file diff --git a/src/static/images/2025/third-parties/3p-req-categories-by-rank.png b/src/static/images/2025/third-parties/3p-req-categories-by-rank.png new file mode 100644 index 0000000000000000000000000000000000000000..258ec20732c6c40f992081c96cc9c8285f898820 GIT binary patch literal 27075 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 S=7-PGEugyIy&BaAQU3!X=i-O} literal 0 HcmV?d00001 diff --git a/src/static/images/2025/third-parties/3p-req-types-by-rank.png b/src/static/images/2025/third-parties/3p-req-types-by-rank.png new file mode 100644 index 0000000000000000000000000000000000000000..660b0f490431b26f1ebf36642c506eb27293ac80 GIT binary patch literal 24045 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 literal 0 HcmV?d00001 diff --git a/src/static/images/2025/third-parties/consent-signal-prevalence-by-category.png b/src/static/images/2025/third-parties/consent-signal-prevalence-by-category.png new file mode 100644 index 0000000000000000000000000000000000000000..05a40c8b1aa61e2619e013a8f508b854789cd5db GIT binary patch literal 23750 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 literal 0 HcmV?d00001 diff --git a/src/static/images/2025/third-parties/consent-signal-prevalence-by-domain.png b/src/static/images/2025/third-parties/consent-signal-prevalence-by-domain.png new file mode 100644 index 0000000000000000000000000000000000000000..a7a0d74e00fbadbb11382827290d9ba29da622d1 GIT binary patch literal 27036 zcmb@r1yozn^Dc^8aCZ$J+_f$4E}>Y0mZHU>v}mAMvEnThEs{cTEfR{mJH_1#6e;D> z{{H{F?p^EMx87Utot2fGv(KKH?|d_}Cv$eRo{lO39xWaU3JQU`nvwwu3I+lN1#KG# z{c*(p`V$`t3hJt!mXY$q!$ZkHGcB+B<9K*v^!fRDW@hI7{r%nD9RUHs&CQLlu<+&O z<yZFMdn=(w8Ny43W{$jHdn*4C4glhxJL z?(QBfEv=oMovp3yfPjDsL{&umCowUx#^#p3zP|SMc3WFp6%`dTGcyixgRft|re}S8 zpO|87Y@GcmH#9U@~ zIXM{_nds=~oxQ{FePgh9u~Mo~5e3VthE88!-{jQvs;cTYkr}M4tSdi$xcbNP%b9g{ zc77~C#K*@!d-klYy|eslrEf^GwY7C#Nv)xw;pXOMeqph`m5-~d>zg-ka`FmzczBkU zmYbTI5)u;LC4TLPyWY#N<>bCntV>{*+1-q#fvxMhaH+ID2{&WO3#e^7Y;i^AN`CH z^7+1$sub<->HqO?_;DOSh8-q|l2BX)vw4dKyW_QNE~r(qCAVOvX?jNfAqaEq45~*P z_nm=RdHv7+tFz;q2@Uo^#OYEtGU2#FPNSR(#!zc#7-7Wg@2wBKSejLjYol}YsRNXyBR|^CpUUj_HJ<)TO*;E5=}Y}O z5}VXjVohKy33s(%I%Z7HlWMlNum>M+Ta$<+mJF<`j1RXWc~6f?dGlhDQiD(rs6vp0 z7K_Fkk9HuE^;rWOuWNTvEmS#_y`bYZ!h$Z}#toXj~WSErgc@9>K!)`-C+b;@IQ9t3@5kMyr6KdOo^0}7wBeFB=&1{O3 zN?(Sfz20^8(J!61bM;}ym>J3aFM_UjRz{dy7*H~Ld4GOie7(&+>b$Xb@x~1N2{4lp~hx`Vo&0*4sMRD z0Pp>FKb4z*+9MP6{I(csuO%})>0!2QYVQvi&L~TC8_5?ekuiX%cX3I4Yc2cqyCJoc zlH(aQ0cW7ibIPu8id#-+Vz_1LcMfMRH4<4CM}hfy#0}>)b+#pWn|5fGph=V~y6qn- z2tJ@vl$UuoG#>euK#Rhb{itL1PWt1ppc6szFp0q7Q`09=_UD>$Upkl&zRpa7@b$Wl z-wu%JYmI1Ax3io#y>kwIZ`pTg7)qHZ?X@?o$KU>Ne>Y1TnnM9zbR_S2Ce<*Dd>KJi zskyotcoQnN(H-UTGcL2$yx8z7;P~*$UL)I~C9B?# zrdlswCYlMje|}!d7tnXHwk#2+oS6ShkN6v|XOuq1%po1#hNtBjtpWCAmI1_`5Hp|= z)ss+gB+o4bQZHz~$$`fEYGuLY1-sANrrOACNu^~Y$xZCEX1l>ubY~dP^FQ(zavCnb zd?Eb~AVjiBCPOChpxyF5wM{v*2oH{_v48VT-pf2=X2$cN6kU_JAO3ToVDkYF#KNus zV-BXqE)mr5uKRE^X{H{UDn`D!h8)!5PA%h_kL^g#MEM-AH!)VTa$Yw($C50j(SSav zoGE~rCa*%l=f@K{Tl=C*3_W6Kq((J6aWNC^uaVcI+#r673a+l<>_%@C;s37yU--~Y(z z&O2;n_(nwS{N7<(JkD`^(>|R1AyufJWRSSvoMQlme$QHe8%ep-q2hNbZ5A1xk3h3) zm232gm?q(L-zmOcY_3N%3m{!IDAS7G^?e$?Wfz^NO06byhd(8q2_>>1``qtZW86(! zZ`x(?QRn*?aLknvGG;f%YakNkkHdG>s0m6dH!>)9vOYIRh==6$I0=f@Rfzcvxe2yt zdv-;}2jPVEuA*;JkiE!e*z(*v>nj0IYXM_b)UHrh$QLCGy;yf4Gy+7 zVvs+dq+H6R;9Pd?6rZ7?iL7RdAMVW64IdvB&Zek}E#rlwKP82ZMB*H<&`7=)W=&8~ zmn04JTG;>@e)6<+nJXM9I*?tIz-T5~_XDu`YKUyIfNN zXcxgD9#>HntgN~q?^lcPA!&{%SUbD5)6;g^u3gw(^*;n25M%I2ZPQeRe-h<({9CG5>QBTUD`jy-hZJRCoDCR$w%ZblBk1o@zn-S(tC=ZD5%tM!PPRFaci=l(i~7pztZUK ze7om@62nEvp)N7AS&-l6FvWU3d9YI+iVgBxCqIw_O0w(sSwYq42RvLgk4PLyJ z(7dr|acZeKbYv~cs43?CUL{!!6gn9MR9Y_^MD<%#fV>0?murS&yp1@MD> zo}ggGqHtfr?2}p7;yC5o{iaBy5MsF5X5N`!b+oAPpsolsMGEK56Lq}?Rho$pW_aZq zRv9(JEoA|6zb<$`g$3^0StNg6w6XM64PU{}-2H&eSI>i8+T$ca#})3JC5b0Q`Ih34 z8Sm%A^{FSR*%uBegh|LKpBxDP2|>g?*}}@kgcc!q;$`eccdaov;>=6Yn)01`q zz+e^00ZuFjjB+L&%62kuRh~Ja7~jM~Iy#dY?_$Hfsx^X zsqHRP415?uU?C~EldI|x^g?~XIqrDn<*>_cL6r=V>X^}qt2zYD#LexWs|@j3 z!1G(DPE@aAGGuiOKemAR?kE5J#bIYvdPlJ>LT3ZeNS_Ui5m5cYnO#31QLO#~aiu^ULV>DYbfa1m+2Loq9 zsYNQZUW);bxM`A89D9+`A^5ne%JJ(;;#G3W!ZTEq4*@g&?v{XqyKy9MLAoQA6acyq zuZi9f*!eO3rPgrW#_%t9OBuG@-o+3pj_DPXhxNk+^q<8Dn>E*9>ZiL$>zawPCn~-PCSFY$;FC{jrOLJV0T}#FvbNGZ+dY?HsI*) z)tAQ9?Y7rk>T))<*Bc*kN5FO%ftKq*Gfif~UKTgNJ;H z!&<=ye+mLI$y<1Tsnv)mio*$EY4+(0Vvgv+Ll}VRJ2tJ}2364)hykTrrl%19g+cPcCMv)tuX#i{7gd>_uz zLBcix)K!leQz2YJD$BwO^NSud^Z|ejY)y;hVB7S5s|?mmgcZHV`L^5FXB`p;d0Q-B zhzvua`9&gp_*Hs_3S?XGqA~}O6Oe?037T#b>ox@1zJW4C9K}$0Ha~_+G!q*zvGQ4U zGuK+UjaIqm70~s4%12Y&@=!E0h9?(5D9v30V6fBRs+}41t3nAJ^X~Wed?6|Ywj?#= z!U9*y^1HgT7-0tE*Ow8gD7Lz2-1=fPSO1>P9MnCHyfp#-0^hK!jm0853i8M)b(wDS z%TkiBZXE~!4!~#J@m_nIiFmPZ`9AJUlcmu>XgURO22sr(%?NUw+X1@_B9jr2c+!<& zusIvZQ;7U%n#_brlDlbJm=H*P`03G*DR>(D$8jy9jlyAE(ANc}U~irap@C|Ic##001ml0viu#tK6)1FXIeRlwYSJv%Y)ZMb@kfNPMPelJdH$`J&cyi_E_y z^6PsWTZd?)=~qzefsCV(l#eDa5GthEUl0Sdc6)SRZ?k^66GhP(m0{~z$<<2h*{sz! z@)tIDR|1o70C^6&-6=UR`KuT~#wy;Fc+ZgVos6xsCAa#i}*s8gv;w)4}t6 zLz`&d)@An9E-bh~A_Q%_2lt!cB9LRMxWVi9>6F_uZU@hpH$M_;h8aGb?^iHN|6y@W zl8zyeZ~|tY$aGOo6$^;|u|1NBMtv#In6$!iwGMZkqKUj>xw!9YcJ0KbKD2pq@yYW1 zWP$FZLa-nFw($+xv`fv{6W~*QPTTwM^nWJn_+E%wQ;K6BWF6Md1Pgb6K~50I{R);W z`NB&}psWubcz;0g71J5|hVb=kvE`ON$qnsw{du@|CSwH6<6*BJQV zqfj%PHkE=>n{d+CedEYmavO3N6i}TU2+rVl&w2?S}^{*n~@&pUvqxbE*|#y zHsgOe@ML^z3b=@sQLkNOiNlQ0rmmxuk{$4)UGrZ--2*%GyA-{J`{9mziq~^&nD%Ts zGR+xQTE`8fd=v6qCS4DO*kNfJZZLU$5v}Hn`wY>Z$1qQW?_tK}){5Td%z4#KNt|>30dll& zpv%PIy5X%8hp2K8D=h8N-Qaa?sN=J7YRZy=IppiDslm8koBAu+=mIAJB@{2G)3GUi zV(99#egF{>{$smKNUxj{uefd^Oq2eU^#e3#4bMZF0EK;)5Q@^=rB>EDbcptOqKyah z&D2Qc3;5WMKzv->aMaDTLA#@wzau0QFZPwPfz-8y8X4Fvt@{yzX3A*&&HGK9I@9x` zq>kq)*xFdul;CCp)}0r$Paq;*Uta21$ed%=rEjCqEWJt$U{9Aw3v`6!iE%XDn?rC} z7%y}pOHz$&7nQRvAdO@v)v>Cm6-Rwcu%;UxL@Rmx4vX|eKe1&ej%Amua#Zf?d#|;# zApRhUBkOs``NGV0=UlHr4n5NZ?6lnV7~7pa?IrV2%*!eUD|ydA_CMs{XEFmbM9dMp zxc&LVA(F0lZzr}xTMU5Hoi%*B?OD!L>9WZqSw>W$V#YMZG^Kv%_<0dSdLVL)WxKW^@{8oZ#yFR78#kwgE4mWODazqJ_ls2Wxob(~s=L>O zG`&*aAHcuMo1#8V#ru*aeHEgYBs=1UVX}1Wobh7Dc39GU-O|aR93#g7^Cf!Cvw7;s zU_nitdSV4{g2j(FDkEA2Xw(t{nO@zI3$>OO^|Pb9f*XEM)hk*;DN;^EtCR9yya}`^`!-TM_Ohk-rf|8QIN_7pPbZh+d#CC086mSJ@owq! z=PhqpK@BnCPrOgoVv~Oo)b#Zt10?_lGebfV%ov2fqK>5$%c!SHKCo~ytPN)&w61IPSqtgYu(k@kDd1E;nA>f4ky zKL=YT^wDO~ld=i2{4f+5Ta@*-)_=U~Az8~*oDzMPjs3gdTXL96>JqS%BJ5j_4UN zs)!f!8%Ma{4I9b|+<>|`UPr{KrqM&2Xxe&!x9VeNBvL`dXBpdp)6hWM@G#Y2a%yeQ z+kU92WUKFF5#@7Fr-z;AStlo!%LJFJn|!Z+D!TVt5QC2Zsp~sgvY9?@4a#D` zkmpEJTCw;~H?Ds+4ow3I6hvWly~|aZdpOLeIYe86sA0^+7wWEsDP}w0rAK@H8|~GH zXOtQVG$KxzE%N8FG_bXy1eT0mK@S*&ITYe2u$-oE+2({UpLTZk>$S)}>8pK?U!a`n zzl^l+z5OK9^|6fu9?m;=t_QXfm)w4}QG)MW@JHb!OGwEa%=_btQYdX<;o=Pi`ZHp_ zmwZ^AK(|SUW?C@XA-E59iQR2*x8EnK)uHQRR}U;QCYkVcj8ebtE5>9YvH$6IR(nN* z*8n~}sp)Xt-t5r)UX=C1jB`}`=Sl@U`>;yf@{dzz7}SFltQ0k$u*>StD( z#Z9*F>Je@r4CWg23GQiA43aS8@1yJ-=Q&jdes=R8&Z$#z?yKEBP;|S|$|4a~LJ7)T z@KC#VWRwv|X`Jmf#M8an05V^*T?>SZ7JE3S`_Xo}so2hJ1g0xg%Fo2g@Ui3htZpGR z%Ai;ayBtxtMR6Ca`S6rjDg4vGrz)B9G?x_-I`XeE-n(Xz$YiLEFF1V55jhZFD*i?B zL=npV+;QFCTy>wIg23zT#h|}d+n*%u(M1uFVzo2HJ`OU9yG7*f7B75{6B%>CV^=r4 zF>DLX4C&2_3TNfswgZJ-qf|_6^??rwqr5&P%*oN(U`)t&W!aFfvp}xLlGhzZB4xr{6)?u21d_g!#V`Ay8PUG@SW6~uW8N*NW^BrVNe3a-H zA{e{H-QJ2X3v_&@@8u!UZurKr`1>c*=i@}4M4_Yg&t!J|U}CRGx6Y#xdM_k`x7PCz z6@Mg($T)Tqi>^36tHy&d#@ERYP{vTn7O$9hlYyG%x5(_V~0 z_E(qnh{)BKh18JnqmpGNDA66s>+fh?tv0dOPX`G&pE=Ckqd2=z9JGEr3#Ot}yX0xR zGXgjIAI=R3MZBhD#~ZBQO{=IpwEXsN;(aNS^&O?zT-B$2Utf2oD*wTRXPSXooB=gE zzrVfWvD(83YGePfpRD{#PY~t;&>ZH7dL>M*l>`nqX&I3gOG+xY(tv%q#_g0Gr^0nh z9pH(&&nEOl3~6vWd|h(KasIktcSm()1m+rHjs6+K-9wF(GD?GyP|{wuYWCFKW@Q%j z9aV^sH46iA@}s}&a1T>1%2G&qwL&P_Y;bW*&~soF_BjCOt%BV@U$63H6oFR(JAYu3 z2)n1Qn$$oMf&v!$&ulc+TR$i%V{iiA?($O$J04O;?h0vB!7$CY`>Ggdodl`g|t0%7rAdhgFD z7m*6mst2Dy1`iSnAn%B9GRnhEAb&FOGFGYSM4&z zp2A^1ms0*c`9AWP{JvsDOJ^ZYeTy1&Q8?;8Tkw%vKp=QQ(5Q3bBxHRQjzO53{6&?BJji|Q z7f_Ba;P$8@I$7!uC_&CiBqXZ3F^NzqYh@=YGD_C@r^0CDU4kwi~w`c>TSa2y%auta&82r+^&d; z7;UwKF$7ywgCIqgsN=c!({pZ*jydO{p6Q?RHSVY?=PydTz^4wsxA4*$am2|%uO%qe z8EquN@iFbK+Wc|2GYe!74pHzP?Y*Uoo%ibE>EAdBJSnb~7_B>((S?K0dGl~*api+6+3Ya3kD?d^??_710Y(Y(Eg7viR`$`6c#fYutmElX2))4q5 z(d5%xO%e1}LbC@Eu$jYsNdo7n2IJQ(-HRWI?!8tOkmy*5VrTW$JJ5zq_>vxi*eL~; z1o@_DmonVQ9sR&`ijEZ|e6NadBg&dw$#@0|m|_e9Jei|@{YVD?Uouu|*gD)TdpqQ1 z!hg*2uyslc^8s|S&);h+X#~ZL{f3G#tA-nMo>=B9CAJlTMu=IbPr6VwrYvyJNsO=)?kR70Fdvs;`qbtR#l5uAaPj$h|N{As{ zaOVrdiOm(;4myIF>5^dF{sPKbl0{qY$JxnFD7<(x-wD-}eEZS1`-K%`IKc6pj$lVo z`;kX-90H$U! zmJ1g+WzKOmQ)2n?&6$j^Q(Wtwd_A`x8m%P?FFs!eBp_;g((nI7Zd7vj72UkdC9Um?WTrqkX~XTI$24*#yYpT2F=WqV- zj_vO}H*ONuj<7JXEd$o~A()uX14T+prvOTFIQOG8`lGZVKl^$}rFAg!QF5!=?45~W z%@}a$GsHfi--`5s&`fg6yFCSsDuATmI$k^hQ1DGDamk zM|al%x||}^`u6*4V?Cf0)2@MFZd@3}{g1gL@${&by0PrnZ{_#GE@k+^ucV6GCCpM9 zzY*EVYY+CFpgAsngA`@SUtdbml#(&UV5_S;Nz2d(A9(2Y$hYu>#&-{q>*c1d$?b+b zI2a~{S+;wBNB8-*SmTUUt>L=L%E`NkwQts^(z%d>wqUHBe{|@Wfg1elYcpZy!W`Oy znZPnCZ8kr+pb65wk?~$cKcx}dE~Mb8L6IUCAN%`Vv($@v*fe3GBQ=e-zDYX4ENCgc zl?YsK7cB=LYK7-rjnNr( zkO~f_pRD3=A`Q5{)#yb10Q}U^=}>J{+qn?xuFO?Q0jY2xLap z34+VVz%#!oT{MV3XW}s`D&~3IALSFw++Hq05Y>7W z!0qRD$#?{=rjZsrQU`|kE{Nw1q_EMBnHW>OpTT(P~I^{+pA=*NzyT+$E6?fmdN1$eMFhMI`aj847IX!sgL_;SNMMmnN+TgVL8vgHT%|U-?$bgmJBeV zf4}FSA{os_Yk*re?vCqq%FGh*Y;_OwkGFq|F(iz=Tx;IJSqy*1rX4Wc6IUiKJ`pJ3 z4%HI3n}gJG5B7D5V0v(E$PJa^hBj~KL)cd>RFVwMx)#OG&}po>7a{#6;rO|RRPZDV z#JabZYBpRQ9<(5UCR)fztklWl-SK2az4U5Se?&_YziApOf4

GrDuTJtboxIjk3u^&+tk_+|;2u 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 Ghy52N;n>Ci literal 0 HcmV?d00001 diff --git a/src/static/images/2025/third-parties/consent-signal-prevalence-by-rank.png b/src/static/images/2025/third-parties/consent-signal-prevalence-by-rank.png new file mode 100644 index 0000000000000000000000000000000000000000..c0095bc7978d5aa9d24c012972609212a83455a5 GIT binary patch literal 21233 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 literal 0 HcmV?d00001 diff --git a/src/static/images/2025/third-parties/num-3p-by-rank.png b/src/static/images/2025/third-parties/num-3p-by-rank.png new file mode 100644 index 0000000000000000000000000000000000000000..b8f35b8b1c80e81d49fd1b9c011d619ea1e8cbb2 GIT binary patch literal 19435 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 Dm(-Cf literal 0 HcmV?d00001 diff --git a/src/static/images/2025/third-parties/num-3p-req-per-page-by-rank.png b/src/static/images/2025/third-parties/num-3p-req-per-page-by-rank.png new file mode 100644 index 0000000000000000000000000000000000000000..d2349a403cad141833942aad0ad0a2cac84e8e1f GIT binary patch literal 22339 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≪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 a%SVMby7(_CJqQY-47JpBZ|1352mBY4B}gg& literal 0 HcmV?d00001 diff --git a/src/static/images/2025/third-parties/pages-using-at-least-one-3p.png b/src/static/images/2025/third-parties/pages-using-at-least-one-3p.png new file mode 100644 index 0000000000000000000000000000000000000000..be8f971210baaefba7cb8287560fd4364b2380b1 GIT binary patch literal 19064 zcmc$`byQrAt+PgQq4U9;D`fAvBR2a^;N007`9$jiJ20Fa>o z0OBe-61>IuJe>pnApJ^NUH10&*1$eIzqkJ4;)0Y-;qdT~kB=`oIT;-M9^R9bl=RoH zUpF^5fgxe3si{|2SLf#!<5NE}GBU2NuTM@+r>Ca}1_naje^^{xY-?-V+1VK!9IUNx z7$2W_{~^-IF+xN{WN&Zp;^K03eS81lXnA!jGc)t>=p-d2g^7u2YiECRbE^^7*Ecv` zQ&U@BQJI?YX=rHp`1rWDufMpsWO#TaH?N?nxp{SUt)uhn?Ck8^+rKS1B)vm7Y@87@Y<>e(LrL=bq=H%vXY;2Z7q1M*cF8=YM@%iDA zG4I~J8ycN8b&kr)%BpSdX=!bp|M4RtA?NJud~)VzVPTYE-U;!U(1wPFnAGAIZyndy*E7FV ztgNiGbPj@}vJ(;#LPJA+!ZKg$xwdtG|5Q+uoL#A;q%P6hlKpT3Xs?&z{+NMk;99Y#i=dy2n0y zX|}ku-ZwPmSa?n}RGJ+f!;$^PRaJl)RZcV_;ubk27z5OfOCdq&10?}b5&oWzM6 zB(*-|(!Kh6ne#O_HKw!UlS-q|PdP#LTD8q}TRRU8zp3gFYJU*R+@W^6fe}CCR}ry# z@yy_rVj3|pIo4sbnzu5GyiD0&h(1PQx7ET$10pl$o_Pv?KVhDOff+7%#gg$XPu z1H338;l&n1=LI1ChHwQ=Cuk*j6_hKyBf<}?JHUdfL9xl?ZK13PpTN1lh;TKFhBD&7 z2?b(EA}Cw4718scl_Edf#8c0wKoYW^D;4sbW2VF(pp#6j zL^GKWO*op39i!JXMdl4CeND|gVpxuhxjlktN>w$mk;?W}P>DRBDG{x=!HPkHM1f0D z^IR!L?6L~idQ)V%3IIXU}M0j0RO=zyy;E3Fo-aM;>l(`kL&G=_I~ zq|Ht%pIjzNA3j9csFKMmP$8Rb;s^|KS>Ejw8sJ}wW1jZEwL7)nJOd%|&~e!Jr4Ci9 zzY35Qliv$|P7|wpDU`xD4}{|K%H2hX@MYYH-*wCY9xM5B3oc%a8t%2Aysf|@1_6(t0Ipa3nxz#|T3RAU2MiVjL7A>ai8eY>TB0YHnZVwa{X z_x3WhQK4OK`U!9z%ivTK=1^u=CJvnQgfh_<(u~8BU=Dk6d==UFg(>6^jE`sTP?uT^ zl^IV(XzyT;oYccUkK-(q_m3bFp*>CZ?D?Xv-}*b}-Xf|dSh&<^LDjQWz_}jZiqR9T z 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

  • {}dok3&?x$)A%HTe#6)v_Q5sL(HWM8EBynz@o|r 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 literal 0 HcmV?d00001 diff --git a/src/static/images/2025/third-parties/top-3p-by-num-pages.png b/src/static/images/2025/third-parties/top-3p-by-num-pages.png new file mode 100644 index 0000000000000000000000000000000000000000..beb7eaf569832ea091b8ace755c72c1879aaa299 GIT binary patch literal 35470 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 lhY9@+YEKdLc literal 0 HcmV?d00001 From d30ed0d597f132a10dbcb538b62f5a2bc826514a Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sat, 3 Jan 2026 12:56:35 +0000 Subject: [PATCH 15/37] Lint SQL --- .../consent_signal_basic_analysis.sql | 36 +++++------ ...nal_prevalence_by_third_party_category.sql | 26 ++++---- ...survival_rate_through_chains_optimized.sql | 52 +++++++-------- ...signal_survival_rate_through_redirects.sql | 36 +++++------ ...vival_rate_through_redirects_optimized.sql | 64 +++++++++---------- ...urvival_rate_through_redirects_working.sql | 44 ++++++------- ...nals_by_parameter_and_domain_optimized.sql | 20 +++--- ...ribution_of_length_of_inclusion_chains.sql | 6 +- ...of_websites_by_number_of_third_parties.sql | 1 - .../length_of_chain_by_intiator.sql | 6 +- ...f_websites_with_third_party_by_ranking.sql | 3 +- ...nals_in_third_party_requests_optimized.sql | 18 +++--- 12 files changed, 155 insertions(+), 157 deletions(-) diff --git a/sql/2025/third-parties/consent_signal_basic_analysis.sql b/sql/2025/third-parties/consent_signal_basic_analysis.sql index 9ca789bcf11..50a8b0e179f 100644 --- a/sql/2025/third-parties/consent_signal_basic_analysis.sql +++ b/sql/2025/third-parties/consent_signal_basic_analysis.sql @@ -9,8 +9,8 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' - AND rank <= 50000 -- Expand to top 50K sites + date = '2025-06-01' AND + rank <= 50000 -- Expand to top 50K sites ), -- Find requests with consent signals (no redirect filtering) @@ -21,15 +21,15 @@ consent_requests AS ( r.url, NET.REG_DOMAIN(r.page) AS page_domain, NET.REG_DOMAIN(r.url) AS url_domain, - + -- Extract consent signals REGEXP_CONTAINS(r.url, r'[?&]us_privacy=') AS has_usp_standard, REGEXP_CONTAINS(r.url, r'[?&](ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string)=') AS has_usp_nonstandard, REGEXP_CONTAINS(r.url, r'[?&](gdpr|gdpr_consent|gdpr_pd)=') AS has_tcf_standard, REGEXP_CONTAINS(r.url, r'[?&](gpp|gpp_sid)=') AS has_gpp_standard, - + -- Check if request has redirects - JSON_EXTRACT(r.summary, '$.redirects') IS NOT NULL AND + JSON_EXTRACT(r.summary, '$.redirects') IS NOT NULL AND TO_JSON_STRING(JSON_EXTRACT(r.summary, '$.redirects')) != '[]' AS has_redirects FROM `httparchive.crawl.requests` r @@ -38,9 +38,9 @@ consent_requests AS ( ON r.client = p.client AND r.page = p.page WHERE - r.date = '2025-06-01' - AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only - AND ( + r.date = '2025-06-01' AND + NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) AND -- Third-party only + ( REGEXP_CONTAINS(r.url, r'[?&]us_privacy=') OR REGEXP_CONTAINS(r.url, r'[?&](ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string)=') OR REGEXP_CONTAINS(r.url, r'[?&](gdpr|gdpr_consent|gdpr_pd)=') OR @@ -60,27 +60,27 @@ requests_with_signals AS ( -- Basic analysis SELECT client, - + -- Overall counts - COUNT(*) AS total_requests_with_consent_signals, + COUNT(0) AS total_requests_with_consent_signals, COUNT(DISTINCT page) AS total_pages_with_consent_signals, COUNT(DISTINCT url_domain) AS total_domains_with_consent_signals, - + -- Signal type breakdown COUNTIF(has_usp_standard) AS usp_standard_requests, COUNTIF(has_usp_nonstandard) AS usp_nonstandard_requests, COUNTIF(has_tcf_standard) AS tcf_standard_requests, COUNTIF(has_gpp_standard) AS gpp_standard_requests, - + -- Percentage breakdown - COUNTIF(has_usp_standard) / COUNT(*) AS pct_usp_standard, - COUNTIF(has_usp_nonstandard) / COUNT(*) AS pct_usp_nonstandard, - COUNTIF(has_tcf_standard) / COUNT(*) AS pct_tcf_standard, - COUNTIF(has_gpp_standard) / COUNT(*) AS pct_gpp_standard, - + COUNTIF(has_usp_standard) / COUNT(0) AS pct_usp_standard, + COUNTIF(has_usp_nonstandard) / COUNT(0) AS pct_usp_nonstandard, + COUNTIF(has_tcf_standard) / COUNT(0) AS pct_tcf_standard, + COUNTIF(has_gpp_standard) / COUNT(0) AS pct_gpp_standard, + -- Redirect availability COUNTIF(has_redirects) AS requests_with_redirects, - COUNTIF(has_redirects) / COUNT(*) AS pct_requests_with_redirects + COUNTIF(has_redirects) / COUNT(0) AS pct_requests_with_redirects FROM requests_with_signals diff --git a/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql b/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql index 1d6e9eca25a..27e90d7d5ce 100644 --- a/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql +++ b/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql @@ -51,7 +51,7 @@ category_totals AS ( r.client, rank_grouping, tp.category, - COUNT(*) AS total_category_requests, + COUNT(0) AS total_category_requests, COUNT(DISTINCT r.page) AS total_category_pages, COUNT(DISTINCT tp.canonicalDomain) AS total_category_domains FROM @@ -64,7 +64,7 @@ category_totals AS ( third_party tp ON NET.HOST(r.url) = NET.HOST(tp.domain), - UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE p.rank <= rank_grouping GROUP BY @@ -82,13 +82,13 @@ consent_signals_by_category AS ( tp.canonicalDomain, r.page, r.url, - + -- Single-pass consent signal detection REGEXP_CONTAINS(r.url, r'[?&]us_privacy=') AS has_usp_standard, REGEXP_CONTAINS(r.url, r'[?&](ccpa|usp_consent|uspString|sst\.us_privacy|uspConsent|ccpa_consent|AV_CCPA|usp|usprivacy|_fw_us_privacy|D9v\.us_privacy|cnsnt|ccpaconsent|usp_string)=') AS has_usp_nonstandard, REGEXP_CONTAINS(r.url, r'[?&](gdpr|gdpr_consent|gdpr_pd)=') AS has_tcf_standard, REGEXP_CONTAINS(r.url, r'[?&](gpp|gpp_sid)=') AS has_gpp_standard - + FROM requests r INNER JOIN @@ -99,11 +99,11 @@ consent_signals_by_category AS ( third_party tp ON NET.HOST(r.url) = NET.HOST(tp.domain), - UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE - p.rank <= rank_grouping + p.rank <= rank_grouping AND -- Pre-filter: only process URLs that might contain consent-related parameters - AND REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|sst\.us_privacy|uspConsent|ccpa_consent|AV_CCPA|usp|usprivacy|_fw_us_privacy|D9v\.us_privacy|cnsnt|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') + REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|sst\.us_privacy|uspConsent|ccpa_consent|AV_CCPA|usp|usprivacy|_fw_us_privacy|D9v\.us_privacy|cnsnt|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') ), -- Add computed flag for any consent signal @@ -121,32 +121,32 @@ category_signal_aggregates AS ( client, rank_grouping, category, - + -- USP Standard metrics COUNTIF(has_usp_standard) AS usp_standard_requests, COUNT(DISTINCT CASE WHEN has_usp_standard THEN page END) AS usp_standard_pages, COUNT(DISTINCT CASE WHEN has_usp_standard THEN canonicalDomain END) AS usp_standard_domains, - + -- USP Non-Standard metrics COUNTIF(has_usp_nonstandard) AS usp_nonstandard_requests, COUNT(DISTINCT CASE WHEN has_usp_nonstandard THEN page END) AS usp_nonstandard_pages, COUNT(DISTINCT CASE WHEN has_usp_nonstandard THEN canonicalDomain END) AS usp_nonstandard_domains, - + -- TCF Standard metrics COUNTIF(has_tcf_standard) AS tcf_standard_requests, COUNT(DISTINCT CASE WHEN has_tcf_standard THEN page END) AS tcf_standard_pages, COUNT(DISTINCT CASE WHEN has_tcf_standard THEN canonicalDomain END) AS tcf_standard_domains, - + -- GPP Standard metrics COUNTIF(has_gpp_standard) AS gpp_standard_requests, COUNT(DISTINCT CASE WHEN has_gpp_standard THEN page END) AS gpp_standard_pages, COUNT(DISTINCT CASE WHEN has_gpp_standard THEN canonicalDomain END) AS gpp_standard_domains, - + -- Any consent signal metrics COUNTIF(has_any_consent_signal) AS any_consent_requests, COUNT(DISTINCT CASE WHEN has_any_consent_signal THEN page END) AS any_consent_pages, COUNT(DISTINCT CASE WHEN has_any_consent_signal THEN canonicalDomain END) AS any_consent_domains, - + -- Totals for this filtered dataset COUNT(0) AS total_filtered_requests FROM diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql index 69447ea5273..ee543dc5590 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql @@ -4,7 +4,7 @@ CREATE TEMP FUNCTION extractConsentSignals(url STRING) RETURNS STRUCT< has_usp_standard BOOL, - has_usp_nonstandard BOOL, + has_usp_nonstandard BOOL, has_tcf_standard BOOL, has_gpp_standard BOOL, has_any_signal BOOL @@ -43,8 +43,8 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' - AND rank <= 10000 -- Aggressive filtering: top 10K only + date = '2025-06-01' AND + rank <= 10000 -- Aggressive filtering: top 10K only ), -- Pre-filter to only requests with consent signals or initiator info @@ -64,12 +64,12 @@ filtered_requests AS ( ON r.client = p.client AND r.page = p.page WHERE - r.date = '2025-06-01' - AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only - AND ( + r.date = '2025-06-01' AND + NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) AND -- Third-party only + ( -- Only process requests with consent signals OR that are part of chains - REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') - OR JSON_VALUE(r.payload, '$._initiator') IS NOT NULL + REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') OR + JSON_VALUE(r.payload, '$._initiator') IS NOT NULL ) ), @@ -80,12 +80,12 @@ step_1_requests AS ( root_page, third_party, consent_signals, - COUNT(*) as step1_count + COUNT(0) AS step1_count FROM filtered_requests WHERE - initiator_etld = root_page -- Direct first-party to third-party requests - AND consent_signals.has_any_signal = true + initiator_etld = root_page AND -- Direct first-party to third-party requests + consent_signals.has_any_signal = TRUE GROUP BY client, root_page, @@ -101,15 +101,15 @@ step_2_requests AS ( fr.third_party AS step2_party, s1.consent_signals AS step1_signals, fr.consent_signals AS step2_signals, - COUNT(*) as step2_count + COUNT(0) AS step2_count FROM filtered_requests fr INNER JOIN step_1_requests s1 ON - fr.client = s1.client - AND fr.root_page = s1.root_page - AND fr.initiator_etld = s1.third_party -- Third-party chain + fr.client = s1.client AND + fr.root_page = s1.root_page AND + fr.initiator_etld = s1.third_party -- Third-party chain GROUP BY fr.client, s1.root_page, @@ -124,14 +124,14 @@ step_1_stats AS ( SELECT client, 1 AS step_number, - + COUNTIF(consent_signals.has_usp_standard) AS usp_standard_count, COUNTIF(consent_signals.has_usp_nonstandard) AS usp_nonstandard_count, COUNTIF(consent_signals.has_tcf_standard) AS tcf_standard_count, COUNTIF(consent_signals.has_gpp_standard) AS gpp_standard_count, COUNTIF(consent_signals.has_any_signal) AS any_signal_count, - - COUNT(*) AS total_requests, + + COUNT(0) AS total_requests, COUNT(DISTINCT root_page) AS total_pages FROM step_1_requests @@ -143,14 +143,14 @@ step_2_stats AS ( SELECT client, 2 AS step_number, - + COUNTIF(step2_signals.has_usp_standard) AS usp_standard_count, COUNTIF(step2_signals.has_usp_nonstandard) AS usp_nonstandard_count, COUNTIF(step2_signals.has_tcf_standard) AS tcf_standard_count, COUNTIF(step2_signals.has_gpp_standard) AS gpp_standard_count, COUNTIF(step2_signals.has_any_signal) AS any_signal_count, - - COUNT(*) AS total_requests, + + COUNT(0) AS total_requests, COUNT(DISTINCT root_page) AS total_pages FROM step_2_requests @@ -186,20 +186,20 @@ SELECT cs.step_number, cs.total_requests, cs.total_pages, - + -- Signal counts and survival rates cs.usp_standard_count, SAFE_DIVIDE(cs.usp_standard_count, b.usp_standard_baseline) AS usp_standard_survival_rate, - + cs.usp_nonstandard_count, SAFE_DIVIDE(cs.usp_nonstandard_count, b.usp_nonstandard_baseline) AS usp_nonstandard_survival_rate, - + cs.tcf_standard_count, SAFE_DIVIDE(cs.tcf_standard_count, b.tcf_standard_baseline) AS tcf_standard_survival_rate, - + cs.gpp_standard_count, SAFE_DIVIDE(cs.gpp_standard_count, b.gpp_standard_baseline) AS gpp_standard_survival_rate, - + cs.any_signal_count, SAFE_DIVIDE(cs.any_signal_count, b.any_signal_baseline) AS any_signal_survival_rate diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql index 342341c0325..769a10c729b 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql @@ -4,7 +4,7 @@ CREATE TEMP FUNCTION extractConsentSignals(url STRING) RETURNS STRUCT< has_usp_standard BOOL, - has_usp_nonstandard BOOL, + has_usp_nonstandard BOOL, has_tcf_standard BOOL, has_gpp_standard BOOL, has_any_signal BOOL, @@ -69,11 +69,11 @@ redirect_chains AS ( ON r.client = p.client AND r.page = p.page WHERE - r.date = '2025-06-01' - AND JSON_EXTRACT(r.summary, '$.redirects') IS NOT NULL - AND JSON_EXTRACT(r.summary, '$.redirects') != '[]' + r.date = '2025-06-01' AND + JSON_EXTRACT(r.summary, '$.redirects') IS NOT NULL AND + JSON_EXTRACT(r.summary, '$.redirects') != '[]' AND -- AND p.rank <= 100000 -- Limit to top 100K sites - AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only + NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only ), -- Parse redirect chains and extract consent signals at each step @@ -86,7 +86,7 @@ parsed_redirects AS ( redirect_step.url AS step_url, redirect_step.redirectURL AS next_url, ROW_NUMBER() OVER ( - PARTITION BY client, page, final_url + PARTITION BY client, page, final_url ORDER BY COALESCE(redirect_step.startedDateTime, redirect_chains.startedDateTime) ) AS redirect_step_number, extractConsentSignals(redirect_step.url) AS step_signals, @@ -117,9 +117,9 @@ redirect_chains_with_final AS ( 'redirect' AS step_type FROM parsed_redirects - + UNION ALL - + -- Final destination URL SELECT client, @@ -145,18 +145,18 @@ step_survival_stats AS ( client, redirect_step_number, step_type, - + -- Signals present at this step COUNTIF(signals_at_step.has_usp_standard) AS usp_standard_at_step, COUNTIF(signals_at_step.has_usp_nonstandard) AS usp_nonstandard_at_step, COUNTIF(signals_at_step.has_tcf_standard) AS tcf_standard_at_step, COUNTIF(signals_at_step.has_gpp_standard) AS gpp_standard_at_step, COUNTIF(signals_at_step.has_any_signal) AS any_signal_at_step, - + -- Average signal count per URL AVG(signals_at_step.signal_count) AS avg_signal_count_at_step, - - COUNT(*) AS total_urls_at_step, + + COUNT(0) AS total_urls_at_step, COUNT(DISTINCT page) AS total_pages_at_step FROM redirect_chains_with_final @@ -191,23 +191,23 @@ SELECT ss.step_type, ss.total_urls_at_step, ss.total_pages_at_step, - + -- Signal counts and survival rates ss.usp_standard_at_step, SAFE_DIVIDE(ss.usp_standard_at_step, bs.usp_standard_baseline) AS usp_standard_survival_rate, - + ss.usp_nonstandard_at_step, SAFE_DIVIDE(ss.usp_nonstandard_at_step, bs.usp_nonstandard_baseline) AS usp_nonstandard_survival_rate, - + ss.tcf_standard_at_step, SAFE_DIVIDE(ss.tcf_standard_at_step, bs.tcf_standard_baseline) AS tcf_standard_survival_rate, - + ss.gpp_standard_at_step, SAFE_DIVIDE(ss.gpp_standard_at_step, bs.gpp_standard_baseline) AS gpp_standard_survival_rate, - + ss.any_signal_at_step, SAFE_DIVIDE(ss.any_signal_at_step, bs.any_signal_baseline) AS any_signal_survival_rate, - + -- Average signal degradation ss.avg_signal_count_at_step, bs.avg_signal_count_baseline, diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql index 304fe587ac3..c3a5f121dcd 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql @@ -4,7 +4,7 @@ CREATE TEMP FUNCTION extractConsentSignals(url STRING) RETURNS STRUCT< has_usp_standard BOOL, - has_usp_nonstandard BOOL, + has_usp_nonstandard BOOL, has_tcf_standard BOOL, has_gpp_standard BOOL, has_any_signal BOOL, @@ -49,8 +49,8 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' - AND rank <= 100000 -- Expanded to top 100K sites + date = '2025-06-01' AND + rank <= 100000 -- Expanded to top 100K sites ), -- Pre-filter requests with redirects and potential consent signals @@ -68,14 +68,14 @@ requests_with_redirects AS ( ON r.client = p.client AND r.page = p.page WHERE - r.date = '2025-06-01' - AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only - AND JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') IS NOT NULL - AND JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') != '' - AND ( + r.date = '2025-06-01' AND + NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) AND -- Third-party only + JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') IS NOT NULL AND + JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') != '' AND + ( -- Pre-filter: only URLs with consent signals in final URL or redirect URL - REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') - OR REGEXP_CONTAINS(JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl'), r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') + REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') OR + REGEXP_CONTAINS(JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl'), r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') ) ), @@ -86,17 +86,17 @@ redirect_steps AS ( page, final_url, final_domain, - + -- Step 1: Original redirect URL (before redirect) redirect_url AS step1_url, - + -- Step 2: Final URL (after redirect) final_url AS step2_url FROM requests_with_redirects WHERE - redirect_url IS NOT NULL - AND redirect_url != '' + redirect_url IS NOT NULL AND + redirect_url != '' ), -- Extract consent signals for each step @@ -105,11 +105,11 @@ signals_by_step AS ( client, page, final_domain, - + -- Step 1 signals (original redirect URL) step1_url, extractConsentSignals(step1_url) AS step1_signals, - + -- Step 2 signals (final URL after redirect) step2_url, extractConsentSignals(step2_url) AS step2_signals @@ -126,44 +126,44 @@ step_aggregations AS ( client, 1 AS redirect_step, 'original' AS step_type, - + COUNTIF(step1_signals.has_usp_standard) AS usp_standard_count, COUNTIF(step1_signals.has_usp_nonstandard) AS usp_nonstandard_count, COUNTIF(step1_signals.has_tcf_standard) AS tcf_standard_count, COUNTIF(step1_signals.has_gpp_standard) AS gpp_standard_count, COUNTIF(step1_signals.has_any_signal) AS any_signal_count, - + AVG(step1_signals.signal_count) AS avg_signal_count, - COUNT(*) AS total_urls, + COUNT(0) AS total_urls, COUNT(DISTINCT page) AS total_pages FROM signals_by_step WHERE - step1_signals.has_any_signal = true -- Only analyze chains that start with signals + step1_signals.has_any_signal = TRUE -- Only analyze chains that start with signals GROUP BY client - + UNION ALL - + -- Step 2 stats (final URL after redirect) SELECT client, 2 AS redirect_step, 'final' AS step_type, - + COUNTIF(step2_signals.has_usp_standard) AS usp_standard_count, COUNTIF(step2_signals.has_usp_nonstandard) AS usp_nonstandard_count, COUNTIF(step2_signals.has_tcf_standard) AS tcf_standard_count, COUNTIF(step2_signals.has_gpp_standard) AS gpp_standard_count, COUNTIF(step2_signals.has_any_signal) AS any_signal_count, - + AVG(step2_signals.signal_count) AS avg_signal_count, - COUNT(*) AS total_urls, + COUNT(0) AS total_urls, COUNT(DISTINCT page) AS total_pages FROM signals_by_step WHERE - step1_signals.has_any_signal = true -- Same baseline + step1_signals.has_any_signal = TRUE -- Same baseline GROUP BY client ), @@ -191,23 +191,23 @@ SELECT sa.step_type, sa.total_urls, sa.total_pages, - + -- Signal survival rates sa.usp_standard_count, SAFE_DIVIDE(sa.usp_standard_count, b.usp_standard_baseline) AS usp_standard_survival_rate, - + sa.usp_nonstandard_count, SAFE_DIVIDE(sa.usp_nonstandard_count, b.usp_nonstandard_baseline) AS usp_nonstandard_survival_rate, - + sa.tcf_standard_count, SAFE_DIVIDE(sa.tcf_standard_count, b.tcf_standard_baseline) AS tcf_standard_survival_rate, - + sa.gpp_standard_count, SAFE_DIVIDE(sa.gpp_standard_count, b.gpp_standard_baseline) AS gpp_standard_survival_rate, - + sa.any_signal_count, SAFE_DIVIDE(sa.any_signal_count, b.any_signal_baseline) AS any_signal_survival_rate, - + -- Signal count preservation sa.avg_signal_count, b.avg_signal_count_baseline, diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql index 7b810104590..ee735bdccac 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql @@ -4,7 +4,7 @@ CREATE TEMP FUNCTION extractConsentSignals(url STRING) RETURNS STRUCT< has_usp_standard BOOL, - has_usp_nonstandard BOOL, + has_usp_nonstandard BOOL, has_tcf_standard BOOL, has_gpp_standard BOOL, has_any_signal BOOL, @@ -49,8 +49,8 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' - AND rank <= 100000 -- Expanded to top 100K sites + date = '2025-06-01' AND + rank <= 100000 -- Expanded to top 100K sites ), -- First, find all third-party requests with consent signals (regardless of redirects) @@ -67,9 +67,9 @@ initial_consent_requests AS ( ON r.client = p.client AND r.page = p.page WHERE - r.date = '2025-06-01' - AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only - AND REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') + r.date = '2025-06-01' AND + NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) AND -- Third-party only + REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') ), -- Now look for those same requests that ALSO have redirect chains @@ -86,13 +86,13 @@ requests_with_redirects AS ( INNER JOIN `httparchive.crawl.requests` r ON - icr.client = r.client - AND icr.page = r.page - AND icr.url = r.url + icr.client = r.client AND + icr.page = r.page AND + icr.url = r.url WHERE - r.date = '2025-06-01' - AND JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') IS NOT NULL - AND JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') != '' + r.date = '2025-06-01' AND + JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') IS NOT NULL AND + JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') != '' ), -- Parse redirect chains (simplified - just look at redirect URL and final URL) @@ -102,17 +102,17 @@ parsed_redirects AS ( page, url AS final_url, url_signals AS final_signals, - + -- Extract redirect URL redirect_url AS redirect_url, - + -- Simple redirect count (1 = single redirect) CASE WHEN redirect_url IS NOT NULL AND redirect_url != '' THEN 1 ELSE 0 END AS redirect_count FROM requests_with_redirects WHERE - redirect_url IS NOT NULL - AND redirect_url != '' + redirect_url IS NOT NULL AND + redirect_url != '' ), -- Extract signals from redirect steps @@ -134,28 +134,28 @@ redirect_analysis AS ( -- Final analysis comparing signals across redirect steps SELECT client, - + -- Overall statistics - COUNT(*) AS total_redirect_chains_with_consent, + COUNT(0) AS total_redirect_chains_with_consent, COUNT(DISTINCT page) AS pages_with_redirect_chains, AVG(redirect_count) AS avg_redirect_count, - + -- Step 1 (redirect URL) signal analysis COUNTIF(redirect_signals.has_any_signal) AS step1_requests_with_signals, COUNTIF(redirect_signals.has_usp_standard) AS step1_usp_standard, COUNTIF(redirect_signals.has_usp_nonstandard) AS step1_usp_nonstandard, COUNTIF(redirect_signals.has_tcf_standard) AS step1_tcf_standard, COUNTIF(redirect_signals.has_gpp_standard) AS step1_gpp_standard, - + -- Final URL signal analysis COUNTIF(final_signals.has_any_signal) AS final_requests_with_signals, COUNTIF(final_signals.has_usp_standard) AS final_usp_standard, COUNTIF(final_signals.has_usp_nonstandard) AS final_usp_nonstandard, COUNTIF(final_signals.has_tcf_standard) AS final_tcf_standard, COUNTIF(final_signals.has_gpp_standard) AS final_gpp_standard, - + -- Survival rates (what percentage of signals make it to final URL) - SAFE_DIVIDE(COUNTIF(final_signals.has_any_signal), COUNT(*)) AS overall_signal_survival_rate, + SAFE_DIVIDE(COUNTIF(final_signals.has_any_signal), COUNT(0)) AS overall_signal_survival_rate, SAFE_DIVIDE(COUNTIF(final_signals.has_usp_standard), COUNTIF(redirect_signals.has_usp_standard)) AS usp_standard_survival_rate, SAFE_DIVIDE(COUNTIF(final_signals.has_tcf_standard), COUNTIF(redirect_signals.has_tcf_standard)) AS tcf_standard_survival_rate, SAFE_DIVIDE(COUNTIF(final_signals.has_gpp_standard), COUNTIF(redirect_signals.has_gpp_standard)) AS gpp_standard_survival_rate diff --git a/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql b/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql index 14da4eaf493..cccbd604c33 100644 --- a/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql +++ b/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql @@ -20,9 +20,9 @@ requests AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-06-01' + date = '2025-06-01' AND -- Pre-filter: only process URLs that contain consent-related parameters - AND REGEXP_CONTAINS(url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') + REGEXP_CONTAINS(url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') ), third_party AS ( @@ -66,7 +66,7 @@ parameter_extraction AS ( third_party tp ON NET.HOST(r.url) = NET.HOST(tp.domain), - UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE p.rank <= rank_grouping ), @@ -92,7 +92,7 @@ parameter_counts AS ( category, rank_grouping, param, - COUNT(*) AS param_count, + COUNT(0) AS param_count, COUNT(DISTINCT CONCAT(client, canonicalDomain)) AS domain_count FROM flattened_parameters @@ -109,7 +109,7 @@ totals AS ( SELECT r.client, rank_grouping, - COUNT(*) AS total_all_requests + COUNT(0) AS total_all_requests FROM `httparchive.crawl.requests` r INNER JOIN @@ -120,10 +120,10 @@ totals AS ( third_party tp ON NET.HOST(r.url) = NET.HOST(tp.domain), - UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE - r.date = '2025-06-01' - AND p.rank <= rank_grouping + r.date = '2025-06-01' AND + p.rank <= rank_grouping GROUP BY r.client, rank_grouping @@ -135,7 +135,7 @@ categorized_params AS ( client, rank_grouping, param, - CASE + CASE WHEN param = 'us_privacy' THEN 'USP Standard' WHEN param IN ('ccpa', 'usp_consent', 'uspString', 'uspConsent', 'ccpa_consent', 'usp', 'usprivacy', 'ccpaconsent', 'usp_string') THEN 'USP Non-Standard' WHEN param IN ('gdpr', 'gdpr_consent', 'gdpr_pd') THEN 'TCF Standard' @@ -198,4 +198,4 @@ ORDER BY client, rank_grouping, total_requests DESC -LIMIT 1000 \ No newline at end of file +LIMIT 1000 diff --git a/sql/2025/third-parties/distribution_of_length_of_inclusion_chains.sql b/sql/2025/third-parties/distribution_of_length_of_inclusion_chains.sql index ace78271aad..236635c1749 100644 --- a/sql/2025/third-parties/distribution_of_length_of_inclusion_chains.sql +++ b/sql/2025/third-parties/distribution_of_length_of_inclusion_chains.sql @@ -48,10 +48,10 @@ WITH data AS ( GROUP BY client, root_page, third_party, initiator_etld ) -SELECT +SELECT client, ARRAY_LENGTH(all_initiators) AS chain_length, - COUNT(*) AS pages_with_this_length + COUNT(0) AS pages_with_this_length FROM ( SELECT root_page, @@ -62,4 +62,4 @@ FROM ( ) WHERE ARRAY_LENGTH(all_initiators) > 0 GROUP BY client, chain_length -ORDER BY client, chain_length; \ No newline at end of file +ORDER BY client, chain_length; diff --git a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql index 9b1165ac09e..e10c6f4e731 100644 --- a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql +++ b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql @@ -13,7 +13,6 @@ WITH requests AS ( date = '2025-06-01' ), - third_party AS ( SELECT domain, diff --git a/sql/2025/third-parties/length_of_chain_by_intiator.sql b/sql/2025/third-parties/length_of_chain_by_intiator.sql index fa36b7b0224..aa491264e3d 100644 --- a/sql/2025/third-parties/length_of_chain_by_intiator.sql +++ b/sql/2025/third-parties/length_of_chain_by_intiator.sql @@ -46,12 +46,12 @@ WITH data AS ( ) -- Add this to the final SELECT to see top initiators by chain length -SELECT +SELECT client, first_initiator, AVG(ARRAY_LENGTH(all_initiators)) AS avg_chain_length, MAX(ARRAY_LENGTH(all_initiators)) AS max_chain_length, - COUNT(*) AS pages + COUNT(0) AS pages FROM ( SELECT root_page, @@ -69,4 +69,4 @@ FROM ( WHERE ARRAY_LENGTH(all_initiators) > 0 ) GROUP BY client, first_initiator -ORDER BY avg_chain_length DESC; \ No newline at end of file +ORDER BY avg_chain_length DESC; diff --git a/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql b/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql index 4558d541b09..d0d4e7d8441 100644 --- a/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql +++ b/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql @@ -12,7 +12,6 @@ WITH requests AS ( date = '2025-06-01' ), - third_party AS ( SELECT domain, @@ -40,7 +39,7 @@ pages AS ( rank FROM `httparchive.crawl.pages` - WHERE + WHERE date = '2025-06-01' ) diff --git a/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql b/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql index 0a5334ddd83..39c27b0425c 100644 --- a/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql +++ b/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql @@ -57,7 +57,7 @@ totals AS ( pages p ON r.client = p.client AND r.page = p.page, - UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE p.rank <= rank_grouping GROUP BY @@ -83,7 +83,7 @@ third_party_requests AS ( third_party tp ON NET.HOST(r.url) = NET.HOST(tp.domain), - UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE p.rank <= rank_grouping ), @@ -97,10 +97,10 @@ consent_signals AS ( canonicalDomain, category, rank_grouping, - + -- Extract all consent parameters in one pass REGEXP_EXTRACT_ALL(url, r'[?&](us_privacy|ccpa|usp_consent|uspString|sst\.us_privacy|uspConsent|ccpa_consent|AV_CCPA|usp|usprivacy|_fw_us_privacy|D9v\.us_privacy|cnsnt|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') AS found_params, - + -- Boolean flags derived from the extracted parameters (computed once) REGEXP_CONTAINS(url, r'[?&]us_privacy=') AS has_usp_standard, REGEXP_CONTAINS(url, r'[?&](ccpa|usp_consent|uspString|sst\.us_privacy|uspConsent|ccpa_consent|AV_CCPA|usp|usprivacy|_fw_us_privacy|D9v\.us_privacy|cnsnt|ccpaconsent|usp_string)=') AS has_usp_nonstandard, @@ -131,27 +131,27 @@ signal_aggregates AS ( COUNTIF(has_usp_standard) AS usp_standard_requests, COUNT(DISTINCT CASE WHEN has_usp_standard THEN page END) AS usp_standard_pages, COUNT(DISTINCT CASE WHEN has_usp_standard THEN canonicalDomain END) AS usp_standard_domains, - + -- USP Non-Standard metrics COUNTIF(has_usp_nonstandard) AS usp_nonstandard_requests, COUNT(DISTINCT CASE WHEN has_usp_nonstandard THEN page END) AS usp_nonstandard_pages, COUNT(DISTINCT CASE WHEN has_usp_nonstandard THEN canonicalDomain END) AS usp_nonstandard_domains, - + -- TCF Standard metrics COUNTIF(has_tcf_standard) AS tcf_standard_requests, COUNT(DISTINCT CASE WHEN has_tcf_standard THEN page END) AS tcf_standard_pages, COUNT(DISTINCT CASE WHEN has_tcf_standard THEN canonicalDomain END) AS tcf_standard_domains, - + -- GPP Standard metrics COUNTIF(has_gpp_standard) AS gpp_standard_requests, COUNT(DISTINCT CASE WHEN has_gpp_standard THEN page END) AS gpp_standard_pages, COUNT(DISTINCT CASE WHEN has_gpp_standard THEN canonicalDomain END) AS gpp_standard_domains, - + -- Any consent signal metrics COUNTIF(has_any_consent_signal) AS any_consent_requests, COUNT(DISTINCT CASE WHEN has_any_consent_signal THEN page END) AS any_consent_pages, COUNT(DISTINCT CASE WHEN has_any_consent_signal THEN canonicalDomain END) AS any_consent_domains, - + -- Totals for this filtered dataset COUNT(0) AS total_third_party_requests FROM From c859d0b63a1e1d5cf36681f7539f57e9a3b26d3e Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sat, 3 Jan 2026 12:57:03 +0000 Subject: [PATCH 16/37] Retake image --- .../third-parties/top-3p-by-num-pages.png | Bin 35470 -> 37263 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/static/images/2025/third-parties/top-3p-by-num-pages.png b/src/static/images/2025/third-parties/top-3p-by-num-pages.png index beb7eaf569832ea091b8ace755c72c1879aaa299..ee4c6cc702534fe5bca2f0381546db71ad0fbed9 100644 GIT binary patch literal 37263 zcmd42bx>PT7cUyz9RkH23W4Gd#hqd$1d4@1fkJVL6k6P!60B%(0t9yp?jD?CEe^#B z1xhddzW3(7d2i<4nfKQ{lbP(ZWv%sFYv;H2&eJ}o`~03gy(S2h3uFcAO% z#ttsVV@JlYznE?A(Ig{o|3* zvEHGn$*Jke>YC56jG5WFm6g@wlQVb~A}c#@|KOmbvwLH6b8324*UV%6+eU3me@aeu zLu2#q?%v!y>frEj`}={5|L5i9mAAf0i>q6G1A|9LN3HE0i%ZK{g|!KpmC>nXbq!6O z-AM1Cly6%<21e&TMrK@I{hF9r<`gpsj?L@ppMuwQot&I@Aje&N<5s`zjE;?eEpIAC zbWP8%{yaM$8=p|rbLj0K2#C&g4@|T7iaR(uO-wIt>KHDn>exR#2~8{%Ry2pc3)WrdeM%SSyyk6{M{=KU$7E<^uU z{Qq}Z{hG)YPh!CDF4-6wuUBC0vcgwZi71U!P6^+IlZKE+va%>Ls~~WrTDGPESojX- zE-vrw21sPY5>}61INuw!L0>Z^By`otzCl?D&=CIBdTYY*^oE3GBW2`vwNyz1Dd|}! zhj-&2$=P>UQ;SBb_Mqw+Ht!jHKi>HyK+eXv-y$XI@zy`)G8r{qdV@QRs4h$6Di| z#*NMrF(5bd*bp;G=+5n+>@VZhD;B~LVs+e4Y}N}V5xeED-eQKUzXz_&1k_a|#4C(k z?4$nFn7qW*i~hK28`wKj*~sx*{fWveNYT~O`^8v~*~*`DAFCb?roUTTP*m{zNOSJW zpsI$gqT4QHto?0H?hWkRqo;ma?R&_b)imWQ!eyt)eLh6s*r~`QPFJIF87mhTZ`p&u z1qgT}nNxV6HSRMZu6}8+`8D`ucU&9I1NUz6xeel$ds(lE4LE&YaI$s6o=)S(SoC4t zTL*}=WQ6xan5UF-DVIXAWGK4c6Q*MT>IL$M0aVLg8AglLDuJe=aQ5n`c(=CinZ^h5 zfLouwBI>w;;ZiJoZF%Kf=NQKG*yu0MdlUS;XbKXF?1Nel3EkKYAce=wy47Bv6^shU z;4z`1O3~ta1Qkr!t{92wZb@H&;P5gB8qPC$Q}hoikyWSkPzFD41i_`+E%(f!edu=v zs>DIP=ghcp{AuLKi{={VY+jIDpEb1KtB_xcX_Ol0tFAJXugGS`_Ok`#)>CVBQ}8uz z`uX!hq(OuHnpZdtIS1T^IKh=zB!pel4qvOdkQ`PT!{fde%CWupGbT8K{26;|r*P=} z;OlUVew=lA4v;ULQ)crJ;3>4EkE><>^QInQ6OQzH`t~aZpmyLjDil8^qR})>-w(P! z8>g*@)aZ5jMH5!!^bhYDn6na*O7yD$;D0 zqx_Or_zerLmPbT+xv;%=17f)ru|r93{uK11@0A!_OPHtH!X}_3%!qD=ys?T@7bAd? zI7~3E*gppW#&e#l56Xf#dm^nDnSem@cb-ay+Mkf4Y!k3=h%7&UrQ=CqZ;Li2VP42^X`#CEz*yTYi9-ZyUAUM5V{gWapnAjS&r$X9Ji5it42}(MF0#=L_T_w!LXB`QGi%X#> z0Eo>t(cYIzd18wF^nr744+ZSrHkgw8K(9voMLSI1hBlPjWzogd=~NTi_{Jl`#pF|R z#a6)sSqJ>P-}|y1q-*6$=XvHVPp4D1No9)_<&LF6hsLvh)XA9sb4Yi`pClbXt&cUo zNoL1sKO@}-_D7P(NZYA&(Ab%9x`~cB%U)7tmr1Mse zIfIn4oTGYIJb%0Z_lf|zbrLMUOKV%2VNA_5${$HxgbH*HAv33=k`Qb^k#jeX|O`nv5 zg4YgV4XpPn{u}0>?Rc}=O(8lK%5}nFsoT;3x_c_d!-Ch#8i9I#;nXM~d;ODspewVR zMJv^S^iI(yHYaNtA*h2X>EmRAuo5`3o=C2H$mWo_=vP_uR8xS1K3V-O2cl@vHIeHV zoAn48H%70(z@ARs1*%#E%C#8r!?SDj57Da+BhpR1ccE`+8pC34kk5C$_eI^4HK9a& z0OmCOiXLwga=_kHLj5$KgVv{u?p^fSa^=g%_@eeb+sd5Y_ijIPSVZ2Ep4i)F0=Zi! zNR`;3Nne@T-O7g}ho?xSuhF43bF%~$3U%h(Pb;W_qjs!78)M@k7J>@5DnyQ8rp8X) znGvn>XXi5own?IOYnRQpBPytFX{3)hEK zQ!^I`CJxna1~ALoXrHi!8A%vJuDN^pkuu&}_hFu7cF5Bvq~OaULtqi2{4Mkt4Uoec zW`x4kqRR#aG)@_wyg3loL-bAo&6U#6Bw2@wO%S zY;G6HP>Xnv^bi)Z`7`(_le9q$qqz7jaSkA9DhuTnm~(g4I3kwIbUkyF|w72`h78q z#%;VV_E2NmSgd_|3RnSFYn5;U{`Mn%$z;dR0dRHV>^7~mt8>b$7uCqU_qM%yMl4uB zIEs7f7atXC{ZKvAyGO_5O1O}%;38TM5RSyi>Wx-$ydt~OX3N&GyKv4520CEL_w_|@ zHv9{kEMFNFFrG1gb(>2C?!)srCN#A>ajjFZFD9%gu8PC7tnSMX-by$Jt|LC{Ba7o} zr8qm)Hjs~6Mt>`P?>B^)<4bItz~yZmLYDh_F1Rg~FiBt1)Q{1#r<;eMGufI+5%zHT0(usJgY-J!UBvnyL&w$+y(_j^}vD zg6VO=zU`_jLJ{PQ-JBOP^XIivNxeDCcN{*0f&Xiwld*tCRid&vL!_8J5INGx+rSpRXg9k6dp-HcF49%MLAThe6z!>LnA;|(VnB3PYO0b$( zo=*o{It=lWPtPmcx8I*IiWErtvA4Hdub>$P%^!-;C>)9Kl)svgfbVw*KL@?X1pD7KUW)qL#WJR4 zXxjNd^SG64Wn%9rlid{VQ#t~hKJOpn&sjlk@y{J|DN#SElq_B`Pe!f&bWzgM&voq` zA4Ue3uwnd|2`X0O-Ws3pbscp$ldRQ}aW6w`bh93&p8kuFlso*u0RNxUKC42OJyT`~ zEKT8Va#zxWpD=`Yb4yM0PNb9wGnAhvamFw*T#aUaR(QY5F_bFBjc^E;`Vd&;x@ z4B))KX-l9~m!%G^Es4o#G=Q}A&76&86|bGj^_i+q3iouQ{E3vu*q;go{0tRoLj1zSi`o8J7BstL_|D2~$;4MI zeDa7h>()gkQ#BP%({a@{*>^`Iq7oXdOU{#{!g{g;(5Hr`%N_dLHi`*4wY zReN<7JM&eyN!juFyIo#$3G1Ej@9VCs4$-UMsI8#491{M;vHoLuAP5aG!UY71zKNN3 znef^d!%yoA{ggDpOVi1u3dxN=Y;UfLU~&05-omrC_^{pEY33`aM%gL+*rti+8~*K~ zBW-%sPP{XO{t5)l7sICoJA#LOi* zq=1Ddsu~`J6B!j@L+-_Y`H}xuKMiBIZ$#7`+ONBlnR%mE(OWjPyKkz$#f4X6iJ1JY zFH-FlApC!Rw!3Oi?+o=je|LW38kxIpvK=+A~q`?)OKf2M_TSk53hxnGIgj z>838Rm2b3#Og}ECYNvgii|XR4fOP)#>ZdqnXevJ!?BO-)7l=)ejL6|8>4tn}TpdYf za%FLOibfH2glxPYEN0A)DAgCv3fnw5v$J0EMsZd0OCvb&s1l9dPQi>BtNr+VEi0IV zWc-1>0&;KYzVx+LMsCSp7zd}a>D`3%+(x5nkQQW1CX#Z0w|j!4+Dl?y2iwwgjRXmI z+@R}Z3%>fu0g)s4`Wy%^$2bnC5Q`)(FYlgcT4r?3dVPg5&6T z6zn0D%%OCnCf8#q1mRm*=l_U~Z`nt?((P=d_#sEBdxacNe5K17md6V)UI0NO(90H zIVZ2m6uSdz*!2+Joc#3f-*q>}7?6DQyBd&Y|JXp0Aa8rq_4oN*cuik;d8p&9vgQH# z&nG_RN=a^%poqlT@g(hJ6>afEWa?iv)Ozd5@Jhp6Y*`J5N6=u} zR>q2kd$t2xz5a)tA|TZ#Ss_iF5V4W_tAY=BJeZ8O2c+)b2w@NJj`Ejak&BCCv%`xsR3m{~Ld6WUc`5XIc(8Kq z&K{BX(_;#4BDn{hn%JGcIiZf)M;MY7ut+v6sV8xJK8J-3MWBm)M*(OECp;1FgEnod zOFo84ZtVCqZg=Yu_Nq&S*O)rN=JE<)lb!10`dCTT8*?ua@yhHA;Wg~pu3gBE&rCuf+Rr|Er8 z;xWRD=kO%@EdWXJNg(pQGE5r}TTw<__F~wFu#QH{9{nH~IvGPN8#9FUC6eUf{G2zmM=E6sdi(1L|MG%>-_xLn_RuPh9g*+o z=JoAymuVJ}m937;vs8m|9xj{b&y2>z_*F!nL-s{R@LiJCKG;(J=@WPXCaoT-8YYW0 zwNHC!nXUArrFLQ_Mk$CvfEw4Ehd5u>USB-ThuS0<9&Bx?=3ZYF5q#_ANY%p&nUB;Z ziy6jc*GcJVo$Gn4xzUCXZje7ReFwzh6?P8ftC$aJ^i!+6krd0zWh_C9Bl#wgNrY1I zEdH%Q$@PJVcidZV%SuBG!j^o_>YiY283m=$0A4!h6_VtDvZ66ZTu&YA1Ry%obmr=C zWdm<>ggZkwUu-V1=8~1|;KmRS7;n$i%}KL>z$25fMAG#qT~DTF9yWtMgaXbQ^y2#C zvi~y(QFns5dUxg@R}%@+UV-H0W)JRzim?UPf>Tb*KP8d@rBN^~vmrUwbT}TEtQmFq zp_USUv>47U{d+T~eOcwSgxml&1vtoXJDmIQSr3E$ceYhgHPmPUs{%bDZcm``;tiq? zjW_!>-l`e;r+)yv><=+T^BKb4;+o_AUVH{cg!?&2ldoujJP9N$Dc~|PrEv`Dd|15F z{M|VpjdZP_?G1VU1^Paz`5hjPXchebNk6);}^E8{c4nizY7|#jqJr6_-;)eLK z;lHr5HVm=zBK%-YqZ*we@kt78x%wON-}S$P*b4?d?mz4-JLY6}z2$}HJD;86I1
  • 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 z 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 N4a5m|rxr(C)<3xe$Fu+d literal 35470 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 lhY9@+YEKdLc From a772fd7b65b74a88a5f2d2e65c19a7c0c8c0d268 Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sat, 3 Jan 2026 13:01:38 +0000 Subject: [PATCH 17/37] tunetheweb contributions --- src/config/contributors.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/config/contributors.json b/src/config/contributors.json index d8f5d410a9c..a6751e6bb70 100644 --- a/src/config/contributors.json +++ b/src/config/contributors.json @@ -701,9 +701,11 @@ "reviewers" ], "2025": [ + "analysts", "committee", "developers", - "analysts" + "editors", + "reviewers" ] }, "twitter": "tunetheweb", From 3f552ed1739686ba591e623bf3af5c7bd12dfd34 Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sat, 3 Jan 2026 13:05:30 +0000 Subject: [PATCH 18/37] Linting fixes --- ...ent_signal_prevalence_by_third_party_category.sql | 2 +- ...signal_survival_rate_through_chains_optimized.sql | 12 ++++++------ ...onsent_signal_survival_rate_through_redirects.sql | 10 +++++----- ...nal_survival_rate_through_redirects_optimized.sql | 10 +++++----- ...ignal_survival_rate_through_redirects_working.sql | 10 +++++----- ...ent_signals_in_third_party_requests_optimized.sql | 2 +- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql b/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql index 27e90d7d5ce..17c02ee3cce 100644 --- a/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql +++ b/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql @@ -127,7 +127,7 @@ category_signal_aggregates AS ( COUNT(DISTINCT CASE WHEN has_usp_standard THEN page END) AS usp_standard_pages, COUNT(DISTINCT CASE WHEN has_usp_standard THEN canonicalDomain END) AS usp_standard_domains, - -- USP Non-Standard metrics + -- USP Non-Standard metrics COUNTIF(has_usp_nonstandard) AS usp_nonstandard_requests, COUNT(DISTINCT CASE WHEN has_usp_nonstandard THEN page END) AS usp_nonstandard_pages, COUNT(DISTINCT CASE WHEN has_usp_nonstandard THEN canonicalDomain END) AS usp_nonstandard_domains, diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql index ee543dc5590..d006c9bf654 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql @@ -17,18 +17,18 @@ LANGUAGE js AS """ has_tcf_standard: /[?&](gdpr|gdpr_consent|gdpr_pd)=/.test(url), has_gpp_standard: /[?&](gpp|gpp_sid)=/.test(url) }; - - signals.has_any_signal = signals.has_usp_standard || - signals.has_usp_nonstandard || - signals.has_tcf_standard || + + signals.has_any_signal = signals.has_usp_standard || + signals.has_usp_nonstandard || + signals.has_tcf_standard || signals.has_gpp_standard; - + return signals; } catch (e) { return { has_usp_standard: false, has_usp_nonstandard: false, - has_tcf_standard: false, + has_tcf_standard: false, has_gpp_standard: false, has_any_signal: false }; diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql index 769a10c729b..9686dede46a 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql @@ -18,22 +18,22 @@ LANGUAGE js AS """ has_tcf_standard: /[?&](gdpr|gdpr_consent|gdpr_pd)=/.test(url), has_gpp_standard: /[?&](gpp|gpp_sid)=/.test(url) }; - + signals.signal_count = [ signals.has_usp_standard, - signals.has_usp_nonstandard, + signals.has_usp_nonstandard, signals.has_tcf_standard, signals.has_gpp_standard ].filter(Boolean).length; - + signals.has_any_signal = signals.signal_count > 0; - + return signals; } catch (e) { return { has_usp_standard: false, has_usp_nonstandard: false, - has_tcf_standard: false, + has_tcf_standard: false, has_gpp_standard: false, has_any_signal: false, signal_count: 0 diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql index c3a5f121dcd..b2b217f2756 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql @@ -13,23 +13,23 @@ RETURNS STRUCT< LANGUAGE js AS """ try { if (!url || typeof url !== 'string') return { - has_usp_standard: false, has_usp_nonstandard: false, + has_usp_standard: false, has_usp_nonstandard: false, has_tcf_standard: false, has_gpp_standard: false, has_any_signal: false, signal_count: 0 }; - + const signals = { has_usp_standard: /[?&]us_privacy=/.test(url), has_usp_nonstandard: /[?&](ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string)=/.test(url), has_tcf_standard: /[?&](gdpr|gdpr_consent|gdpr_pd)=/.test(url), has_gpp_standard: /[?&](gpp|gpp_sid)=/.test(url) }; - + signals.signal_count = [ - signals.has_usp_standard, signals.has_usp_nonstandard, + signals.has_usp_standard, signals.has_usp_nonstandard, signals.has_tcf_standard, signals.has_gpp_standard ].filter(Boolean).length; - + signals.has_any_signal = signals.signal_count > 0; return signals; } catch (e) { diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql index ee735bdccac..7b52af5b9a4 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql @@ -13,23 +13,23 @@ RETURNS STRUCT< LANGUAGE js AS """ try { if (!url || typeof url !== 'string') return { - has_usp_standard: false, has_usp_nonstandard: false, + has_usp_standard: false, has_usp_nonstandard: false, has_tcf_standard: false, has_gpp_standard: false, has_any_signal: false, signal_count: 0 }; - + const signals = { has_usp_standard: /[?&]us_privacy=/.test(url), has_usp_nonstandard: /[?&](ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string)=/.test(url), has_tcf_standard: /[?&](gdpr|gdpr_consent|gdpr_pd)=/.test(url), has_gpp_standard: /[?&](gpp|gpp_sid)=/.test(url) }; - + signals.signal_count = [ - signals.has_usp_standard, signals.has_usp_nonstandard, + signals.has_usp_standard, signals.has_usp_nonstandard, signals.has_tcf_standard, signals.has_gpp_standard ].filter(Boolean).length; - + signals.has_any_signal = signals.signal_count > 0; return signals; } catch (e) { diff --git a/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql b/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql index 39c27b0425c..c943609719b 100644 --- a/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql +++ b/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql @@ -132,7 +132,7 @@ signal_aggregates AS ( COUNT(DISTINCT CASE WHEN has_usp_standard THEN page END) AS usp_standard_pages, COUNT(DISTINCT CASE WHEN has_usp_standard THEN canonicalDomain END) AS usp_standard_domains, - -- USP Non-Standard metrics + -- USP Non-Standard metrics COUNTIF(has_usp_nonstandard) AS usp_nonstandard_requests, COUNT(DISTINCT CASE WHEN has_usp_nonstandard THEN page END) AS usp_nonstandard_pages, COUNT(DISTINCT CASE WHEN has_usp_nonstandard THEN canonicalDomain END) AS usp_nonstandard_domains, From b147e2353ef9227d5e859b374db9f8cc9f36fe1b Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sat, 3 Jan 2026 13:09:29 +0000 Subject: [PATCH 19/37] Linting --- ...onsent_signal_survival_rate_through_chains_optimized.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql index d006c9bf654..c2bff87837a 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql @@ -19,9 +19,9 @@ LANGUAGE js AS """ }; signals.has_any_signal = signals.has_usp_standard || - signals.has_usp_nonstandard || - signals.has_tcf_standard || - signals.has_gpp_standard; + signals.has_usp_nonstandard || + signals.has_tcf_standard || + signals.has_gpp_standard; return signals; } catch (e) { From a7d2d1763e2fad711a1f38f32434c5c8ad31d09b Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sat, 3 Jan 2026 13:12:54 +0000 Subject: [PATCH 20/37] Tweaks --- src/content/en/2025/third-parties.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/content/en/2025/third-parties.md b/src/content/en/2025/third-parties.md index 1eab6067a69..e4edb355019 100644 --- a/src/content/en/2025/third-parties.md +++ b/src/content/en/2025/third-parties.md @@ -53,11 +53,11 @@ When third-party content is directly served from a first-party domain, it is cou Further, it is becoming increasingly common for third parties to be masqueraded as a first party. Two key techniques enable this: -**CNAME cloaking** involves using a CNAME record to make a third party's content appear to come from the first-party domain. We consider CNAME-cloaked services as first parties in this analysis. +- **CNAME cloaking** involves using a CNAME record to make a third party's content appear to come from the first-party domain. We consider CNAME-cloaked services as first parties in this analysis. -**Server-side tracking** is an emerging trend where the site owner embeds the tracker as a first party and routes all requests through the first-party domain, making the tracker appear as a first party. For example, a website `www.example.com` may embed server-side Google Tag Manager with Google Analytics and cloak the subdomain sst.example.com to send requests to a Google Tag Manager Container. In this way, requests to third parties originate from the tag manager's server rather than the user's browser. +- **Server-side tracking** is an emerging trend where the site owner embeds the tracker as a first party and routes all requests through the first-party domain, making the tracker appear as a first party. For example, a website `www.example.com` may embed server-side Google Tag Manager with Google Analytics and cloak the subdomain `sst.example.com` to send requests to a Google Tag Manager Container. In this way, requests to third parties originate from the tag manager's server rather than the user's browser. -In our analysis, we treat such cases as first-party interactions because the third-party communication occurs server-to-server and is not directly observable in the client-side HTTP Archive data. As a result, our measurements represent a **lower bound** on the actual prevalence of third parties on the web +In our analysis, we treat such cases as first-party interactions because the third-party communication occurs server-to-server and is not directly observable in the client-side HTTP Archive data. As a result, our measurements represent a lower bound on the actual prevalence of third parties on the web ## Categories From 1e346c4607ea8a15bb2947bb9f97fa838c1c1e1b Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sat, 3 Jan 2026 14:34:09 +0000 Subject: [PATCH 21/37] Apply suggestions from code review --- src/config/contributors.json | 7 +++++-- src/content/en/2025/third-parties.md | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/config/contributors.json b/src/config/contributors.json index a6751e6bb70..037a03680f7 100644 --- a/src/config/contributors.json +++ b/src/config/contributors.json @@ -647,8 +647,11 @@ "twitter": "LoukilAymen", "website": "http://www.aymen-loukil.com/en/" }, - "aziz": { - "name": "Aziz", + "abubakaraziz": { + "avatar_url": "10674679", + "github": "abubakaraziz", + "linkedin": "aziz313f" + "name": "Muhammad Abu Bakar Aziz", "teams": { "2025": [ "authors" diff --git a/src/content/en/2025/third-parties.md b/src/content/en/2025/third-parties.md index e4edb355019..ca08faeafb7 100644 --- a/src/content/en/2025/third-parties.md +++ b/src/content/en/2025/third-parties.md @@ -3,7 +3,7 @@ title: Third Parties description: Third Parties chapter of the 2025 Web Almanac covering data of what third parties are used on the web and an analysis of inclusion chains of third parties. hero_alt: Hero image of Web Almanac characters plugging various things into a web page. -authors: [jazlan01, aziz] +authors: [jazlan01, abubakaraziz] reviewers: [tunetheweb] analysts: [jazlan01] editors: [tunetheweb] From ae9218c84a456970b6bafd860f93c84b6a18c7b0 Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sat, 3 Jan 2026 14:36:31 +0000 Subject: [PATCH 22/37] Update src/content/en/2025/third-parties.md --- src/content/en/2025/third-parties.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/en/2025/third-parties.md b/src/content/en/2025/third-parties.md index ca08faeafb7..daa65a7ffc8 100644 --- a/src/content/en/2025/third-parties.md +++ b/src/content/en/2025/third-parties.md @@ -9,7 +9,7 @@ analysts: [jazlan01] editors: [tunetheweb] translators: [] jazlan01_bio: Muhammad Jazlan is a Computer Science senior at Lahore University of Management Sciences, School of Science and Engineering. He works as a research assistant in the domain of Networks, AI Safety and Next Generation Cellular Devices. His work experience also includes working as a research intern at Biomedical Informatics & Engineering Research Laboratory. -aziz_bio: TODO +abubakaraziz_bio: Muhammad Abu Bakar Aziz is a PhD candidate in Computer Science at Northeastern University in Boston. His research focuses on web privacy. In particular, he empirically measures how third parties and online advertisers comply with privacy laws such as the CCPA and GDPR. results: https://docs.google.com/spreadsheets/d/1FPssodcLgX8iFWFXDrthWVkBCUTl5_IJon2cyaZVudU/edit featured_quote: TODO featured_stat_1: TODO From 4f9ecd4df34d77e531725d91962108697dbee495 Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sat, 3 Jan 2026 14:36:59 +0000 Subject: [PATCH 23/37] Update contributors --- src/config/contributors.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/config/contributors.json b/src/config/contributors.json index 037a03680f7..0edbba451e1 100644 --- a/src/config/contributors.json +++ b/src/config/contributors.json @@ -647,17 +647,6 @@ "twitter": "LoukilAymen", "website": "http://www.aymen-loukil.com/en/" }, - "abubakaraziz": { - "avatar_url": "10674679", - "github": "abubakaraziz", - "linkedin": "aziz313f" - "name": "Muhammad Abu Bakar Aziz", - "teams": { - "2025": [ - "authors" - ] - } - }, "tunetheweb": { "avatar_url": "10931297", "bluesky": "tunetheweb.com", @@ -3394,6 +3383,17 @@ ] } }, + "abubakaraziz": { + "avatar_url": "10674679", + "github": "abubakaraziz", + "linkedin": "aziz313f", + "name": "Muhammad Abu Bakar Aziz", + "teams": { + "2025": [ + "authors" + ] + } + }, "jazlan01": { "github": "jazlan01", "name": "Muhammad Jazlan", From 19997887d67544ca7081e93ec51a0c159152f0ae Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sat, 3 Jan 2026 18:18:34 +0000 Subject: [PATCH 24/37] Clean up images --- .../consent-signal-prevalence-by-category.png | Bin 23750 -> 23218 bytes .../consent-signal-prevalence-by-domain.png | Bin 27036 -> 25439 bytes .../consent-signal-prevalence-by-rank.png | Bin 21233 -> 21587 bytes .../2025/third-parties/num-3p-by-rank.png | Bin 19435 -> 19354 bytes .../pages-using-at-least-one-3p.png | Bin 19064 -> 18952 bytes .../third-parties/top-3p-by-num-pages.png | Bin 37263 -> 37188 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/static/images/2025/third-parties/consent-signal-prevalence-by-category.png b/src/static/images/2025/third-parties/consent-signal-prevalence-by-category.png index 05a40c8b1aa61e2619e013a8f508b854789cd5db..6505365147550620e28a6f4b4beea7cd57543415 100644 GIT binary patch literal 23218 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 diff --git a/src/static/images/2025/third-parties/consent-signal-prevalence-by-domain.png b/src/static/images/2025/third-parties/consent-signal-prevalence-by-domain.png index a7a0d74e00fbadbb11382827290d9ba29da622d1..b9a1081071a5c457c8415f7308029b0b7e661a5a 100644 GIT binary patch literal 25439 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 literal 27036 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

    GrDuTJtboxIjk3u^&+tk_+|;2u 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 Ghy52N;n>Ci diff --git a/src/static/images/2025/third-parties/consent-signal-prevalence-by-rank.png b/src/static/images/2025/third-parties/consent-signal-prevalence-by-rank.png index c0095bc7978d5aa9d24c012972609212a83455a5..4d6dc7d97dc13d01449c9c71bb9e52c9e382e446 100644 GIT binary patch literal 21587 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 literal 21233 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 Dm(-Cf diff --git a/src/static/images/2025/third-parties/pages-using-at-least-one-3p.png b/src/static/images/2025/third-parties/pages-using-at-least-one-3p.png index be8f971210baaefba7cb8287560fd4364b2380b1..4cebf887ac25fadd6630dedda3ff8674d9ee2709 100644 GIT binary patch literal 18952 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�ug@`2v z;B0#zq&MeX?6DVl@N(|Z;Q~vul3ViT<5BuOwQI7Vw~l> zmjb}Zutj1(0!^jWvl4m$i}b24P*iSw@<0G%s$ZE1 zoXdc<6GG5PJZlHDT5bngU+?PN>ZFushQ8Rtt|~u3c0~46{?+n zsa_GN>JDL55u+~}Mgo}j1{Q3YG^b5eb+Ncl@7V-SdXa%g^^QZ4a@gG=PR7@-njqhI zKvir&4t|7aDZn~QKmm@+t(=}GWLMK$6XIl7fdpV=2~f|R37b1Rl4` zt~C)A>gy^9=Jls`K-oK(8tE@+{Ty##C|U_?6qwJS4kwCEjoQk+H0Q-hLN-Gq>+pZe z*|d=JH$wmZ>$kl7@W1KM_-;#<|9j(qdPRc1()+Wx4lU;PUPNBz>|8P{98IN?o-S literal 19064 zcmc$`byQrAt+PgQq4U9;D`fAvBR2a^;N007`9$jiJ20Fa>o z0OBe-61>IuJe>pnApJ^NUH10&*1$eIzqkJ4;)0Y-;qdT~kB=`oIT;-M9^R9bl=RoH zUpF^5fgxe3si{|2SLf#!<5NE}GBU2NuTM@+r>Ca}1_naje^^{xY-?-V+1VK!9IUNx z7$2W_{~^-IF+xN{WN&Zp;^K03eS81lXnA!jGc)t>=p-d2g^7u2YiECRbE^^7*Ecv` zQ&U@BQJI?YX=rHp`1rWDufMpsWO#TaH?N?nxp{SUt)uhn?Ck8^+rKS1B)vm7Y@87@Y<>e(LrL=bq=H%vXY;2Z7q1M*cF8=YM@%iDA zG4I~J8ycN8b&kr)%BpSdX=!bp|M4RtA?NJud~)VzVPTYE-U;!U(1wPFnAGAIZyndy*E7FV ztgNiGbPj@}vJ(;#LPJA+!ZKg$xwdtG|5Q+uoL#A;q%P6hlKpT3Xs?&z{+NMk;99Y#i=dy2n0y zX|}ku-ZwPmSa?n}RGJ+f!;$^PRaJl)RZcV_;ubk27z5OfOCdq&10?}b5&oWzM6 zB(*-|(!Kh6ne#O_HKw!UlS-q|PdP#LTD8q}TRRU8zp3gFYJU*R+@W^6fe}CCR}ry# z@yy_rVj3|pIo4sbnzu5GyiD0&h(1PQx7ET$10pl$o_Pv?KVhDOff+7%#gg$XPu z1H338;l&n1=LI1ChHwQ=Cuk*j6_hKyBf<}?JHUdfL9xl?ZK13PpTN1lh;TKFhBD&7 z2?b(EA}Cw4718scl_Edf#8c0wKoYW^D;4sbW2VF(pp#6j zL^GKWO*op39i!JXMdl4CeND|gVpxuhxjlktN>w$mk;?W}P>DRBDG{x=!HPkHM1f0D z^IR!L?6L~idQ)V%3IIXU}M0j0RO=zyy;E3Fo-aM;>l(`kL&G=_I~ zq|Ht%pIjzNA3j9csFKMmP$8Rb;s^|KS>Ejw8sJ}wW1jZEwL7)nJOd%|&~e!Jr4Ci9 zzY35Qliv$|P7|wpDU`xD4}{|K%H2hX@MYYH-*wCY9xM5B3oc%a8t%2Aysf|@1_6(t0Ipa3nxz#|T3RAU2MiVjL7A>ai8eY>TB0YHnZVwa{X z_x3WhQK4OK`U!9z%ivTK=1^u=CJvnQgfh_<(u~8BU=Dk6d==UFg(>6^jE`sTP?uT^ zl^IV(XzyT;oYccUkK-(q_m3bFp*>CZ?D?Xv-}*b}-Xf|dSh&<^LDjQWz_}jZiqR9T z z#hk>BS7oKRXw~tNP70QnJi_W9MsZ3du}sX9f!?=yNMX#N`i5T%UxH0)67?coa4~y)r*HE9; zO8umeh3n>n)^C3o)Z*mIYK4A+S<2(|CNxJP<~ODtD{H= zxDdn~{Pd}?=H$jp^Jbi5ylR1pWF1$JZ|(1)LDAkEPks(cpbms)6wV zRd8_Q9hSg9TLX;InNU2~fm-x0ZUw`A%-pR{5=ZO;iUTt~1u7HHYl#M+T0UpNAY=>x^MEtq0k zQleefF7$ne2r!a>8TjD2RII_?WA=tJHRU3^_>50z=>wEU>_FLL)${sK{&|{qU!KK9 zH&c8wQpne7Y{2`FOlR_fI z*&?iedKH#(&WS2nd1X8oyqXfZz0^9h;n5a-KG%_qs+Fpm%9khiU%6F;X8HlYb{ zE|zbcxVOPLe}RxLECmHiGY1~99~&M)2nez-V*@ZVO&EM;RY@u=onk)xK&BGXHWp8& z>rJYVpDlMw?gD#MI4md6dnvX}9l03jn#j3{GGCJ>bCpEESp zW2EW-O}{wL1MIu6Si9Dm6l4lj2AR~Fu1LBQ+3@tscY_paeo8?TuirnjrokhM9}53! z>cEFL(ETi5U3A3tRgknSQ`YXh{V4Y&bx+a9D33O9I+? z*6I861myjx$VjM-<2ig~gw1bX?DVBACoh^Jeiki;6 zzzKrU`UTxtXk?9*9fq+=0>8w-C_18-=no+xMwE1@;gFUG>}-9vPlvk&fhl_pfvWJ3 z{JHxY8n#{_m6wZ6@2%4KRV0JwB9VKdKOl*afDJ)}1gYm`Cg{MYq#YT{#BKCO7ut); z^{N)c4`P~<;ZpelniF!1U#)rUL%+-h-$)NY?=rN&i#^XiUFQXf+2aHXce};SeQlp-sL- zR=UO^^a;;`FUYo78_CGxrf*|&?7*=N{b@4H+GD9_i)_^l+YFxD2^p`s&`P8t20po; zAY31Je&8I`y`Bq`Me2P1CLCo?b~2*K?W=wJS&XjRXOKUAAKBPSs#|I6?00V+unfuB zcr%O_#FMA@9VM26*8GIvm9ecP&4wTK4s^Xt2V0CF_85=ZOw3V3nMu-!bZMmPQ`TJg zR@oDB=8eu?E3QQIljE=x*{*FTq6FS+txJbt0V=9`4D_O_5pI!AC)^vriLo)~CI;vl zjI!ToC8+zbb+(D9Mcf8vyIM8L=h;_|8^BHX;OrLsI<~pz(F^CA<+bF1C0H!|hHXBz z`DyRQ@FE&@4@lwl_VSPt9z6-WLRcnF#uk_!%%H%d{&A_`y=XTb{QltBNgHzT=y!)a zz3sUy96xS=jH+m`EHD7{*pJ1Iy_J(%emdt}+2=3ig_}(roOR1wFOO(@>xEG>M^#_+U|1Z2WdhdSzw zS_ATi`; z3w!gNlz0@2@2aTRcPM6;T8?b$+g}sv48CmFexP$1*ji7-Y38#f~I#)1?5 zi<=S``s(Dy7ruB_@fWds$lmBQFOKDL^&=D#K|w|oBq<%94)lgOj|gx0dgHW3|$C;Y+2NN$R$n*>ME$ zFz~;?OT?1e!vgc0Z1x;mN-3T2`fE>f`szc;HM-C!AE6Pj(|l8(<6I<|I2JJM(SnV< zwe)=l)ieTf>MD2TT%v*$3Y#UG=*Cd|Fnc=r9I#@&J$>{IIASy!1r2z4xfJDKg&p>5 z9iU{A=Rfx31&-YGLKuNQ3tlq{|IRvc| zV1?i8t)9;_E-yrY+?|0Zwv%eMb<(D~oz1qzrrRvljoaRf$j{$frLNqDMI>^>(RyEP znN&&&s7x~C($8ZU+Yf9X zUyLcq^QNtULgd17E?4x6c!#mhdea=FB0(S3-3@;sE(KsCV9%`;l#MsN3^4wtASMpl z(cI#FHCkWsT0I8MDTSUX7cYRYZflx?twg22)+wx7GiSu`Nu`b9fG+jdAMZX9uX06E zQ3p;YIi{0DGngHY?3$JUlv69622qs0n~j$lb`AEgCHw-(E>n?mX{Z=35^R|L=%C3M zVoi4V>LjHG4f~wC_OzNewdEtEtyX0A{oLenbNX z3fFYy=oQQ&t_5J*rcUCiHuP#HWVb4Md`rS*H#jH6>#hK(NF zM{@l}AvB=uEiG(!Bo|+5zV#VqAKid+^ZUj&N0$tK!*P-JmEJYAcMif@K^MFgF9`5e zMNk?TX#&|0lrePu1)O(^vt$MEEX6-DF4?jyOjtobr7l*LKd{T0`kvDI^BvT0zDDQq zjU6xAE5<_B1?OqEr)vsnfT68R*Ul9$l-Nt&ZA+22p8&oDE`-pm01=eHU83qfDzftA z2&0r50TQn#z@hNVR|vfriHl1O??rZ(E{VAgwSt*{vw%q0Z&~q33(o5O#$4^pioMFJLyLVJ_1Ua0BbH=n`X+%S&Bo= z)@rF7g!0SQZ{_|;D2pJRQTd8*tEG+x18vyc+S3^n`q@(YT(+ww!C>t4mI z9*cU%M%;y1LqW1PGi8X^rv<6IyZpZZ&uyTnUWyzH*>ABEZJAqdmdE?|RKuP zfc*ViJ+*HQB#1g>A>U%=-fk5kiqfX*$u`LBje3yNBaZ4ozlFX7+j{+AUtOczd26D0}a*d3d~ePAI}hWbS;snY5f?&jB1JL#NunI)*$eU2S*-Vm0M7MKg%tZ zXhdjVYShnC@)}=y3bE|5L2*kOH~Lo*kHkNz#Nz zR0s1=gs!P}hv$4wwB-slfM)I-@G+YL1iY@EWd#r*9`z#H^^rWNw z{21a`yBn(Q2&|zGd^!YRJh|4bYTItJc_l?HHe>m~wg1&nNXr?7#BaO+)H$gzSc

  • {}dok3&?x$)A%HTe#6)v_Q5sL(HWM8EBynz@o|r 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 diff --git a/src/static/images/2025/third-parties/top-3p-by-num-pages.png b/src/static/images/2025/third-parties/top-3p-by-num-pages.png index ee4c6cc702534fe5bca2f0381546db71ad0fbed9..3ce81c493df4016559baef75d965495669653b74 100644 GIT binary patch literal 37188 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 literal 37263 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
  • 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 z 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 N4a5m|rxr(C)<3xe$Fu+d From abdde907740cc042816be2685203aee7ce7abc21 Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Sat, 10 Jan 2026 14:09:11 -0800 Subject: [PATCH 25/37] Most recently used queries --- .../third-parties/compressed_images_by_3p.sql | 2 +- .../consent_signal_basic_analysis.sql | 4 +- ...nal_prevalence_by_third_party_category.sql | 4 +- ...survival_rate_through_chains_optimized.sql | 38 +++++++++---------- ...signal_survival_rate_through_redirects.sql | 4 +- ...vival_rate_through_redirects_optimized.sql | 4 +- ...urvival_rate_through_redirects_working.sql | 6 +-- ...nals_by_parameter_and_domain_optimized.sql | 6 +-- sql/2025/third-parties/content_encoding.sql | 2 +- .../content_encoding_by_content_type.sql | 2 +- .../csp_allowed_host_frequency.sql | 4 +- sql/2025/third-parties/depth_of_gtm_calls.sql | 2 +- ...distribution_of_3XX_response_body_size.sql | 2 +- ...ribution_of_length_of_inclusion_chains.sql | 2 +- ...tion_of_size_and_time_by_third_parties.sql | 2 +- ...distribution_of_third_parties_by_frame.sql | 2 +- ...of_third_parties_by_number_of_websites.sql | 2 +- ...of_websites_by_number_of_third_parties.sql | 2 +- ...tes_by_number_of_third_party_providers.sql | 2 +- .../length_of_chain_by_intiator.sql | 2 +- ...ighthouse_average_unminified_css_by_3p.sql | 4 +- ...lighthouse_average_unminified_js_by_3p.sql | 2 +- .../number_of_third_parties_by_rank.sql | 4 +- ..._of_third_parties_by_rank_and_category.sql | 2 +- ...umber_of_third_party_providers_by_rank.sql | 2 +- ...d_party_providers_by_rank_and_category.sql | 4 +- ...number_of_third_party_requests_by_rank.sql | 5 ++- ..._third_party_requests_per_page_by_rank.sql | 4 +- ...rcent_of_third_parties_by_content_type.sql | 2 +- .../percent_of_third_party_cache.sql | 2 +- ...and_bytes_by_category_and_content_type.sql | 2 +- ...t_of_third_party_with_security_headers.sql | 2 +- .../percent_of_websites_with_third_party.sql | 2 +- ...f_websites_with_third_party_by_ranking.sql | 4 +- ...nals_in_third_party_requests_optimized.sql | 4 +- sql/2025/third-parties/tao_by_third_party.sql | 2 +- .../third_parties_blocking_rendering.sql | 2 +- ...parties_blocking_rendering_percentiles.sql | 4 +- ...d_parties_by_median_body_size_and_time.sql | 2 +- ...00_third_parties_by_number_of_websites.sql | 6 +-- ...d_parties_by_client_and_frame_location.sql | 2 +- 41 files changed, 79 insertions(+), 78 deletions(-) diff --git a/sql/2025/third-parties/compressed_images_by_3p.sql b/sql/2025/third-parties/compressed_images_by_3p.sql index 36f36b9e20a..93ae0e5d79e 100644 --- a/sql/2025/third-parties/compressed_images_by_3p.sql +++ b/sql/2025/third-parties/compressed_images_by_3p.sql @@ -31,7 +31,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain diff --git a/sql/2025/third-parties/consent_signal_basic_analysis.sql b/sql/2025/third-parties/consent_signal_basic_analysis.sql index 9ca789bcf11..6bd544f21e5 100644 --- a/sql/2025/third-parties/consent_signal_basic_analysis.sql +++ b/sql/2025/third-parties/consent_signal_basic_analysis.sql @@ -9,7 +9,7 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' + date = '2025-07-01' AND rank <= 50000 -- Expand to top 50K sites ), @@ -38,7 +38,7 @@ consent_requests AS ( ON r.client = p.client AND r.page = p.page WHERE - r.date = '2025-06-01' + r.date = '2025-07-01' AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only AND ( REGEXP_CONTAINS(r.url, r'[?&]us_privacy=') OR diff --git a/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql b/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql index 1d6e9eca25a..e69fe5924ba 100644 --- a/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql +++ b/sql/2025/third-parties/consent_signal_prevalence_by_third_party_category.sql @@ -9,7 +9,7 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' + date = '2025-07-01' ), requests AS ( @@ -20,7 +20,7 @@ requests AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-06-01' + date = '2025-07-01' ), third_party AS ( diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql index 69447ea5273..d6752707bba 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql @@ -4,7 +4,7 @@ CREATE TEMP FUNCTION extractConsentSignals(url STRING) RETURNS STRUCT< has_usp_standard BOOL, - has_usp_nonstandard BOOL, + has_usp_nonstandard BOOL, has_tcf_standard BOOL, has_gpp_standard BOOL, has_any_signal BOOL @@ -17,18 +17,18 @@ LANGUAGE js AS """ has_tcf_standard: /[?&](gdpr|gdpr_consent|gdpr_pd)=/.test(url), has_gpp_standard: /[?&](gpp|gpp_sid)=/.test(url) }; - - signals.has_any_signal = signals.has_usp_standard || - signals.has_usp_nonstandard || - signals.has_tcf_standard || - signals.has_gpp_standard; - + + signals.has_any_signal = signals.has_usp_standard || + signals.has_usp_nonstandard || + signals.has_tcf_standard || + signals.has_gpp_standard; + return signals; } catch (e) { return { has_usp_standard: false, has_usp_nonstandard: false, - has_tcf_standard: false, + has_tcf_standard: false, has_gpp_standard: false, has_any_signal: false }; @@ -43,7 +43,7 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' + date = '2025-07-01' AND rank <= 10000 -- Aggressive filtering: top 10K only ), @@ -64,7 +64,7 @@ filtered_requests AS ( ON r.client = p.client AND r.page = p.page WHERE - r.date = '2025-06-01' + r.date = '2025-07-01' AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only AND ( -- Only process requests with consent signals OR that are part of chains @@ -124,13 +124,13 @@ step_1_stats AS ( SELECT client, 1 AS step_number, - + COUNTIF(consent_signals.has_usp_standard) AS usp_standard_count, COUNTIF(consent_signals.has_usp_nonstandard) AS usp_nonstandard_count, COUNTIF(consent_signals.has_tcf_standard) AS tcf_standard_count, COUNTIF(consent_signals.has_gpp_standard) AS gpp_standard_count, COUNTIF(consent_signals.has_any_signal) AS any_signal_count, - + COUNT(*) AS total_requests, COUNT(DISTINCT root_page) AS total_pages FROM @@ -143,13 +143,13 @@ step_2_stats AS ( SELECT client, 2 AS step_number, - + COUNTIF(step2_signals.has_usp_standard) AS usp_standard_count, COUNTIF(step2_signals.has_usp_nonstandard) AS usp_nonstandard_count, COUNTIF(step2_signals.has_tcf_standard) AS tcf_standard_count, COUNTIF(step2_signals.has_gpp_standard) AS gpp_standard_count, COUNTIF(step2_signals.has_any_signal) AS any_signal_count, - + COUNT(*) AS total_requests, COUNT(DISTINCT root_page) AS total_pages FROM @@ -186,20 +186,20 @@ SELECT cs.step_number, cs.total_requests, cs.total_pages, - + -- Signal counts and survival rates cs.usp_standard_count, SAFE_DIVIDE(cs.usp_standard_count, b.usp_standard_baseline) AS usp_standard_survival_rate, - + cs.usp_nonstandard_count, SAFE_DIVIDE(cs.usp_nonstandard_count, b.usp_nonstandard_baseline) AS usp_nonstandard_survival_rate, - + cs.tcf_standard_count, SAFE_DIVIDE(cs.tcf_standard_count, b.tcf_standard_baseline) AS tcf_standard_survival_rate, - + cs.gpp_standard_count, SAFE_DIVIDE(cs.gpp_standard_count, b.gpp_standard_baseline) AS gpp_standard_survival_rate, - + cs.any_signal_count, SAFE_DIVIDE(cs.any_signal_count, b.any_signal_baseline) AS any_signal_survival_rate diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql index 342341c0325..f9a30960e11 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql @@ -49,7 +49,7 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' + date = '2025-07-01' ), -- Get redirect chains from crawl.requests summary column @@ -69,7 +69,7 @@ redirect_chains AS ( ON r.client = p.client AND r.page = p.page WHERE - r.date = '2025-06-01' + r.date = '2025-07-01' AND JSON_EXTRACT(r.summary, '$.redirects') IS NOT NULL AND JSON_EXTRACT(r.summary, '$.redirects') != '[]' -- AND p.rank <= 100000 -- Limit to top 100K sites diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql index 304fe587ac3..f273ad44ff6 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql @@ -49,7 +49,7 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' + date = '2025-07-01' AND rank <= 100000 -- Expanded to top 100K sites ), @@ -68,7 +68,7 @@ requests_with_redirects AS ( ON r.client = p.client AND r.page = p.page WHERE - r.date = '2025-06-01' + r.date = '2025-07-01' AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only AND JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') IS NOT NULL AND JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') != '' diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql index 7b810104590..bcf20fbe1ca 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql @@ -49,7 +49,7 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' + date = '2025-07-01' AND rank <= 100000 -- Expanded to top 100K sites ), @@ -67,7 +67,7 @@ initial_consent_requests AS ( ON r.client = p.client AND r.page = p.page WHERE - r.date = '2025-06-01' + r.date = '2025-07-01' AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only AND REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') ), @@ -90,7 +90,7 @@ requests_with_redirects AS ( AND icr.page = r.page AND icr.url = r.url WHERE - r.date = '2025-06-01' + r.date = '2025-07-01' AND JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') IS NOT NULL AND JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') != '' ), diff --git a/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql b/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql index 14da4eaf493..d4ce05896c0 100644 --- a/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql +++ b/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql @@ -9,7 +9,7 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' + date = '2025-07-01' ), requests AS ( @@ -20,7 +20,7 @@ requests AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-06-01' + date = '2025-07-01' -- Pre-filter: only process URLs that contain consent-related parameters AND REGEXP_CONTAINS(url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') ), @@ -122,7 +122,7 @@ totals AS ( NET.HOST(r.url) = NET.HOST(tp.domain), UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping WHERE - r.date = '2025-06-01' + r.date = '2025-07-01' AND p.rank <= rank_grouping GROUP BY r.client, diff --git a/sql/2025/third-parties/content_encoding.sql b/sql/2025/third-parties/content_encoding.sql index 9da7ce700a1..a747d710b3a 100644 --- a/sql/2025/third-parties/content_encoding.sql +++ b/sql/2025/third-parties/content_encoding.sql @@ -21,7 +21,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain diff --git a/sql/2025/third-parties/content_encoding_by_content_type.sql b/sql/2025/third-parties/content_encoding_by_content_type.sql index 2364ff65454..847bebe37f8 100644 --- a/sql/2025/third-parties/content_encoding_by_content_type.sql +++ b/sql/2025/third-parties/content_encoding_by_content_type.sql @@ -22,7 +22,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain diff --git a/sql/2025/third-parties/csp_allowed_host_frequency.sql b/sql/2025/third-parties/csp_allowed_host_frequency.sql index 3132a51e317..fbc11b365d1 100644 --- a/sql/2025/third-parties/csp_allowed_host_frequency.sql +++ b/sql/2025/third-parties/csp_allowed_host_frequency.sql @@ -19,7 +19,7 @@ WITH totals AS ( FROM `httparchive.all.requests` WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND is_main_document GROUP BY client @@ -33,7 +33,7 @@ csp_data AS ( FROM `httparchive.all.requests` WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND is_main_document AND response_headers IS NOT NULL ), diff --git a/sql/2025/third-parties/depth_of_gtm_calls.sql b/sql/2025/third-parties/depth_of_gtm_calls.sql index 75404eb2dde..7dad88cfd6b 100644 --- a/sql/2025/third-parties/depth_of_gtm_calls.sql +++ b/sql/2025/third-parties/depth_of_gtm_calls.sql @@ -67,7 +67,7 @@ WITH data AS ( `httparchive.crawl.requests` WHERE NET.REG_DOMAIN(root_page) != NET.REG_DOMAIN(url) AND - date = '2025-06-01' + date = '2025-07-01' ) WHERE third_party != initiator_etld AND root_page != initiator_etld diff --git a/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql b/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql index 928ae266500..32894fca755 100644 --- a/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql +++ b/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql @@ -24,7 +24,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/distribution_of_length_of_inclusion_chains.sql b/sql/2025/third-parties/distribution_of_length_of_inclusion_chains.sql index ace78271aad..90412f9bf57 100644 --- a/sql/2025/third-parties/distribution_of_length_of_inclusion_chains.sql +++ b/sql/2025/third-parties/distribution_of_length_of_inclusion_chains.sql @@ -40,7 +40,7 @@ WITH data AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND NET.REG_DOMAIN(root_page) != NET.REG_DOMAIN(url) ) WHERE third_party != initiator_etld AND diff --git a/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql b/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql index e30e4c01b51..393897c6972 100644 --- a/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql +++ b/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql @@ -23,7 +23,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql b/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql index 690e18dd4d1..694d1fd039c 100644 --- a/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql +++ b/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql @@ -13,7 +13,7 @@ WITH document_frameid AS ( JSON_EXTRACT_SCALAR(payload, '$._frame_id') AS frame_id, is_main_document FROM `httparchive.crawl.requests` AS requests - WHERE requests.date = '2025-06-01' AND requests.is_root_page = true + WHERE requests.date = '2025-07-01' AND requests.is_root_page = true ), page_frames AS ( diff --git a/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql b/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql index fefcac1e569..cda81450b4c 100644 --- a/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql +++ b/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql @@ -22,7 +22,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql index 9b1165ac09e..1d91f2deae4 100644 --- a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql +++ b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_parties.sql @@ -10,7 +10,7 @@ WITH requests AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-06-01' + date = '2025-07-01' ), diff --git a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql index 503818667f3..44f0a4ffd6f 100644 --- a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql +++ b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql @@ -22,7 +22,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/length_of_chain_by_intiator.sql b/sql/2025/third-parties/length_of_chain_by_intiator.sql index fa36b7b0224..8cab7a64d8a 100644 --- a/sql/2025/third-parties/length_of_chain_by_intiator.sql +++ b/sql/2025/third-parties/length_of_chain_by_intiator.sql @@ -38,7 +38,7 @@ WITH data AS ( `httparchive.crawl.requests` WHERE NET.REG_DOMAIN(root_page) != NET.REG_DOMAIN(url) AND - date = '2025-06-01' + date = '2025-07-01' ) WHERE third_party != initiator_etld AND root_page != initiator_etld diff --git a/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql b/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql index de202c6180c..f2b38666ac2 100644 --- a/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql @@ -27,14 +27,14 @@ FROM ( client, page, NET.HOST(unminified.url) IS NOT NULL AND NET.HOST(unminified.url) IN ( - SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2025-06-01' AND category != 'hosting' + SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2025-07-01' AND category != 'hosting' ) AS is_3p, unminified.wastedBytes AS wasted_bytes FROM `httparchive.all.pages` AS allpages CROSS JOIN UNNEST(getUnminifiedJsUrls(JSON_EXTRACT(allpages.lighthouse, "$.audits['unminified-css']"))) AS unminified - WHERE allpages.date = '2025-06-01' AND allpages.is_root_page = TRUE + WHERE allpages.date = '2025-07-01' AND allpages.is_root_page = TRUE ) GROUP BY client, diff --git a/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql b/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql index cc1ef92efae..43f5f152632 100644 --- a/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql +++ b/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql @@ -27,7 +27,7 @@ FROM ( _TABLE_SUFFIX AS client, lighthouse.url AS page, NET.HOST(unminified.url) IS NOT NULL AND NET.HOST(unminified.url) IN ( - SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2025-06-01' AND category != 'hosting' + SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2025-07-01' AND category != 'hosting' ) AS is_3p, unminified.wastedBytes AS wasted_bytes FROM diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank.sql b/sql/2025/third-parties/number_of_third_parties_by_rank.sql index c7559524cba..3e858d5c99b 100644 --- a/sql/2025/third-parties/number_of_third_parties_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_parties_by_rank.sql @@ -9,7 +9,7 @@ WITH requests AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND is_root_page = true ), @@ -21,7 +21,7 @@ pages AS ( FROM `httparchive.crawl.pages` AS pg WHERE - pg.date = '2025-06-01' AND + pg.date = '2025-07-01' AND pg.is_root_page = true ), diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql index be79bc7536f..47955ae72cd 100644 --- a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql +++ b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql @@ -31,7 +31,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category NOT IN ('hosting') GROUP BY domain, diff --git a/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql b/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql index 132271e46b3..53832d48692 100644 --- a/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql @@ -30,7 +30,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql index d96e77931c6..f786b83715a 100644 --- a/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql +++ b/sql/2025/third-parties/number_of_third_party_providers_by_rank_and_category.sql @@ -9,7 +9,7 @@ WITH requests AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-06-01' + date = '2025-07-01' ), pages AS ( @@ -20,7 +20,7 @@ pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' + date = '2025-07-01' ), third_party AS ( diff --git a/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql b/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql index e78af3902a5..c63c3ba89cb 100644 --- a/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_party_requests_by_rank.sql @@ -1,5 +1,6 @@ #standardSQL # Number of third-party requests by rank + WITH requests AS ( SELECT client, @@ -8,7 +9,7 @@ WITH requests AS ( FROM `httparchive.all.requests` AS req WHERE - req.date = '2025-06-01' AND + req.date = '2025-07-01' AND req.is_root_page = true ), @@ -20,7 +21,7 @@ pages AS ( FROM `httparchive.all.pages` AS pg WHERE - pg.date = '2025-06-01' AND + pg.date = '2025-07-01' AND pg.is_root_page = true ), diff --git a/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql b/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql index b626d197a07..cfc54316b1b 100644 --- a/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql +++ b/sql/2025/third-parties/number_of_third_party_requests_per_page_by_rank.sql @@ -9,7 +9,7 @@ WITH requests AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND is_root_page = true ), @@ -21,7 +21,7 @@ pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND is_root_page = true ), diff --git a/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql b/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql index f6a0a333b9d..18481200c68 100644 --- a/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql +++ b/sql/2025/third-parties/percent_of_third_parties_by_content_type.sql @@ -10,7 +10,7 @@ WITH requests AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-06-01' + date = '2025-07-01' ), third_party AS ( diff --git a/sql/2025/third-parties/percent_of_third_party_cache.sql b/sql/2025/third-parties/percent_of_third_party_cache.sql index ba7375cf9cd..445052b36be 100644 --- a/sql/2025/third-parties/percent_of_third_party_cache.sql +++ b/sql/2025/third-parties/percent_of_third_party_cache.sql @@ -27,7 +27,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql b/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql index e0496e976cf..058ca0ac10e 100644 --- a/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql +++ b/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql @@ -23,7 +23,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql b/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql index 51e75c658b2..cf340e8d90d 100644 --- a/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql +++ b/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql @@ -23,7 +23,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_websites_with_third_party.sql b/sql/2025/third-parties/percent_of_websites_with_third_party.sql index 9a7164016c6..6bada8aa52c 100644 --- a/sql/2025/third-parties/percent_of_websites_with_third_party.sql +++ b/sql/2025/third-parties/percent_of_websites_with_third_party.sql @@ -23,7 +23,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql b/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql index 4558d541b09..336253f0f3d 100644 --- a/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql +++ b/sql/2025/third-parties/percent_of_websites_with_third_party_by_ranking.sql @@ -9,7 +9,7 @@ WITH requests AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-06-01' + date = '2025-07-01' ), @@ -41,7 +41,7 @@ pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' + date = '2025-07-01' ) SELECT diff --git a/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql b/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql index 0a5334ddd83..8c7c4ea3619 100644 --- a/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql +++ b/sql/2025/third-parties/prevalence_of_consent_signals_in_third_party_requests_optimized.sql @@ -9,7 +9,7 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-06-01' + date = '2025-07-01' ), requests AS ( @@ -20,7 +20,7 @@ requests AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-06-01' + date = '2025-07-01' ), third_party AS ( diff --git a/sql/2025/third-parties/tao_by_third_party.sql b/sql/2025/third-parties/tao_by_third_party.sql index 7cbef01455f..541a32cef8f 100644 --- a/sql/2025/third-parties/tao_by_third_party.sql +++ b/sql/2025/third-parties/tao_by_third_party.sql @@ -46,7 +46,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/third_parties_blocking_rendering.sql b/sql/2025/third-parties/third_parties_blocking_rendering.sql index 51c4628afbe..a0c6072bee4 100644 --- a/sql/2025/third-parties/third_parties_blocking_rendering.sql +++ b/sql/2025/third-parties/third_parties_blocking_rendering.sql @@ -33,7 +33,7 @@ WITH total_third_party_usage AS ( `httparchive.almanac.third_parties` ON NET.HOST(requests.url) = NET.HOST(domain) AND - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY client, diff --git a/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql b/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql index cdc0e1db3e9..3811b1865e1 100644 --- a/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql +++ b/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql @@ -33,7 +33,7 @@ WITH total_third_party_usage AS ( `httparchive.almanac.third_parties` ON NET.HOST(requests.url) = NET.HOST(domain) AND - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY client, @@ -85,7 +85,7 @@ FROM ( `httparchive.almanac.third_parties` ON NET.HOST(JSON_VALUE(render_blocking_items, '$.url')) = domain AND - date = '2025-06-01' + date = '2025-07-01' GROUP BY client, canonicalDomain, diff --git a/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql b/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql index 053a4e809cc..73b65d25682 100644 --- a/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql +++ b/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql @@ -24,7 +24,7 @@ third_party AS ( requests r ON NET.HOST(r.url) = NET.HOST(tp.domain) WHERE - date = '2025-06-01' AND + date = '2025-07-01' AND category != 'hosting' GROUP BY domain, diff --git a/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql b/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql index f54d3cb220e..dc915aa07d8 100644 --- a/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql +++ b/sql/2025/third-parties/top100_third_parties_by_number_of_websites.sql @@ -9,7 +9,7 @@ WITH requests AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-06-01' + date = '2025-07-01' ), totals AS ( @@ -20,7 +20,7 @@ totals AS ( FROM `httparchive.crawl.requests` WHERE - date = '2025-06-01' + date = '2025-07-01' GROUP BY client ), @@ -44,7 +44,7 @@ third_party AS ( canonicalDomain, category HAVING - page_usage >= 50 + page_usage >= 5 ) SELECT diff --git a/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql b/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql index 8ee47c5f168..548aa20da24 100644 --- a/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql +++ b/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql @@ -13,7 +13,7 @@ WITH document_frameid AS ( JSON_EXTRACT_SCALAR(payload, '$._frame_id') AS frame_id, is_main_document FROM `httparchive.all.requests` AS requests - WHERE requests.date = '2025-06-01' AND requests.is_root_page = true + WHERE requests.date = '2025-07-01' AND requests.is_root_page = true ), page_frames AS ( From e5592e5b9cea4bf6f6f4a58e5809a865fbdd6577 Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Sat, 10 Jan 2026 14:17:57 -0800 Subject: [PATCH 26/37] Updated bio for Jazlan --- src/content/en/2025/third-parties.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/en/2025/third-parties.md b/src/content/en/2025/third-parties.md index daa65a7ffc8..d99307ac83d 100644 --- a/src/content/en/2025/third-parties.md +++ b/src/content/en/2025/third-parties.md @@ -8,7 +8,7 @@ reviewers: [tunetheweb] analysts: [jazlan01] editors: [tunetheweb] translators: [] -jazlan01_bio: Muhammad Jazlan is a Computer Science senior at Lahore University of Management Sciences, School of Science and Engineering. He works as a research assistant in the domain of Networks, AI Safety and Next Generation Cellular Devices. His work experience also includes working as a research intern at Biomedical Informatics & Engineering Research Laboratory. +jazlan01_bio: Muhammad Jazlan is a second year PhD student in Computer Science at University of California, Davis. His research focuses on the measurement, detection and mitigation of tracking on the web. abubakaraziz_bio: Muhammad Abu Bakar Aziz is a PhD candidate in Computer Science at Northeastern University in Boston. His research focuses on web privacy. In particular, he empirically measures how third parties and online advertisers comply with privacy laws such as the CCPA and GDPR. results: https://docs.google.com/spreadsheets/d/1FPssodcLgX8iFWFXDrthWVkBCUTl5_IJon2cyaZVudU/edit featured_quote: TODO From cfe46a8aa5b6f6ed43fe2a1223ad17426c848f79 Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Sat, 10 Jan 2026 14:44:12 -0800 Subject: [PATCH 27/37] Added featured stats, sql file names --- src/content/en/2025/third-parties.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/content/en/2025/third-parties.md b/src/content/en/2025/third-parties.md index d99307ac83d..8c63277ade5 100644 --- a/src/content/en/2025/third-parties.md +++ b/src/content/en/2025/third-parties.md @@ -11,13 +11,13 @@ translators: [] jazlan01_bio: Muhammad Jazlan is a second year PhD student in Computer Science at University of California, Davis. His research focuses on the measurement, detection and mitigation of tracking on the web. abubakaraziz_bio: Muhammad Abu Bakar Aziz is a PhD candidate in Computer Science at Northeastern University in Boston. His research focuses on web privacy. In particular, he empirically measures how third parties and online advertisers comply with privacy laws such as the CCPA and GDPR. results: https://docs.google.com/spreadsheets/d/1FPssodcLgX8iFWFXDrthWVkBCUTl5_IJon2cyaZVudU/edit -featured_quote: TODO -featured_stat_1: TODO -featured_stat_label_1: TODO -featured_stat_2: TODO -featured_stat_label_2: TODO -featured_stat_3: TODO -featured_stat_label_3: TODO +featured_quote: The top 10 third-party domains are dominated by Google. +featured_stat_1: 90% +featured_stat_label_1: Pages with at least one third party +featured_stat_2: 16 +featured_stat_label_2: The median number of third-party domains present on a page +featured_stat_3: 18% +featured_stat_label_3: Percentage of websites that use TCF Standard doi: TODO --- @@ -179,7 +179,7 @@ We examine consent signal prevalence in three dimensions: across different websi description="Bar chart showing... TODO.", chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=2066656520&format=interactive", sheets_gid="1614774531", - sql_file="TODO.sql" + sql_file="consent_signal_prevalence_by_third_party_category.sql" ) }} @@ -193,7 +193,7 @@ We find that TCF is the dominant consent standard, particularly among higher-ran description="Stacked bar chart showing.. TODO.", chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=828032137&format=interactive", sheets_gid="1614774531", - sql_file="TODO.sql" + sql_file="consent_signal_prevalence_by_third_party_category.sql" ) }} @@ -207,7 +207,7 @@ We observe different consent standard preferences across third-party categories. description="Bar chart showing... TODO.", chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=1262795614&format=interactive", sheets_gid="1788947788", - sql_file="TODO.sql" + sql_file="consent_signals_by_parameter_and_domain_optimized.sql" ) }} From 62bb93b6d4800af0924ff1a01f4b74b55512de45 Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Sat, 10 Jan 2026 16:10:43 -0800 Subject: [PATCH 28/37] Fixed linter errors --- .../consent_signal_basic_analysis.sql | 10 +++++----- ...gnal_survival_rate_through_chains_optimized.sql | 14 +++++--------- ...l_survival_rate_through_redirects_optimized.sql | 4 ++-- ...t_signals_by_parameter_and_domain_optimized.sql | 2 +- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/sql/2025/third-parties/consent_signal_basic_analysis.sql b/sql/2025/third-parties/consent_signal_basic_analysis.sql index 2612b9fa92b..aac8ad9dd53 100644 --- a/sql/2025/third-parties/consent_signal_basic_analysis.sql +++ b/sql/2025/third-parties/consent_signal_basic_analysis.sql @@ -9,8 +9,8 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-07-01' - AND rank <= 50000 -- Expand to top 50K sites + date = '2025-07-01' AND + rank <= 50000 -- Expand to top 50K sites ), -- Find requests with consent signals (no redirect filtering) @@ -38,9 +38,9 @@ consent_requests AS ( ON r.client = p.client AND r.page = p.page WHERE - r.date = '2025-07-01' - AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only - AND ( + r.date = '2025-07-01' AND + NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) AND -- Third-party only + ( REGEXP_CONTAINS(r.url, r'[?&]us_privacy=') OR REGEXP_CONTAINS(r.url, r'[?&](ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string)=') OR REGEXP_CONTAINS(r.url, r'[?&](gdpr|gdpr_consent|gdpr_pd)=') OR diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql index b07564b30d8..eb7e2da6163 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql @@ -44,8 +44,8 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-07-01' - AND rank <= 10000 -- Aggressive filtering: top 10K only + date = '2025-07-01' AND + rank <= 10000 -- Aggressive filtering: top 10K only ), -- Pre-filter to only requests with consent signals or initiator info @@ -65,9 +65,9 @@ filtered_requests AS ( ON r.client = p.client AND r.page = p.page WHERE - r.date = '2025-07-01' - AND NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only - AND ( + r.date = '2025-07-01' AND + NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) AND -- Third-party only + ( -- Only process requests with consent signals OR that are part of chains REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') OR JSON_VALUE(r.payload, '$._initiator') IS NOT NULL @@ -188,23 +188,19 @@ SELECT cs.total_requests, cs.total_pages, - -- Signal counts and survival rates cs.usp_standard_count, SAFE_DIVIDE(cs.usp_standard_count, b.usp_standard_baseline) AS usp_standard_survival_rate, - cs.usp_nonstandard_count, SAFE_DIVIDE(cs.usp_nonstandard_count, b.usp_nonstandard_baseline) AS usp_nonstandard_survival_rate, - cs.tcf_standard_count, SAFE_DIVIDE(cs.tcf_standard_count, b.tcf_standard_baseline) AS tcf_standard_survival_rate, cs.gpp_standard_count, SAFE_DIVIDE(cs.gpp_standard_count, b.gpp_standard_baseline) AS gpp_standard_survival_rate, - cs.any_signal_count, SAFE_DIVIDE(cs.any_signal_count, b.any_signal_baseline) AS any_signal_survival_rate diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql index fed6c20de8e..cbada86dba0 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql @@ -49,8 +49,8 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-07-01' - AND rank <= 100000 -- Expanded to top 100K sites + date = '2025-07-01' AND + rank <= 100000 -- Expanded to top 100K sites ), -- Pre-filter requests with redirects and potential consent signals diff --git a/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql b/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql index e9e8695c46a..cbc7da0a4e5 100644 --- a/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql +++ b/sql/2025/third-parties/consent_signals_by_parameter_and_domain_optimized.sql @@ -1,5 +1,5 @@ #standardSQL -# Optimized: Detailed breakdown of consent signals by individual parameters and top domains +# Detailed breakdown of consent signals by individual parameters and top domains WITH pages AS ( SELECT From 0cd4ce5e0b91547d2bed49588f81c183f3314e33 Mon Sep 17 00:00:00 2001 From: AbuBakar Aziz Date: Sat, 10 Jan 2026 19:21:09 -0500 Subject: [PATCH 29/37] text update --- src/content/en/2025/third-parties.md | 64 ++++++++++++++++------------ 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/src/content/en/2025/third-parties.md b/src/content/en/2025/third-parties.md index 8c63277ade5..4418d21d89e 100644 --- a/src/content/en/2025/third-parties.md +++ b/src/content/en/2025/third-parties.md @@ -31,7 +31,7 @@ In this chapter, we conduct an empirical analysis of third-party usage patterns - **Resource types:** The forms third parties take (images, JavaScript, fonts, etc.) - **Functional categories:** Ad networks, analytics, CDNs, video providers, tag managers, and others - **Integration methods:** How third parties are loaded directly or indirectly on pages -- **Consent infrastructure:** Which third parties handle consent signals and how those exchanges occur in practice +- **Consent infrastructure:** Which third parties transfer consent signals and how those transmissions happen in practice ## Definitions @@ -43,21 +43,21 @@ In this chapter, like previous years, we use the term site to depict the regist ### What is a Third Party? -We stick to the definition of a third party used in previous editions of the Web Almanac to allow for comparison between this and the previous editions. +We stick to the definition of a third party used in previous editions of the Web Almanac to allow for comparison with earlier versions. -A third party is an entity different from the site owner (aka first party). It involves the aspects of the site not directly implemented and served by the site owner. More precisely, third-party content is loaded from a different site (i.e., the third party) rather than the one originally visited by the user. Assume that the user visits example.com (the first party) and example.com includes silly cat images from `awesome-cats.edu` (for example using an `` tag). In that scenario, `awesome-cats.edu` is the third party, as it was not originally visited by the user. However, if the user directly visits `awesome-cats.edu`, `awesome-cats.edu` is the first party. +A third party is an entity different from the site owner (also known as the first party). It involves the aspects of the site not directly implemented and served by the site owner. More precisely, third-party content is loaded from a different site (i.e., the third party) rather than the one originally visited by the user. Assume that the user visits example.com (the first party) and example.com includes silly cat images from `awesome-cats.edu` (for example using an `` tag). In that scenario, `awesome-cats.edu` is the third party, as it was not originally visited by the user. However, if the user directly visits `awesome-cats.edu`, `awesome-cats.edu` is the first party. -For our analysis, only third parties originating from a domain whose resources can be found on at least 5 unique pages in the HTTP Archive dataset were included to match the definition. +For our analysis, only third parties originating from a domain whose resources can be found on at least 5 unique pages in the HTTP Archive dataset were included. -When third-party content is directly served from a first-party domain, it is counted as first-party content. For example, self-hosted analytics scripts, CSS, or fonts are counted as first-party content. Similarly, first-party content served from a third-party domain is counted as third-party content. Some third parties serve content from different subdomains. However, regardless of the number of subdomains, they are counted as a single third party. +When third-party content is directly served from a first party domain, it is counted as first party content. For example, self-hosted analytics scripts, CSS, or fonts are counted as first party content. Similarly, first-party content served from a third-party domain is counted as third-party content. Some third parties serve content from different subdomains. However, regardless of the number of subdomains, they are counted as a single third party. Further, it is becoming increasingly common for third parties to be masqueraded as a first party. Two key techniques enable this: -- **CNAME cloaking** involves using a CNAME record to make a third party's content appear to come from the first-party domain. We consider CNAME-cloaked services as first parties in this analysis. +- **CNAME cloaking** involves using a CNAME record to make a third party's content appear to come from the first party domain. We consider CNAME-cloaked services as first parties in this analysis. -- **Server-side tracking** is an emerging trend where the site owner embeds the tracker as a first party and routes all requests through the first-party domain, making the tracker appear as a first party. For example, a website `www.example.com` may embed server-side Google Tag Manager with Google Analytics and cloak the subdomain `sst.example.com` to send requests to a Google Tag Manager Container. In this way, requests to third parties originate from the tag manager's server rather than the user's browser. +- **Server-side tracking** is an emerging trend where the site owner embeds the tracker as a first party and routes all requests through the first party domain, making the tracker appear as a first party. For example, a website `www.example.com` may embed server-side Google Tag Manager with Google Analytics and cloak the subdomain `sst.example.com` to send requests to a Google Tag Manager container. In this way, requests to third parties originate from the tag manager's server rather than the user's browser. -In our analysis, we treat such cases as first-party interactions because the third-party communication occurs server-to-server and is not directly observable in the client-side HTTP Archive data. As a result, our measurements represent a lower bound on the actual prevalence of third parties on the web +In our analysis, we treat such cases as first party interactions because the third party communication occurs server-to-server and is not directly observable in the client-side HTTP Archive data. As a result, our measurements represent a lower bound on the actual prevalence of third parties on the web. ## Categories @@ -79,7 +79,7 @@ As previously indicated, third parties can be used for various use cases—for e ### `Content-Type` -We use the [`Content-Type`](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Type) HTTP header to categorize third-party resources into different types, such as scripts, HTML content, JSON data, plain text, and images. This allows us to analyze the composition of third-party resources served across websites +We use the [`Content-Type`](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Type) HTTP header to categorize third-party resources into different types, such as scripts, HTML content, JSON data, plain text, and images. This allows us to analyze the composition of third-party resources served across websites. ## Prevalence @@ -93,7 +93,7 @@ We use the [`Content-Type`](https://developer.mozilla.org/docs/Web/HTTP/Headers/ ) }} -Compared to the previous year, we observe a slight decrease in the percentage of pages that use one or more third parties across websites. However, despite this decrease, the percentage of pages with one or more third parties remain greater or equal to 90%. +Compared to the previous year, we observe a slight decrease in the percentage of pages that use one or more third parties across websites. However, despite this decrease, the percentage of pages with one or more third parties remains greater or equal to 90%. {{ figure_markup( image="num-3p-by-rank.png", @@ -105,9 +105,9 @@ Compared to the previous year, we observe a slight decrease in the percentage of ) }} -Compared to the previous year, we observe a decrease in the median number of third-party domains across all website ranks. +Compared to the previous year, we observe a significant decrease in the median number of third-party domains across all website ranks, with a particularly large decrease among low-ranked websites. -This decline may be due to several factors. First, third parties are increasingly obscured through CNAME cloaking and server-side tracking, which can reduce their visibility in client-side measurements. Second, our crawlers do not interact with cookie banners during the crawl, which may not generate consent signals. As a result, third parties may not run on a page, resulting in fewer observable third-party requests. +This decline may be due to several factors. First, third parties are increasingly obscured through CNAME cloaking and server-side tracking, which can reduce their visibility in client-side measurements. Second, HTTP Archive crawlers do not interact with web pages or scroll down the page, which may prevent some third parties from loading properly due to lazy loading. As a result, fewer third-party requests may be observed. We also observe that desktop pages generally include more third parties than mobile pages. @@ -121,13 +121,13 @@ We also observe that desktop pages generally include more third parties than mob ) }} -Higher-ranked websites load more third-party requests. The top 1,000 have a median of 129 requests on desktop and 106 on mobile, compared to 83 on desktop and 79 on mobile across all sites. +Low-ranked websites load more third-party requests. The top 1,000 have a median of 129 requests on desktop and 106 on mobile, compared to 83 on desktop and 79 on mobile across all sites. Year-over-year, third-party requests have increased across all ranks. The top 1,000 sites show an increase of 15 requests on desktop and 15 on mobile compared to 2024, while the broader dataset increased by 5 requests on desktop and 5 on mobile. This upward trend occurs despite the decrease in the number of unique third-party domains we observed earlier, suggesting that individual third parties are sending more requests per page. {{ figure_markup( image="3p-req-categories-by-rank.png", - caption="Distribution of the third party request categories by rank.", + caption="Distribution of the third-party request categories by rank.", description="Bar chart showing distribution of third party categories by rank groups. Top categories are consent provider, video, and customer success.", chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=1133634663&format=interactive", sheets_gid="445864775", @@ -135,11 +135,11 @@ Year-over-year, third-party requests have increased across all ranks. The top 1, ) }} -The top categories include ad, analytics consent-provider and other categories. +The top categories include ad, analytics and cdn categories. {{ figure_markup( image="3p-req-types-by-rank.png", - caption="Distribution of the third party request types by rank.", + caption="Distribution of the third-party request types by rank.", description="Pie chart showing percentage distribution of third party requests by content type. The top 3 content types are `script` (24.8%), `image` (19.9%), and other (13.9%)", chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=1309978891&format=interactive", sheets_gid="418010554", @@ -147,7 +147,7 @@ The top categories include ad, analytics consent-provider and other categories. ) }} -The top 3 types include script, image, and other. +The three most common third-party request types are script, image, and other. {{ figure_markup( image="top-3p-by-num-pages.png", @@ -165,53 +165,61 @@ The top 10 third-party domains are dominated by Google-owned services, including ## Consent propagation among third parties -In this section, we examine how different third parties transmit user consent across the web. Previous research has shown that third parties often rely on industry-standard frameworks to communicate consent information. In our analysis, we focus primarily on the IAB’s consent standards, including the Transparency and Consent Framework (TCF), the CCPA Compliance Framework, and the Global Privacy Platform (GPP). +In this section, we examine how different third parties transmit user consent across the web. Previous research has shown that third parties often rely on industry-standard frameworks to communicate consent information. In our analysis, we focus primarily on the IAB’s three consent standards: the Transparency and Consent Framework (TCF), the CCPA Framework, and the Global Privacy Platform (GPP). -These frameworks define how consent information should be encoded and shared. For example, the IAB CCPA framework specifies that consent strings should be transmitted using the `us_privacy` parameter in network requests. We begin by identifying which consent standards are most prevalent among third parties observed in our dataset. -We examine consent signal prevalence in three dimensions: across different website ranks, among different third-party categories, and among the third parties receiving the highest volume of consent signals +These frameworks define how consent information is encoded and shared between websites and third parties. We begin by identifying which consent standards are most prevalent among the third parties observed in our dataset. To determine which framework a third party uses, we rely on the presence of specific parameters in the request URLs. Details of the different standards are below: + +- **TCF Standard**: We identify use of the TCF framework by checking whether a third-party request includes the `gdpr` or `gdpr_consent` parameters, as specified by the IAB TCF. + +- **GPP Standard**: We identify use of the GPP framework by checking for the presence of the `gpp` and `gpp_sid` parameters. + +- **USP Standard and non-USP Standard**: We identify use of the USP Standard by checking whether a request transmits a `us_privacy` parameter, as defined by the IAB CCPA Framework. We also identify use of the non-standard USP Standard by detecting consent strings transmitted via non-standard parameters identified in the prior work. + +We analyze consent signal prevalence across website ranks, third-party categories, and the most frequently observed consent-receiving third parties. + ### Prevalence of consent signals across different ranks {{ figure_markup( image="consent-signal-prevalence-by-rank.png", caption="Consent signal prevalence by rank.", - description="Bar chart showing... TODO.", + description="Bar chart showing the prevalence of different consent standards in third-party requests across website ranks.", chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=2066656520&format=interactive", sheets_gid="1614774531", sql_file="consent_signal_prevalence_by_third_party_category.sql" ) }} -We find that TCF is the dominant consent standard, particularly among higher-ranked sites where it reaches 36% compared to 18% across all sites. This aligns with GDPR's stricter opt-in requirements for European users. The USP Standard is the second most prevalent, ranging from 9-17% across ranks, reflecting CCPA and state-level US privacy laws. GPP adoption remains minimal at 3-6%, despite being designed to unify consent frameworks across different jurisdictions and regulatory regimes. +We find that TCF Standard is the dominant consent standard, particularly among low-ranked sites where it reaches 36% compared to 18% across all sites. This higher adoption aligns with stronger opt-in consent requirements under GDPR. The USP Standard is the second most prevalent, with adoption ranging from 9–17% across ranks. This reflects use of the IAB CCPA consent framework introduced in response to the CCPA. GPP adoption remains minimal at 3–6%, despite its goal to unify consent frameworks across jurisdictions. ### Consent standard distribution across different categories {{ figure_markup( image="consent-signal-prevalence-by-category.png", caption="Consent signal prevalence by category.", - description="Stacked bar chart showing.. TODO.", + description="Bar chart showing consent standard prevalence across different third-party categories.", chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=828032137&format=interactive", sheets_gid="1614774531", sql_file="consent_signal_prevalence_by_third_party_category.sql" ) }} -We observe different consent standard preferences across third-party categories. Social services show the highest TCF adoption, while advertising vendors employ a more balanced mix of GPP, USP Standard, and smaller TCF shares. Analytics vendors predominantly adopt GPP. Marketing services show balanced adoption of GPP and USP Standard, with minimal TCF. Content and utility services rely primarily on GPP and USP Standard rather than TCF. Tag managers and video services diverge significantly from other categories, showing minimal adoption of TCF, GPP, and USP Standard in favor of USP Non-Standard signals—with video vendors relying on non-standard mechanisms almost exclusively. +We observe different consent standard preferences across different third-party categories. For example, Social services show the highest TCF adoption, while advertising vendors employ a more balanced mix of GPP, USP Standard, and smaller TCF shares. Furthermore, Analytics vendors predominantly adopt GPP. ### Top third parties receiving consent {{ figure_markup( image="consent-signal-prevalence-by-domain.png", caption="Consent signal prevalence by domain.", - description="Bar chart showing... TODO.", + description="Bar chart showing the third parties that receive the highest volume of consent signals.", chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=1262795614&format=interactive", sheets_gid="1788947788", sql_file="consent_signals_by_parameter_and_domain_optimized.sql" ) }} -Among top-ranked websites, `pubmatic.com` receives the highest volume of consent signals, with `adservice.google.com` in second place. The majority of domains receiving the most consent signals are advertising and ad tech vendors—ad exchanges, DSPs, and ad servers. +Among top-ranked websites, `pubmatic.com` receives the highest volume of consent signals, with `adservice.google.com` in second place. The majority of domains receiving the most consent signals are advertising and ad tech vendors—ad exchanges, DSPs, and ad servers. This makes intuitive sense, as in many jurisdictions third party advertising and analytics providers must obtain user consent before using user data for ads and other purposes. ## Inclusion @@ -227,7 +235,7 @@ Recall from our earlier example that example.com (a first party) can include an ) }} -The median depth of the inclusion chain is 3 which means the majority of the third party include at least another third party on a web page. The maximum depth of the inclusion chain is 2,285. +The median depth of the inclusion chain is 3 which means the majority of the third parties include at least another third party on a web page. The maximum depth of the inclusion chain is 2,285. ## Conclusion @@ -235,4 +243,4 @@ Our findings show the ubiquitous and increasingly concentrated nature of third p In terms of consent standards, TCF is the dominant consent standard across all website ranks. Among individual third parties, `pubmatic.com`, `adservice.google.com` and other ad tech domains receive the highest volume of consent signals. -Finally, the increasing use of obfuscation techniques such as CNAME cloaking and server-side tracking reduces visibility of third parties in client-side measurements, suggesting our findings represent a lower bound on actual prevalence. The privacy, security, and performance implications of third-party use remain important considerations for web developers. +Finally, the increasing use of obfuscation techniques such as CNAME cloaking and server-side tracking reduces visibility of third parties in client-side measurements, suggesting our findings represent a lower bound on actual prevalence. From 2bad4b36ad2c5d605568d175a2cc4c99bf01bdd8 Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sun, 11 Jan 2026 15:00:01 +0000 Subject: [PATCH 30/37] Technical edit --- src/content/en/2025/third-parties.md | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/content/en/2025/third-parties.md b/src/content/en/2025/third-parties.md index 4418d21d89e..a12d518d9ac 100644 --- a/src/content/en/2025/third-parties.md +++ b/src/content/en/2025/third-parties.md @@ -41,13 +41,13 @@ First, we establish some definitions and terminology that are used throughout ou In this chapter, like previous years, we use the term site to depict the registerable part of a given domain which is often referred to as *extended Top Level Domain plus one* (eTLD+1). For example, given the URL `https://www.bar.com/` the eTLD+1 is `bar.com` and for the URL `https://foo.co.uk` the eTLD+1 is `foo.co.uk`. By page (or web page), we mean a unique URL or, more specifically, the document (for example HTML or JavaScript) located at the particular URL. -### What is a Third Party? +### What is a third party? We stick to the definition of a third party used in previous editions of the Web Almanac to allow for comparison with earlier versions. -A third party is an entity different from the site owner (also known as the first party). It involves the aspects of the site not directly implemented and served by the site owner. More precisely, third-party content is loaded from a different site (i.e., the third party) rather than the one originally visited by the user. Assume that the user visits example.com (the first party) and example.com includes silly cat images from `awesome-cats.edu` (for example using an `` tag). In that scenario, `awesome-cats.edu` is the third party, as it was not originally visited by the user. However, if the user directly visits `awesome-cats.edu`, `awesome-cats.edu` is the first party. +A _third party_ is an entity different from the site owner (also known as the first party). It involves the aspects of the site not directly implemented and served by the site owner. More precisely, third-party content is loaded from a different site rather than the one originally visited by the user. Assume that the user visits `example.com` (the first party) and `example.com` includes silly cat images from `awesome-cats.edu` (for example using an `` tag). In that scenario, `awesome-cats.edu` is the third party, as it was not originally visited by the user. However, if the user directly visits `awesome-cats.edu`, `awesome-cats.edu` is the first party. -For our analysis, only third parties originating from a domain whose resources can be found on at least 5 unique pages in the HTTP Archive dataset were included. +For our analysis, only third parties originating from a domain whose resources can be found on at least five unique pages in the HTTP Archive dataset were included. When third-party content is directly served from a first party domain, it is counted as first party content. For example, self-hosted analytics scripts, CSS, or fonts are counted as first party content. Similarly, first-party content served from a third-party domain is counted as third-party content. Some third parties serve content from different subdomains. However, regardless of the number of subdomains, they are counted as a single third party. @@ -93,7 +93,7 @@ We use the [`Content-Type`](https://developer.mozilla.org/docs/Web/HTTP/Headers/ ) }} -Compared to the previous year, we observe a slight decrease in the percentage of pages that use one or more third parties across websites. However, despite this decrease, the percentage of pages with one or more third parties remains greater or equal to 90%. +Compared to [the previous year](../2024/third-parties#prevalence), we observe a slight decrease in the percentage of pages that use one or more third parties across websites. However, despite this decrease, the percentage of pages with one or more third parties remains greater or equal to 90%. {{ figure_markup( image="num-3p-by-rank.png", @@ -107,7 +107,7 @@ Compared to the previous year, we observe a slight decrease in the percentage of Compared to the previous year, we observe a significant decrease in the median number of third-party domains across all website ranks, with a particularly large decrease among low-ranked websites. -This decline may be due to several factors. First, third parties are increasingly obscured through CNAME cloaking and server-side tracking, which can reduce their visibility in client-side measurements. Second, HTTP Archive crawlers do not interact with web pages or scroll down the page, which may prevent some third parties from loading properly due to lazy loading. As a result, fewer third-party requests may be observed. +This decline may be due to several factors. First, third parties are increasingly obscured through `CNAME` cloaking and server-side tracking, which can reduce their visibility in client-side measurements. Second, HTTP Archive crawlers do not interact with web pages or scroll down the page, which may prevent some third parties from loading properly due to lazy loading. As a result, fewer third-party requests may be observed. We also observe that desktop pages generally include more third parties than mobile pages. @@ -123,7 +123,7 @@ We also observe that desktop pages generally include more third parties than mob Low-ranked websites load more third-party requests. The top 1,000 have a median of 129 requests on desktop and 106 on mobile, compared to 83 on desktop and 79 on mobile across all sites. -Year-over-year, third-party requests have increased across all ranks. The top 1,000 sites show an increase of 15 requests on desktop and 15 on mobile compared to 2024, while the broader dataset increased by 5 requests on desktop and 5 on mobile. This upward trend occurs despite the decrease in the number of unique third-party domains we observed earlier, suggesting that individual third parties are sending more requests per page. +Year-over-year, third-party requests have increased across all ranks. The top 1,000 sites show an increase of 15 requests on desktop and 15 on mobile [compared to 2024](../2024/third-parties#fig-3), while the broader dataset increased by five requests on desktop and five on mobile. This upward trend occurs despite the decrease in the number of unique third-party domains we observed earlier, suggesting that individual third parties are sending more requests per page. {{ figure_markup( image="3p-req-categories-by-rank.png", @@ -135,7 +135,7 @@ Year-over-year, third-party requests have increased across all ranks. The top 1, ) }} -The top categories include ad, analytics and cdn categories. +The top categories include `ad`, `analytics` and `cdn` categories. {{ figure_markup( image="3p-req-types-by-rank.png", @@ -147,7 +147,7 @@ The top categories include ad, analytics and cdn categories. ) }} -The three most common third-party request types are script, image, and other. +The three most common third-party request types are `script`, `image`, and `other`. {{ figure_markup( image="top-3p-by-num-pages.png", @@ -165,8 +165,7 @@ The top 10 third-party domains are dominated by Google-owned services, including ## Consent propagation among third parties -In this section, we examine how different third parties transmit user consent across the web. Previous research has shown that third parties often rely on industry-standard frameworks to communicate consent information. In our analysis, we focus primarily on the IAB’s three consent standards: the Transparency and Consent Framework (TCF), the CCPA Framework, and the Global Privacy Platform (GPP). - +In this section, we examine how different third parties transmit user consent across the web. Previous research has shown that third parties often rely on industry-standard frameworks to communicate consent information. In our analysis, we focus primarily on the IAB’s three consent standards: the Transparency and Consent Framework (TCF), the CCPA Framework, and the Global Privacy Protocol (GPP). These frameworks define how consent information is encoded and shared between websites and third parties. We begin by identifying which consent standards are most prevalent among the third parties observed in our dataset. To determine which framework a third party uses, we rely on the presence of specific parameters in the request URLs. Details of the different standards are below: @@ -178,7 +177,6 @@ These frameworks define how consent information is encoded and shared between we We analyze consent signal prevalence across website ranks, third-party categories, and the most frequently observed consent-receiving third parties. - ### Prevalence of consent signals across different ranks {{ figure_markup( @@ -205,7 +203,7 @@ We find that TCF Standard is the dominant consent standard, particularly among l ) }} -We observe different consent standard preferences across different third-party categories. For example, Social services show the highest TCF adoption, while advertising vendors employ a more balanced mix of GPP, USP Standard, and smaller TCF shares. Furthermore, Analytics vendors predominantly adopt GPP. +We observe different consent standard preferences across different third-party categories. For example, Social services show the highest TCF adoption, while advertising vendors employ a more balanced mix of GPP, USP Standard, and smaller TCF shares. Furthermore, Analytics vendors predominantly adopt GPP. ### Top third parties receiving consent @@ -223,7 +221,7 @@ Among top-ranked websites, `pubmatic.com` receives the highest volume of consent ## Inclusion -Recall from our earlier example that example.com (a first party) can include an image from awesome-cats.edu (a third party via an `` tag). This inclusion of an image would be considered direct inclusion. However, if the image was loaded by a third-party script on the site via the XMLHttpRequest, then the inclusion of the image would be considered indirect inclusion. The indirectly included third parties can further include additional third parties. For example, a third-party script that is directly included on the site may further include another third-party script. In this chapter, we do basic analysis of the depths of inclusion chains of the third parties. +Recall from our earlier example that `example.com` (a first party) can include an image from `awesome-cats.edu` (a third party via an `` tag). This inclusion of an image would be considered direct inclusion. However, if the image was loaded by a third-party script on the site via the `XMLHttpRequest`, then the inclusion of the image would be considered indirect inclusion. The indirectly included third parties can further include additional third parties. For example, a third-party script that is directly included on the site may further include another third-party script. In this chapter, we do basic analysis of the depths of inclusion chains of the third parties. {{ figure_markup( image="median-depth-tp-inclusion-chains.png", @@ -243,4 +241,4 @@ Our findings show the ubiquitous and increasingly concentrated nature of third p In terms of consent standards, TCF is the dominant consent standard across all website ranks. Among individual third parties, `pubmatic.com`, `adservice.google.com` and other ad tech domains receive the highest volume of consent signals. -Finally, the increasing use of obfuscation techniques such as CNAME cloaking and server-side tracking reduces visibility of third parties in client-side measurements, suggesting our findings represent a lower bound on actual prevalence. +Finally, the increasing use of obfuscation techniques such as CNAME cloaking and server-side tracking reduces visibility of third parties in client-side measurements, suggesting our findings represent a lower bound on actual prevalence. From 5c202c1b09e55b29e1a74edabf1dff74b52fb2c0 Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Sun, 11 Jan 2026 07:28:11 -0800 Subject: [PATCH 31/37] Cleaned up SQL --- .../a11y_overall_tech_usage_by_rank.sql | 61 ----- .../third-parties/a11y_technology_usage.sql | 35 --- .../a11y_technology_usage_by_rank.sql | 65 ----- .../third-parties/compressed_images_by_3p.sql | 81 ------ ...t_signal_survival_rate_through_chains.sql} | 0 ...signal_survival_rate_through_redirects.sql | 244 +++++++++--------- ...vival_rate_through_redirects_optimized.sql | 225 ---------------- ...urvival_rate_through_redirects_working.sql | 168 ------------ sql/2025/third-parties/content_encoding.sql | 51 ---- .../content_encoding_by_content_type.sql | 55 ---- .../csp_allowed_host_frequency.sql | 84 ------ sql/2025/third-parties/depth_of_gtm_calls.sql | 93 ------- ...distribution_of_3XX_response_body_size.sql | 64 ----- ...ion_of_lighthouse_unminified_css_by_3p.sql | 61 ----- ...tion_of_lighthouse_unminified_js_by_3p.sql | 62 ----- ...ibution_of_lighthouse_unused_css_by_3p.sql | 62 ----- ...ribution_of_lighthouse_unused_js_by_3p.sql | 62 ----- ...lighthouse_uses_optimized_images_by_3p.sql | 61 ----- ...tion_of_size_and_time_by_third_parties.sql | 65 ----- ...distribution_of_third_parties_by_frame.sql | 86 ------ ...of_third_parties_by_number_of_websites.sql | 65 ----- ...tes_by_number_of_third_party_providers.sql | 63 ----- .../third-parties/iframe_allow_attribute.sql | 45 ---- .../iframe_attribute_popular_hosts.sql | 54 ---- ...ighthouse_average_unminified_css_by_3p.sql | 44 ---- ...lighthouse_average_unminified_js_by_3p.sql | 42 --- .../lighthouse_third_party_facades.sql | 20 -- .../lighthouse_unminified_css_by_3p.sql | 70 ----- .../lighthouse_unminified_js_by_3p.sql | 70 ----- .../lighthouse_unminified_js_by_3p_by_url.sql | 75 ------ ...unminified_uses_optimized_images_by_3p.sql | 70 ----- .../lighthouse_unused_css_bytes_by_3p.sql | 70 ----- .../lighthouse_unused_js_bytes_by_3p.sql | 70 ----- ..._of_third_parties_by_rank_and_category.sql | 26 +- ...umber_of_third_party_providers_by_rank.sql | 78 ------ ..._of_third_parties_using_document_write.sql | 71 ----- ..._third_parties_using_legacy_javascript.sql | 71 ----- ...parties_using_legacy_javascript_by_url.sql | 88 ------- .../percent_of_third_party_cache.sql | 75 ------ ...and_bytes_by_category_and_content_type.sql | 86 ------ ...t_of_third_party_with_security_headers.sql | 73 ------ .../percent_of_websites_with_third_party.sql | 51 ---- .../scripts_using_async_defer.sql | 76 ------ .../scripts_using_async_defer_by_3p.sql | 81 ------ sql/2025/third-parties/tao_by_third_party.sql | 105 -------- .../third_parties_blocking_main_thread.sql | 71 ----- ...rties_blocking_main_thread_percentiles.sql | 59 ----- ...ocking_main_thread_percentiles_by_host.sql | 70 ----- .../third_parties_blocking_rendering.sql | 116 --------- ...parties_blocking_rendering_percentiles.sql | 114 -------- .../third_parties_using_legacy_javascript.sql | 57 ---- ...d_parties_by_median_body_size_and_time.sql | 87 ------- ...d_parties_by_client_and_frame_location.sql | 106 -------- .../usage_of_lite_youtube_embed.sql | 37 --- sql/2025/third-parties/usage_of_partytown.sql | 37 --- 55 files changed, 140 insertions(+), 3938 deletions(-) delete mode 100644 sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql delete mode 100644 sql/2025/third-parties/a11y_technology_usage.sql delete mode 100644 sql/2025/third-parties/a11y_technology_usage_by_rank.sql delete mode 100644 sql/2025/third-parties/compressed_images_by_3p.sql rename sql/2025/third-parties/{consent_signal_survival_rate_through_chains_optimized.sql => consent_signal_survival_rate_through_chains.sql} (100%) delete mode 100644 sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql delete mode 100644 sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql delete mode 100644 sql/2025/third-parties/content_encoding.sql delete mode 100644 sql/2025/third-parties/content_encoding_by_content_type.sql delete mode 100644 sql/2025/third-parties/csp_allowed_host_frequency.sql delete mode 100644 sql/2025/third-parties/depth_of_gtm_calls.sql delete mode 100644 sql/2025/third-parties/distribution_of_3XX_response_body_size.sql delete mode 100644 sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql delete mode 100644 sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql delete mode 100644 sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql delete mode 100644 sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql delete mode 100644 sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql delete mode 100644 sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql delete mode 100644 sql/2025/third-parties/distribution_of_third_parties_by_frame.sql delete mode 100644 sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql delete mode 100644 sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql delete mode 100644 sql/2025/third-parties/iframe_allow_attribute.sql delete mode 100644 sql/2025/third-parties/iframe_attribute_popular_hosts.sql delete mode 100644 sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql delete mode 100644 sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql delete mode 100644 sql/2025/third-parties/lighthouse_third_party_facades.sql delete mode 100644 sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql delete mode 100644 sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql delete mode 100644 sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql delete mode 100644 sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql delete mode 100644 sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql delete mode 100644 sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql delete mode 100644 sql/2025/third-parties/number_of_third_party_providers_by_rank.sql delete mode 100644 sql/2025/third-parties/percent_of_third_parties_using_document_write.sql delete mode 100644 sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql delete mode 100644 sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql delete mode 100644 sql/2025/third-parties/percent_of_third_party_cache.sql delete mode 100644 sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql delete mode 100644 sql/2025/third-parties/percent_of_third_party_with_security_headers.sql delete mode 100644 sql/2025/third-parties/percent_of_websites_with_third_party.sql delete mode 100644 sql/2025/third-parties/scripts_using_async_defer.sql delete mode 100644 sql/2025/third-parties/scripts_using_async_defer_by_3p.sql delete mode 100644 sql/2025/third-parties/tao_by_third_party.sql delete mode 100644 sql/2025/third-parties/third_parties_blocking_main_thread.sql delete mode 100644 sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql delete mode 100644 sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql delete mode 100644 sql/2025/third-parties/third_parties_blocking_rendering.sql delete mode 100644 sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql delete mode 100644 sql/2025/third-parties/third_parties_using_legacy_javascript.sql delete mode 100644 sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql delete mode 100644 sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql delete mode 100644 sql/2025/third-parties/usage_of_lite_youtube_embed.sql delete mode 100644 sql/2025/third-parties/usage_of_partytown.sql diff --git a/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql b/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql deleted file mode 100644 index f3882c6ad08..00000000000 --- a/sql/2025/third-parties/a11y_overall_tech_usage_by_rank.sql +++ /dev/null @@ -1,61 +0,0 @@ -#standardSQL -# Overall A11Y technology usage by domain rank - -WITH a11y_technologies AS ( - SELECT - _TABLE_SUFFIX AS client, - url - FROM - `httparchive.technologies.2025_06_01_*` - WHERE - category = 'Accessibility' -), - -pages AS ( - SELECT - _TABLE_SUFFIX AS client, - url, - rank_grouping - FROM - `httparchive.summary_pages.2025_06_01_*`, - UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping - WHERE - rank <= rank_grouping -), - -rank_totals AS ( - SELECT - _TABLE_SUFFIX AS client, - rank_grouping, - COUNT(0) AS total - FROM - `httparchive.summary_pages.2025_06_01_*`, - UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping - WHERE - rank <= rank_grouping - GROUP BY - client, - rank_grouping -) - -SELECT - client, - rank_grouping AS rank, - COUNT(DISTINCT url) AS freq, - total, - (COUNT(DISTINCT url) / total) * 100 AS pct -FROM - a11y_technologies -LEFT OUTER JOIN - pages -USING (client, url) -JOIN - rank_totals -USING (client, rank_grouping) -GROUP BY - rank_grouping, - total, - client -ORDER BY - client, - rank diff --git a/sql/2025/third-parties/a11y_technology_usage.sql b/sql/2025/third-parties/a11y_technology_usage.sql deleted file mode 100644 index 988478c30bb..00000000000 --- a/sql/2025/third-parties/a11y_technology_usage.sql +++ /dev/null @@ -1,35 +0,0 @@ -#standardSQL -# A11Y technology usage - -WITH a11y_technologies AS ( - SELECT - _TABLE_SUFFIX AS client, - COUNT(DISTINCT url) AS freq - FROM - `httparchive.technologies.2025_06_01_*` - WHERE - category = 'Accessibility' - GROUP BY - client -), - -pages AS ( - SELECT - _TABLE_SUFFIX AS client, - COUNT(0) AS total - FROM - `httparchive.summary_pages.2025_06_01_*` - GROUP BY - client -) - -SELECT - client, - freq, - total, - (freq / total) * 100 AS pct -FROM - a11y_technologies -JOIN - pages -USING (client) diff --git a/sql/2025/third-parties/a11y_technology_usage_by_rank.sql b/sql/2025/third-parties/a11y_technology_usage_by_rank.sql deleted file mode 100644 index cecc18fd76f..00000000000 --- a/sql/2025/third-parties/a11y_technology_usage_by_rank.sql +++ /dev/null @@ -1,65 +0,0 @@ -#standardSQL -# A11Y technology usage by domain rank - -WITH a11y_technologies AS ( - SELECT - _TABLE_SUFFIX AS client, - app, - url - FROM - `httparchive.technologies.2025_06_01_*` - WHERE - category = 'Accessibility' -), - -pages AS ( - SELECT - _TABLE_SUFFIX AS client, - url, - rank_grouping - FROM - `httparchive.summary_pages.2025_06_01_*`, - UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping - WHERE - rank <= rank_grouping -), - -rank_totals AS ( - SELECT - _TABLE_SUFFIX AS client, - rank_grouping, - COUNT(0) AS total - FROM - `httparchive.summary_pages.2025_06_01_*`, - UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping - WHERE - rank <= rank_grouping - GROUP BY - client, - rank_grouping -) - -SELECT - client, - rank_grouping AS rank, - app, - COUNT(0) AS freq, - total, - (COUNT(0) / total) * 100 AS pct -FROM - a11y_technologies -LEFT OUTER JOIN - pages -USING (client, url) -JOIN - rank_totals -USING (client, rank_grouping) -GROUP BY - rank_grouping, - total, - client, - app -ORDER BY - client, - rank, - pct DESC diff --git a/sql/2025/third-parties/compressed_images_by_3p.sql b/sql/2025/third-parties/compressed_images_by_3p.sql deleted file mode 100644 index 93ae0e5d79e..00000000000 --- a/sql/2025/third-parties/compressed_images_by_3p.sql +++ /dev/null @@ -1,81 +0,0 @@ -#standardSQL -# Compressed images (excluding SVG) by third parties - -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - pageid AS page, - url, - resp_content_encoding AS content_encoding, - type, - respBodySize AS size - FROM - `httparchive.summary_requests.2025_06_01_*` - WHERE - type = 'image' AND ( - resp_content_encoding = 'gzip' OR - resp_content_encoding = 'br' - ) AND NOT ( - resp_content_type LIKE 'image/svg%' OR - ENDS_WITH(url, '.svg') - ) -), - -third_party AS ( - SELECT - NET.HOST(domain) AS domain, - COUNT(DISTINCT page) AS page_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain - HAVING - page_usage >= 50 -) - -SELECT - client, - content_encoding, - domain, - size, - SUM(size) OVER (PARTITION BY client) AS total_size, - size / SUM(size) OVER (PARTITION BY client) AS pct_size, - num_requests, - total_requests, - pct_requests -FROM ( - SELECT - client, - content_encoding, - domain, - COUNT(0) AS num_requests, - SUM(size) AS size, - SUM(COUNT(0)) OVER (PARTITION BY client) AS total_requests, - COUNT(0) / SUM(COUNT(0)) OVER (PARTITION BY client) AS pct_requests, - RANK() OVER (PARTITION BY client, type, content_encoding ORDER BY COUNT(0) DESC) AS domain_rank - FROM - requests - LEFT JOIN - third_party - ON - NET.HOST(requests.url) = NET.HOST(third_party.domain) - WHERE - domain IS NOT NULL - GROUP BY - client, - type, - content_encoding, - domain -) -WHERE - domain_rank <= 100 -ORDER BY - client, - content_encoding, - size DESC diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_chains.sql similarity index 100% rename from sql/2025/third-parties/consent_signal_survival_rate_through_chains_optimized.sql rename to sql/2025/third-parties/consent_signal_survival_rate_through_chains.sql diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql index 154b4b36f7c..f2bc1d609bc 100644 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql +++ b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects.sql @@ -1,5 +1,5 @@ #standardSQL -# Consent signal survival rate through HTTP redirects +# Consent signal survival rate through HTTP redirects (memory-efficient) CREATE TEMP FUNCTION extractConsentSignals(url STRING) RETURNS STRUCT< @@ -12,31 +12,31 @@ RETURNS STRUCT< > LANGUAGE js AS """ try { + if (!url || typeof url !== 'string') return { + has_usp_standard: false, has_usp_nonstandard: false, + has_tcf_standard: false, has_gpp_standard: false, + has_any_signal: false, signal_count: 0 + }; + const signals = { has_usp_standard: /[?&]us_privacy=/.test(url), - has_usp_nonstandard: /[?&](ccpa|usp_consent|uspString|sst\\.us_privacy|uspConsent|ccpa_consent|AV_CCPA|usp|usprivacy|_fw_us_privacy|D9v\\.us_privacy|cnsnt|ccpaconsent|usp_string)=/.test(url), + has_usp_nonstandard: /[?&](ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string)=/.test(url), has_tcf_standard: /[?&](gdpr|gdpr_consent|gdpr_pd)=/.test(url), has_gpp_standard: /[?&](gpp|gpp_sid)=/.test(url) }; signals.signal_count = [ - signals.has_usp_standard, - signals.has_usp_nonstandard, - signals.has_tcf_standard, - signals.has_gpp_standard + signals.has_usp_standard, signals.has_usp_nonstandard, + signals.has_tcf_standard, signals.has_gpp_standard ].filter(Boolean).length; signals.has_any_signal = signals.signal_count > 0; - return signals; } catch (e) { return { - has_usp_standard: false, - has_usp_nonstandard: false, - has_tcf_standard: false, - has_gpp_standard: false, - has_any_signal: false, - signal_count: 0 + has_usp_standard: false, has_usp_nonstandard: false, + has_tcf_standard: false, has_gpp_standard: false, + has_any_signal: false, signal_count: 0 }; } """; @@ -49,18 +49,17 @@ WITH pages AS ( FROM `httparchive.crawl.pages` WHERE - date = '2025-07-01' + date = '2025-07-01' AND + rank <= 100000 -- Expanded to top 100K sites ), --- Get redirect chains from crawl.requests summary column -redirect_chains AS ( +-- Pre-filter requests with redirects and potential consent signals +requests_with_redirects AS ( SELECT r.client, r.page, r.url AS final_url, - JSON_EXTRACT(r.summary, '$.redirects') AS redirects, - JSON_EXTRACT_SCALAR(r.summary, '$.startedDateTime') AS startedDateTime, - JSON_EXTRACT_SCALAR(r.summary, '$.endedDateTime') AS endedDateTime, + JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') AS redirect_url, NET.REG_DOMAIN(r.url) AS final_domain FROM `httparchive.crawl.requests` r @@ -70,156 +69,157 @@ redirect_chains AS ( r.client = p.client AND r.page = p.page WHERE r.date = '2025-07-01' AND - JSON_EXTRACT(r.summary, '$.redirects') IS NOT NULL AND - JSON_EXTRACT(r.summary, '$.redirects') != '[]' AND - -- AND p.rank <= 100000 -- Limit to top 100K sites - NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) -- Third-party only + NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) AND -- Third-party only + JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') IS NOT NULL AND + JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') != '' AND + ( + -- Pre-filter: only URLs with consent signals in final URL or redirect URL + REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') OR + REGEXP_CONTAINS(JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl'), r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') + ) ), --- Parse redirect chains and extract consent signals at each step -parsed_redirects AS ( +-- Simplified redirect parsing - 2 step analysis +redirect_steps AS ( SELECT client, page, final_url, final_domain, - redirect_step.url AS step_url, - redirect_step.redirectURL AS next_url, - ROW_NUMBER() OVER ( - PARTITION BY client, page, final_url - ORDER BY COALESCE(redirect_step.startedDateTime, redirect_chains.startedDateTime) - ) AS redirect_step_number, - extractConsentSignals(redirect_step.url) AS step_signals, - extractConsentSignals(COALESCE(redirect_step.redirectURL, final_url)) AS next_signals + + -- Step 1: Original redirect URL (before redirect) + redirect_url AS step1_url, + + -- Step 2: Final URL (after redirect) + final_url AS step2_url FROM - redirect_chains, - UNNEST(JSON_EXTRACT_ARRAY(redirects)) AS redirect_step_json, - UNNEST([STRUCT( - JSON_EXTRACT_SCALAR(redirect_step_json, '$.url') AS url, - JSON_EXTRACT_SCALAR(redirect_step_json, '$.redirectURL') AS redirectURL, - JSON_EXTRACT_SCALAR(redirect_step_json, '$.startedDateTime') AS startedDateTime - )]) AS redirect_step + requests_with_redirects WHERE - redirect_step.url IS NOT NULL + redirect_url IS NOT NULL AND + redirect_url != '' ), --- Add final URL as the last step -redirect_chains_with_final AS ( - -- Intermediate redirect steps +-- Extract consent signals for each step +signals_by_step AS ( SELECT client, page, - final_url, final_domain, - redirect_step_number, - step_url AS url_at_step, - step_signals AS signals_at_step, - 'redirect' AS step_type - FROM - parsed_redirects - UNION ALL + -- Step 1 signals (original redirect URL) + step1_url, + extractConsentSignals(step1_url) AS step1_signals, - -- Final destination URL - SELECT - client, - page, - final_url, - final_domain, - MAX(redirect_step_number) + 1 AS redirect_step_number, - final_url AS url_at_step, - extractConsentSignals(final_url) AS signals_at_step, - 'final' AS step_type + -- Step 2 signals (final URL after redirect) + step2_url, + extractConsentSignals(step2_url) AS step2_signals FROM - parsed_redirects - GROUP BY - client, - page, - final_url, - final_domain + redirect_steps + WHERE + step1_url IS NOT NULL ), --- Calculate signal preservation statistics by step -step_survival_stats AS ( +-- Calculate step-wise aggregations (memory efficient) +step_aggregations AS ( + -- Step 1 stats (original redirect URL) SELECT client, - redirect_step_number, - step_type, - - -- Signals present at this step - COUNTIF(signals_at_step.has_usp_standard) AS usp_standard_at_step, - COUNTIF(signals_at_step.has_usp_nonstandard) AS usp_nonstandard_at_step, - COUNTIF(signals_at_step.has_tcf_standard) AS tcf_standard_at_step, - COUNTIF(signals_at_step.has_gpp_standard) AS gpp_standard_at_step, - COUNTIF(signals_at_step.has_any_signal) AS any_signal_at_step, + 1 AS redirect_step, + 'original' AS step_type, + + COUNTIF(step1_signals.has_usp_standard) AS usp_standard_count, + COUNTIF(step1_signals.has_usp_nonstandard) AS usp_nonstandard_count, + COUNTIF(step1_signals.has_tcf_standard) AS tcf_standard_count, + COUNTIF(step1_signals.has_gpp_standard) AS gpp_standard_count, + COUNTIF(step1_signals.has_any_signal) AS any_signal_count, + + AVG(step1_signals.signal_count) AS avg_signal_count, + COUNT(0) AS total_urls, + COUNT(DISTINCT page) AS total_pages + FROM + signals_by_step + WHERE + step1_signals.has_any_signal = TRUE -- Only analyze chains that start with signals + GROUP BY + client - -- Average signal count per URL - AVG(signals_at_step.signal_count) AS avg_signal_count_at_step, + UNION ALL - COUNT(0) AS total_urls_at_step, - COUNT(DISTINCT page) AS total_pages_at_step + -- Step 2 stats (final URL after redirect) + SELECT + client, + 2 AS redirect_step, + 'final' AS step_type, + + COUNTIF(step2_signals.has_usp_standard) AS usp_standard_count, + COUNTIF(step2_signals.has_usp_nonstandard) AS usp_nonstandard_count, + COUNTIF(step2_signals.has_tcf_standard) AS tcf_standard_count, + COUNTIF(step2_signals.has_gpp_standard) AS gpp_standard_count, + COUNTIF(step2_signals.has_any_signal) AS any_signal_count, + + AVG(step2_signals.signal_count) AS avg_signal_count, + COUNT(0) AS total_urls, + COUNT(DISTINCT page) AS total_pages FROM - redirect_chains_with_final + signals_by_step WHERE - redirect_step_number <= 6 -- Limit to first 6 steps (most redirects are short) + step1_signals.has_any_signal = TRUE -- Same baseline GROUP BY - client, - redirect_step_number, - step_type + client ), --- Get baseline (first step) for survival rate calculation -baseline_stats AS ( +-- Calculate baselines (step 1) +baselines AS ( SELECT client, - usp_standard_at_step AS usp_standard_baseline, - usp_nonstandard_at_step AS usp_nonstandard_baseline, - tcf_standard_at_step AS tcf_standard_baseline, - gpp_standard_at_step AS gpp_standard_baseline, - any_signal_at_step AS any_signal_baseline, - avg_signal_count_at_step AS avg_signal_count_baseline + usp_standard_count AS usp_standard_baseline, + usp_nonstandard_count AS usp_nonstandard_baseline, + tcf_standard_count AS tcf_standard_baseline, + gpp_standard_count AS gpp_standard_baseline, + any_signal_count AS any_signal_baseline, + avg_signal_count AS avg_signal_count_baseline FROM - step_survival_stats + step_aggregations WHERE - redirect_step_number = 1 + redirect_step = 1 ) -- Final output with survival rates SELECT - ss.client, - ss.redirect_step_number, - ss.step_type, - ss.total_urls_at_step, - ss.total_pages_at_step, + sa.client, + sa.redirect_step, + sa.step_type, + sa.total_urls, + sa.total_pages, - -- Signal counts and survival rates - ss.usp_standard_at_step, - SAFE_DIVIDE(ss.usp_standard_at_step, bs.usp_standard_baseline) AS usp_standard_survival_rate, + -- Signal survival rates + sa.usp_standard_count, + SAFE_DIVIDE(sa.usp_standard_count, b.usp_standard_baseline) AS usp_standard_survival_rate, - ss.usp_nonstandard_at_step, - SAFE_DIVIDE(ss.usp_nonstandard_at_step, bs.usp_nonstandard_baseline) AS usp_nonstandard_survival_rate, + sa.usp_nonstandard_count, + SAFE_DIVIDE(sa.usp_nonstandard_count, b.usp_nonstandard_baseline) AS usp_nonstandard_survival_rate, - ss.tcf_standard_at_step, - SAFE_DIVIDE(ss.tcf_standard_at_step, bs.tcf_standard_baseline) AS tcf_standard_survival_rate, + sa.tcf_standard_count, + SAFE_DIVIDE(sa.tcf_standard_count, b.tcf_standard_baseline) AS tcf_standard_survival_rate, - ss.gpp_standard_at_step, - SAFE_DIVIDE(ss.gpp_standard_at_step, bs.gpp_standard_baseline) AS gpp_standard_survival_rate, + sa.gpp_standard_count, + SAFE_DIVIDE(sa.gpp_standard_count, b.gpp_standard_baseline) AS gpp_standard_survival_rate, - ss.any_signal_at_step, - SAFE_DIVIDE(ss.any_signal_at_step, bs.any_signal_baseline) AS any_signal_survival_rate, + sa.any_signal_count, + SAFE_DIVIDE(sa.any_signal_count, b.any_signal_baseline) AS any_signal_survival_rate, - -- Average signal degradation - ss.avg_signal_count_at_step, - bs.avg_signal_count_baseline, - ss.avg_signal_count_at_step - bs.avg_signal_count_baseline AS signal_count_change, - SAFE_DIVIDE(ss.avg_signal_count_at_step, bs.avg_signal_count_baseline) AS signal_count_retention_rate + -- Signal count preservation + sa.avg_signal_count, + b.avg_signal_count_baseline, + sa.avg_signal_count - b.avg_signal_count_baseline AS signal_count_change, + SAFE_DIVIDE(sa.avg_signal_count, b.avg_signal_count_baseline) AS signal_count_retention_rate FROM - step_survival_stats ss + step_aggregations sa JOIN - baseline_stats bs + baselines b USING (client) ORDER BY client, - redirect_step_number + redirect_step diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql deleted file mode 100644 index cbada86dba0..00000000000 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_optimized.sql +++ /dev/null @@ -1,225 +0,0 @@ -#standardSQL -# Optimized: Consent signal survival rate through HTTP redirects (memory-efficient) - -CREATE TEMP FUNCTION extractConsentSignals(url STRING) -RETURNS STRUCT< - has_usp_standard BOOL, - has_usp_nonstandard BOOL, - has_tcf_standard BOOL, - has_gpp_standard BOOL, - has_any_signal BOOL, - signal_count INT64 -> -LANGUAGE js AS """ - try { - if (!url || typeof url !== 'string') return { - has_usp_standard: false, has_usp_nonstandard: false, - has_tcf_standard: false, has_gpp_standard: false, - has_any_signal: false, signal_count: 0 - }; - - const signals = { - has_usp_standard: /[?&]us_privacy=/.test(url), - has_usp_nonstandard: /[?&](ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string)=/.test(url), - has_tcf_standard: /[?&](gdpr|gdpr_consent|gdpr_pd)=/.test(url), - has_gpp_standard: /[?&](gpp|gpp_sid)=/.test(url) - }; - - signals.signal_count = [ - signals.has_usp_standard, signals.has_usp_nonstandard, - signals.has_tcf_standard, signals.has_gpp_standard - ].filter(Boolean).length; - - signals.has_any_signal = signals.signal_count > 0; - return signals; - } catch (e) { - return { - has_usp_standard: false, has_usp_nonstandard: false, - has_tcf_standard: false, has_gpp_standard: false, - has_any_signal: false, signal_count: 0 - }; - } -"""; - -WITH pages AS ( - SELECT - client, - page, - rank - FROM - `httparchive.crawl.pages` - WHERE - date = '2025-07-01' AND - rank <= 100000 -- Expanded to top 100K sites -), - --- Pre-filter requests with redirects and potential consent signals -requests_with_redirects AS ( - SELECT - r.client, - r.page, - r.url AS final_url, - JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') AS redirect_url, - NET.REG_DOMAIN(r.url) AS final_domain - FROM - `httparchive.crawl.requests` r - INNER JOIN - pages p - ON - r.client = p.client AND r.page = p.page - WHERE - r.date = '2025-07-01' AND - NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) AND -- Third-party only - JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') IS NOT NULL AND - JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') != '' AND - ( - -- Pre-filter: only URLs with consent signals in final URL or redirect URL - REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') OR - REGEXP_CONTAINS(JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl'), r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') - ) -), - --- Simplified redirect parsing - 2 step analysis -redirect_steps AS ( - SELECT - client, - page, - final_url, - final_domain, - - -- Step 1: Original redirect URL (before redirect) - redirect_url AS step1_url, - - -- Step 2: Final URL (after redirect) - final_url AS step2_url - FROM - requests_with_redirects - WHERE - redirect_url IS NOT NULL AND - redirect_url != '' -), - --- Extract consent signals for each step -signals_by_step AS ( - SELECT - client, - page, - final_domain, - - -- Step 1 signals (original redirect URL) - step1_url, - extractConsentSignals(step1_url) AS step1_signals, - - -- Step 2 signals (final URL after redirect) - step2_url, - extractConsentSignals(step2_url) AS step2_signals - FROM - redirect_steps - WHERE - step1_url IS NOT NULL -), - --- Calculate step-wise aggregations (memory efficient) -step_aggregations AS ( - -- Step 1 stats (original redirect URL) - SELECT - client, - 1 AS redirect_step, - 'original' AS step_type, - - COUNTIF(step1_signals.has_usp_standard) AS usp_standard_count, - COUNTIF(step1_signals.has_usp_nonstandard) AS usp_nonstandard_count, - COUNTIF(step1_signals.has_tcf_standard) AS tcf_standard_count, - COUNTIF(step1_signals.has_gpp_standard) AS gpp_standard_count, - COUNTIF(step1_signals.has_any_signal) AS any_signal_count, - - AVG(step1_signals.signal_count) AS avg_signal_count, - COUNT(0) AS total_urls, - COUNT(DISTINCT page) AS total_pages - FROM - signals_by_step - WHERE - step1_signals.has_any_signal = TRUE -- Only analyze chains that start with signals - GROUP BY - client - - UNION ALL - - -- Step 2 stats (final URL after redirect) - SELECT - client, - 2 AS redirect_step, - 'final' AS step_type, - - COUNTIF(step2_signals.has_usp_standard) AS usp_standard_count, - COUNTIF(step2_signals.has_usp_nonstandard) AS usp_nonstandard_count, - COUNTIF(step2_signals.has_tcf_standard) AS tcf_standard_count, - COUNTIF(step2_signals.has_gpp_standard) AS gpp_standard_count, - COUNTIF(step2_signals.has_any_signal) AS any_signal_count, - - AVG(step2_signals.signal_count) AS avg_signal_count, - COUNT(0) AS total_urls, - COUNT(DISTINCT page) AS total_pages - FROM - signals_by_step - WHERE - step1_signals.has_any_signal = TRUE -- Same baseline - GROUP BY - client -), - --- Calculate baselines (step 1) -baselines AS ( - SELECT - client, - usp_standard_count AS usp_standard_baseline, - usp_nonstandard_count AS usp_nonstandard_baseline, - tcf_standard_count AS tcf_standard_baseline, - gpp_standard_count AS gpp_standard_baseline, - any_signal_count AS any_signal_baseline, - avg_signal_count AS avg_signal_count_baseline - FROM - step_aggregations - WHERE - redirect_step = 1 -) - --- Final output with survival rates -SELECT - sa.client, - sa.redirect_step, - sa.step_type, - sa.total_urls, - sa.total_pages, - - -- Signal survival rates - sa.usp_standard_count, - SAFE_DIVIDE(sa.usp_standard_count, b.usp_standard_baseline) AS usp_standard_survival_rate, - - sa.usp_nonstandard_count, - SAFE_DIVIDE(sa.usp_nonstandard_count, b.usp_nonstandard_baseline) AS usp_nonstandard_survival_rate, - - sa.tcf_standard_count, - SAFE_DIVIDE(sa.tcf_standard_count, b.tcf_standard_baseline) AS tcf_standard_survival_rate, - - sa.gpp_standard_count, - SAFE_DIVIDE(sa.gpp_standard_count, b.gpp_standard_baseline) AS gpp_standard_survival_rate, - - sa.any_signal_count, - SAFE_DIVIDE(sa.any_signal_count, b.any_signal_baseline) AS any_signal_survival_rate, - - -- Signal count preservation - sa.avg_signal_count, - b.avg_signal_count_baseline, - sa.avg_signal_count - b.avg_signal_count_baseline AS signal_count_change, - SAFE_DIVIDE(sa.avg_signal_count, b.avg_signal_count_baseline) AS signal_count_retention_rate - -FROM - step_aggregations sa -JOIN - baselines b -USING (client) - -ORDER BY - client, - redirect_step diff --git a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql b/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql deleted file mode 100644 index 1e560fb9a89..00000000000 --- a/sql/2025/third-parties/consent_signal_survival_rate_through_redirects_working.sql +++ /dev/null @@ -1,168 +0,0 @@ -#standardSQL -# Working version: Consent signal survival rate through HTTP redirects - -CREATE TEMP FUNCTION extractConsentSignals(url STRING) -RETURNS STRUCT< - has_usp_standard BOOL, - has_usp_nonstandard BOOL, - has_tcf_standard BOOL, - has_gpp_standard BOOL, - has_any_signal BOOL, - signal_count INT64 -> -LANGUAGE js AS """ - try { - if (!url || typeof url !== 'string') return { - has_usp_standard: false, has_usp_nonstandard: false, - has_tcf_standard: false, has_gpp_standard: false, - has_any_signal: false, signal_count: 0 - }; - - const signals = { - has_usp_standard: /[?&]us_privacy=/.test(url), - has_usp_nonstandard: /[?&](ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string)=/.test(url), - has_tcf_standard: /[?&](gdpr|gdpr_consent|gdpr_pd)=/.test(url), - has_gpp_standard: /[?&](gpp|gpp_sid)=/.test(url) - }; - - signals.signal_count = [ - signals.has_usp_standard, signals.has_usp_nonstandard, - signals.has_tcf_standard, signals.has_gpp_standard - ].filter(Boolean).length; - - signals.has_any_signal = signals.signal_count > 0; - return signals; - } catch (e) { - return { - has_usp_standard: false, has_usp_nonstandard: false, - has_tcf_standard: false, has_gpp_standard: false, - has_any_signal: false, signal_count: 0 - }; - } -"""; - -WITH pages AS ( - SELECT - client, - page, - rank - FROM - `httparchive.crawl.pages` - WHERE - date = '2025-07-01' AND - rank <= 100000 -- Expanded to top 100K sites -), - --- First, find all third-party requests with consent signals (regardless of redirects) -initial_consent_requests AS ( - SELECT - r.client, - r.page, - r.url, - extractConsentSignals(r.url) AS url_signals - FROM - `httparchive.crawl.requests` r - INNER JOIN - pages p - ON - r.client = p.client AND r.page = p.page - WHERE - r.date = '2025-07-01' AND - NET.REG_DOMAIN(r.page) != NET.REG_DOMAIN(r.url) AND -- Third-party only - REGEXP_CONTAINS(r.url, r'[?&](us_privacy|ccpa|usp_consent|uspString|uspConsent|ccpa_consent|usp|usprivacy|ccpaconsent|usp_string|gdpr|gdpr_consent|gdpr_pd|gpp|gpp_sid)=') -), - --- Now look for those same requests that ALSO have redirect chains -requests_with_redirects AS ( - SELECT - icr.client, - icr.page, - icr.url, - icr.url_signals, - r.summary, - JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') AS redirect_url - FROM - initial_consent_requests icr - INNER JOIN - `httparchive.crawl.requests` r - ON - icr.client = r.client AND - icr.page = r.page AND - icr.url = r.url - WHERE - r.date = '2025-07-01' AND - JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') IS NOT NULL AND - JSON_EXTRACT_SCALAR(r.summary, '$.redirectUrl') != '' -), - --- Parse redirect chains (simplified - just look at redirect URL and final URL) -parsed_redirects AS ( - SELECT - client, - page, - url AS final_url, - url_signals AS final_signals, - - -- Extract redirect URL - redirect_url AS redirect_url, - - -- Simple redirect count (1 = single redirect) - CASE WHEN redirect_url IS NOT NULL AND redirect_url != '' THEN 1 ELSE 0 END AS redirect_count - FROM - requests_with_redirects - WHERE - redirect_url IS NOT NULL AND - redirect_url != '' -), - --- Extract signals from redirect steps -redirect_analysis AS ( - SELECT - client, - page, - final_url, - final_signals, - redirect_url, - redirect_count, - extractConsentSignals(redirect_url) AS redirect_signals - FROM - parsed_redirects - WHERE - redirect_url IS NOT NULL -) - --- Final analysis comparing signals across redirect steps -SELECT - client, - - -- Overall statistics - COUNT(0) AS total_redirect_chains_with_consent, - COUNT(DISTINCT page) AS pages_with_redirect_chains, - AVG(redirect_count) AS avg_redirect_count, - - -- Step 1 (redirect URL) signal analysis - COUNTIF(redirect_signals.has_any_signal) AS step1_requests_with_signals, - COUNTIF(redirect_signals.has_usp_standard) AS step1_usp_standard, - COUNTIF(redirect_signals.has_usp_nonstandard) AS step1_usp_nonstandard, - COUNTIF(redirect_signals.has_tcf_standard) AS step1_tcf_standard, - COUNTIF(redirect_signals.has_gpp_standard) AS step1_gpp_standard, - - -- Final URL signal analysis - COUNTIF(final_signals.has_any_signal) AS final_requests_with_signals, - COUNTIF(final_signals.has_usp_standard) AS final_usp_standard, - COUNTIF(final_signals.has_usp_nonstandard) AS final_usp_nonstandard, - COUNTIF(final_signals.has_tcf_standard) AS final_tcf_standard, - COUNTIF(final_signals.has_gpp_standard) AS final_gpp_standard, - - -- Survival rates (what percentage of signals make it to final URL) - SAFE_DIVIDE(COUNTIF(final_signals.has_any_signal), COUNT(0)) AS overall_signal_survival_rate, - SAFE_DIVIDE(COUNTIF(final_signals.has_usp_standard), COUNTIF(redirect_signals.has_usp_standard)) AS usp_standard_survival_rate, - SAFE_DIVIDE(COUNTIF(final_signals.has_tcf_standard), COUNTIF(redirect_signals.has_tcf_standard)) AS tcf_standard_survival_rate, - SAFE_DIVIDE(COUNTIF(final_signals.has_gpp_standard), COUNTIF(redirect_signals.has_gpp_standard)) AS gpp_standard_survival_rate - -FROM - redirect_analysis -GROUP BY - client -ORDER BY - client diff --git a/sql/2025/third-parties/content_encoding.sql b/sql/2025/third-parties/content_encoding.sql deleted file mode 100644 index a747d710b3a..00000000000 --- a/sql/2025/third-parties/content_encoding.sql +++ /dev/null @@ -1,51 +0,0 @@ -#standardSQL -#content-encoding by third parties - -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - pageid AS page, - url, - resp_content_encoding AS content_encoding - FROM - `httparchive.summary_requests.2025_06_01_*` -), - -third_party AS ( - SELECT - NET.HOST(domain) AS domain, - COUNT(DISTINCT page) AS page_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain - HAVING - page_usage >= 50 -) - -SELECT - client, - content_encoding, - COUNT(0) AS num_requests, - SUM(COUNT(0)) OVER (PARTITION BY client) AS total, - COUNT(0) / SUM(COUNT(0)) OVER (PARTITION BY client) AS pct -FROM - requests -LEFT JOIN - third_party -ON - NET.HOST(requests.url) = NET.HOST(third_party.domain) -WHERE - domain IS NOT NULL -GROUP BY - client, - content_encoding -ORDER BY - client, - num_requests DESC diff --git a/sql/2025/third-parties/content_encoding_by_content_type.sql b/sql/2025/third-parties/content_encoding_by_content_type.sql deleted file mode 100644 index 847bebe37f8..00000000000 --- a/sql/2025/third-parties/content_encoding_by_content_type.sql +++ /dev/null @@ -1,55 +0,0 @@ -#standardSQL -#content-encoding by third parties by content-type - -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - pageid AS page, - url, - resp_content_encoding AS content_encoding, - type - FROM - `httparchive.summary_requests.2025_06_01_*` -), - -third_party AS ( - SELECT - NET.HOST(domain) AS domain, - COUNT(DISTINCT page) AS page_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain - HAVING - page_usage >= 50 -) - -SELECT - client, - type, - content_encoding, - COUNT(0) AS num_requests, - SUM(COUNT(0)) OVER (PARTITION BY client, type) AS total, - COUNT(0) / SUM(COUNT(0)) OVER (PARTITION BY client, type) AS pct -FROM - requests -LEFT JOIN - third_party -ON - NET.HOST(requests.url) = NET.HOST(third_party.domain) -WHERE - domain IS NOT NULL -GROUP BY - client, - type, - content_encoding -ORDER BY - client, - type, - num_requests DESC diff --git a/sql/2025/third-parties/csp_allowed_host_frequency.sql b/sql/2025/third-parties/csp_allowed_host_frequency.sql deleted file mode 100644 index fbc11b365d1..00000000000 --- a/sql/2025/third-parties/csp_allowed_host_frequency.sql +++ /dev/null @@ -1,84 +0,0 @@ -#standardSQL -# CSP on home pages: most prevalent allowed hosts - -CREATE TEMPORARY FUNCTION getHeader(headers STRING, headername STRING) -RETURNS STRING DETERMINISTIC -LANGUAGE js AS ''' - const parsed_headers = JSON.parse(headers); - const matching_headers = parsed_headers.filter(h => h.name.toLowerCase() == headername.toLowerCase()); - if (matching_headers.length > 0) { - return matching_headers[0].value; - } - return null; -'''; - -WITH totals AS ( - SELECT - client, - COUNT(0) AS total - FROM - `httparchive.all.requests` - WHERE - date = '2025-07-01' AND - is_main_document - GROUP BY - client -), - -csp_data AS ( - SELECT - client, - page, - getHeader(TO_JSON_STRING(response_headers), 'Content-Security-Policy') AS csp_header - FROM - `httparchive.all.requests` - WHERE - date = '2025-07-01' AND - is_main_document AND - response_headers IS NOT NULL -), - -csp_expanded AS ( - SELECT - client, - page, - csp_allowed_host - FROM - csp_data, - UNNEST(REGEXP_EXTRACT_ALL(csp_header, r'(?i)(https*://[^\s;]+)[\s;]')) AS csp_allowed_host - WHERE - csp_header IS NOT NULL -), - -ranked_csp AS ( - SELECT - client, - csp_allowed_host, - COUNT(DISTINCT page) AS freq, - total AS total_pages, - COUNT(DISTINCT page) / total AS pct, - RANK() OVER (PARTITION BY client ORDER BY COUNT(DISTINCT page) DESC) AS csp_allowed_host_rank - FROM - csp_expanded - JOIN - totals - USING (client) - GROUP BY - client, - total, - csp_allowed_host -) - -SELECT - client, - csp_allowed_host, - freq, - total_pages, - pct -FROM - ranked_csp -WHERE - csp_allowed_host_rank <= 100 -ORDER BY - client, - pct DESC; diff --git a/sql/2025/third-parties/depth_of_gtm_calls.sql b/sql/2025/third-parties/depth_of_gtm_calls.sql deleted file mode 100644 index 7dad88cfd6b..00000000000 --- a/sql/2025/third-parties/depth_of_gtm_calls.sql +++ /dev/null @@ -1,93 +0,0 @@ -CREATE TEMP FUNCTION findAllInitiators(rootPage STRING, data ARRAY>) -RETURNS ARRAY -LANGUAGE js AS """ - // Helper function to find all initiator_etlds for a given root_page - function findInitiators(page, visited, data) { - // Find all entries where the root_page matches and the initiator_etld hasn't been visited - const initiators = data - .filter(row => row.root_page === page && !visited.includes(row.initiator_etld)) - .map(row => row.initiator_etld); - - // Add the newly found initiators to the visited list - visited = visited.concat(initiators); - - // Recursively process all new initiators - initiators.forEach(initiator => { - visited = findInitiators(initiator, visited, data); - }); - - return visited; - } - - // Main call: Start recursion from the rootPage - // Use a Set to ensure that all returned values are distinct - return Array.from(new Set(findInitiators(rootPage, [], data))); -"""; - - -CREATE TEMP FUNCTION mean_depth_and_next_element_after_gtm(input_array ARRAY) -RETURNS STRUCT> -LANGUAGE js AS """ - // Initialize the array to hold names of next elements - const nextElements = []; - - // Traverse the input array to find "googletagmanager.com" and capture the next element - for (let i = 0; i < input_array.length - 1; i++) { // -1 to avoid out-of-bounds - if (input_array[i] === 'googletagmanager.com') { - nextElements.push(input_array[i + 1]); - } - } - - // If no "googletagmanager.com" is found, return NULL - if (nextElements.length === 0) { - return { mean_depth: null, next_elements: [] }; - } - - // Calculate mean depth for all next elements - const meanDepth = nextElements.length > 0 - ? nextElements.reduce((sum, _, idx) => sum + (idx + 2), 0) / nextElements.length - : null; - - // Return the result as a struct - return { mean_depth: meanDepth, next_elements: nextElements }; -"""; - - -WITH data AS ( - -- TP interact with other tps - SELECT - * - FROM ( - SELECT - client, - NET.REG_DOMAIN(root_page) AS root_page, - NET.REG_DOMAIN(url) AS third_party, - NET.REG_DOMAIN(JSON_VALUE(payload, '$._initiator')) AS initiator_etld - FROM - `httparchive.crawl.requests` - WHERE - NET.REG_DOMAIN(root_page) != NET.REG_DOMAIN(url) AND - date = '2025-07-01' - ) - WHERE third_party != initiator_etld AND - root_page != initiator_etld - GROUP BY client, root_page, third_party, initiator_etld -) - -SELECT client, next_elements_after_gtm, count(0) AS c FROM ( - SELECT - client, - result.mean_depth AS mean_depth_after_gtm, - result.next_elements AS next_elements_after_gtm - FROM ( - SELECT - root_page, - client, - findAllInitiators(root_page, ARRAY_AGG(STRUCT(root_page, third_party, initiator_etld))) AS all_initiators - FROM data - GROUP BY root_page, client - ), - UNNEST([mean_depth_and_next_element_after_gtm(all_initiators)]) AS result - WHERE result.mean_depth IS NOT NULL - ORDER BY mean_depth_after_gtm -) GROUP BY client, next_elements_after_gtm ORDER BY c; diff --git a/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql b/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql deleted file mode 100644 index 32894fca755..00000000000 --- a/sql/2025/third-parties/distribution_of_3XX_response_body_size.sql +++ /dev/null @@ -1,64 +0,0 @@ -#standardSQL -# Distribution of response body size by redirected third parties -# HTTP status codes documentation: https://developer.mozilla.org/docs/Web/HTTP/Status - -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - pageid AS page, - url, - status, - respBodySize AS body_size - FROM - `httparchive.summary_requests.2025_06_01_*` -), - -third_party AS ( - SELECT - domain, - category, - COUNT(DISTINCT page) AS page_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain, - category - HAVING - page_usage >= 50 -), - -base AS ( - SELECT - client, - domain, - IF(status BETWEEN 300 AND 399, 1, 0) AS redirected, - body_size - FROM - requests - LEFT JOIN - third_party - ON - NET.HOST(requests.url) = NET.HOST(third_party.domain) -) - -SELECT - client, - percentile, - APPROX_QUANTILES(body_size, 1000)[OFFSET(percentile * 10)] AS approx_redirect_body_size -FROM - base, - UNNEST(GENERATE_ARRAY(1, 100)) AS percentile -WHERE - redirected = 1 -GROUP BY - client, - percentile -ORDER BY - client, - percentile diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql deleted file mode 100644 index a66b4cc4224..00000000000 --- a/sql/2025/third-parties/distribution_of_lighthouse_unminified_css_by_3p.sql +++ /dev/null @@ -1,61 +0,0 @@ -#standardSQL -# Pages with unminified third-party CSS - -CREATE TEMPORARY FUNCTION getUnminifiedCssUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(({url, wastedBytes}) => { - return {url, wastedBytes}; - }); -} catch (e) { - return []; -} -'''; - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, - SUM(potential_savings) AS potential_total_savings - FROM ( - SELECT - _TABLE_SUFFIX AS client, - lighthouse.url AS page, - NET.HOST(data.url) AS domain, - data.wastedBytes AS potential_savings - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUnminifiedCssUrls(JSON_EXTRACT(report, "$.audits['unminified-css']"))) AS data - ) AS potential_third_parties - LEFT OUTER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page -) - -SELECT - client, - percentile, - APPROX_QUANTILES(potential_third_party_savings, 1000)[OFFSET(percentile * 10)] AS potential_third_party_savings_bytes, - APPROX_QUANTILES(potential_total_savings, 1000)[OFFSET(percentile * 10)] AS potential_total_savings_bytes -FROM - base, - UNNEST([10, 25, 50, 75, 90, 100]) AS percentile -GROUP BY - client, - percentile -ORDER BY - client, - percentile diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql deleted file mode 100644 index 6bdfd03217e..00000000000 --- a/sql/2025/third-parties/distribution_of_lighthouse_unminified_js_by_3p.sql +++ /dev/null @@ -1,62 +0,0 @@ -#standardSQL -# Pages with unminified third-party JavaScript - -CREATE TEMPORARY FUNCTION getUnminifiedJavascriptUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(({url, wastedBytes}) => { - return {url, wastedBytes}; - }); -} catch (e) { - return []; -} -'''; - - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, - SUM(potential_savings) AS potential_total_savings - FROM ( - SELECT - _TABLE_SUFFIX AS client, - lighthouse.url AS page, - NET.HOST(data.url) AS domain, - data.wastedBytes AS potential_savings - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUnminifiedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS data - ) AS potential_third_parties - LEFT OUTER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page -) - -SELECT - client, - percentile, - APPROX_QUANTILES(potential_third_party_savings, 1000)[OFFSET(percentile * 10)] AS potential_third_party_savings_bytes, - APPROX_QUANTILES(potential_total_savings, 1000)[OFFSET(percentile * 10)] AS potential_total_savings_bytes -FROM - base, - UNNEST([10, 25, 50, 75, 90, 100]) AS percentile -GROUP BY - client, - percentile -ORDER BY - client, - percentile diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql deleted file mode 100644 index 5d1b2c41854..00000000000 --- a/sql/2025/third-parties/distribution_of_lighthouse_unused_css_by_3p.sql +++ /dev/null @@ -1,62 +0,0 @@ -#standardSQL -# Pages with unused third-party CSS - -CREATE TEMPORARY FUNCTION getUnusedCSSUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(({url, wastedBytes}) => { - return {url, wastedBytes}; - }); -} catch (e) { - return []; -} -'''; - - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, - SUM(potential_savings) AS potential_total_savings - FROM ( - SELECT - _TABLE_SUFFIX AS client, - lighthouse.url AS page, - NET.HOST(data.url) AS domain, - data.wastedBytes AS potential_savings - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUnusedCSSUrls(JSON_EXTRACT(report, "$.audits['unused-css-rules']"))) AS data - ) AS potential_third_parties - LEFT OUTER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page -) - -SELECT - client, - percentile, - APPROX_QUANTILES(potential_third_party_savings, 1000)[OFFSET(percentile * 10)] AS potential_third_party_savings_bytes, - APPROX_QUANTILES(potential_total_savings, 1000)[OFFSET(percentile * 10)] AS potential_total_savings_bytes -FROM - base, - UNNEST([10, 25, 50, 75, 90, 100]) AS percentile -GROUP BY - client, - percentile -ORDER BY - client, - percentile diff --git a/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql deleted file mode 100644 index ffbc9fb3f90..00000000000 --- a/sql/2025/third-parties/distribution_of_lighthouse_unused_js_by_3p.sql +++ /dev/null @@ -1,62 +0,0 @@ -#standardSQL -# Pages with unused third-party JavaScript - -CREATE TEMPORARY FUNCTION getUnusedJavascriptUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(({url, wastedBytes}) => { - return {url, wastedBytes}; - }); -} catch (e) { - return []; -} -'''; - - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, - SUM(potential_savings) AS potential_total_savings - FROM ( - SELECT - _TABLE_SUFFIX AS client, - lighthouse.url AS page, - NET.HOST(data.url) AS domain, - data.wastedBytes AS potential_savings - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUnusedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unused-javascript']"))) AS data - ) AS potential_third_parties - LEFT OUTER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page -) - -SELECT - client, - percentile, - APPROX_QUANTILES(potential_third_party_savings, 1000)[OFFSET(percentile * 10)] AS potential_third_party_savings_bytes, - APPROX_QUANTILES(potential_total_savings, 1000)[OFFSET(percentile * 10)] AS potential_total_savings_bytes -FROM - base, - UNNEST([10, 25, 50, 75, 90, 100]) AS percentile -GROUP BY - client, - percentile -ORDER BY - client, - percentile diff --git a/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql b/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql deleted file mode 100644 index 4d09f451b32..00000000000 --- a/sql/2025/third-parties/distribution_of_lighthouse_uses_optimized_images_by_3p.sql +++ /dev/null @@ -1,61 +0,0 @@ -#standardSQL -# Third-party pages with unoptimized images - -CREATE TEMPORARY FUNCTION getUnminifiedImageUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(({url, wastedBytes}) => { - return {url, wastedBytes}; - }); -} catch (e) { - return []; -} -'''; - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, - SUM(potential_savings) AS potential_total_savings - FROM ( - SELECT - _TABLE_SUFFIX AS client, - lighthouse.url AS page, - NET.HOST(data.url) AS domain, - data.wastedBytes AS potential_savings - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUnminifiedImageUrls(JSON_EXTRACT(report, "$.audits['uses-optimized-images']"))) AS data - ) AS potential_third_parties - LEFT OUTER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page -) - -SELECT - client, - percentile, - APPROX_QUANTILES(potential_third_party_savings, 1000)[OFFSET(percentile * 10)] AS potential_third_party_savings_bytes, - APPROX_QUANTILES(potential_total_savings, 1000)[OFFSET(percentile * 10)] AS potential_total_savings_bytes -FROM - base, - UNNEST([10, 25, 50, 75, 90, 100]) AS percentile -GROUP BY - client, - percentile -ORDER BY - client, - percentile diff --git a/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql b/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql deleted file mode 100644 index 393897c6972..00000000000 --- a/sql/2025/third-parties/distribution_of_size_and_time_by_third_parties.sql +++ /dev/null @@ -1,65 +0,0 @@ -#standardSQL -# Distribution of third party requests size and time by category - -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - pageid AS page, - url, - respBodySize AS body_size, - time - FROM - `httparchive.summary_requests.2025_06_01_*` -), - -third_party AS ( - SELECT - domain, - category, - COUNT(DISTINCT page) AS page_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain, - category - HAVING - page_usage >= 50 -), - -base AS ( - SELECT - client, - category, - body_size, - time - FROM - requests - INNER JOIN - third_party - ON - NET.HOST(requests.url) = NET.HOST(third_party.domain) -) - -SELECT - client, - category, - percentile, - APPROX_QUANTILES(body_size, 1000)[OFFSET(percentile * 10)] AS body_size, - APPROX_QUANTILES(time, 1000)[OFFSET(percentile * 10)] AS time -- noqa: L010 -FROM - base, - UNNEST(GENERATE_ARRAY(1, 100)) AS percentile -GROUP BY - client, - category, - percentile -ORDER BY - client, - category, - percentile diff --git a/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql b/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql deleted file mode 100644 index 694d1fd039c..00000000000 --- a/sql/2025/third-parties/distribution_of_third_parties_by_frame.sql +++ /dev/null @@ -1,86 +0,0 @@ -#standardSQL -# Distribution of third-parties embedded in main vs. in iframes - -WITH document_frameid AS ( - SELECT - client, - NET.HOST(page) AS page_host, - NET.HOST(url) AS frame_host, - CASE - WHEN is_main_document = true - THEN JSON_EXTRACT_SCALAR(payload, '$._frame_id') - END AS mainframe_id, - JSON_EXTRACT_SCALAR(payload, '$._frame_id') AS frame_id, - is_main_document - FROM `httparchive.crawl.requests` AS requests - WHERE requests.date = '2025-07-01' AND requests.is_root_page = true -), - -page_frames AS ( - SELECT - client, - page_host, - frame_host, - CASE - WHEN frame_host != page_host - THEN true - ELSE false - END AS tp_flag, - is_main_document, - frame_id, - COALESCE(mainframe_id, FIRST_VALUE(mainframe_id) OVER (PARTITION BY page_host ORDER BY is_main_document DESC)) AS mainframe_id, - CASE - WHEN frame_id = COALESCE(mainframe_id, FIRST_VALUE(mainframe_id) OVER (PARTITION BY page_host ORDER BY is_main_document DESC)) - THEN 'mainframe' - ELSE 'iframe' - END AS frame_type - FROM document_frameid -), - -combined_frame_counts AS ( - SELECT - client, - page_host, - frame_host, - tp_flag, - CASE - WHEN COUNT(DISTINCT frame_type) = 1 AND MAX(CASE WHEN frame_type = 'mainframe' THEN 1 ELSE 0 END) = 1 - THEN 'mainframe-only' - WHEN COUNT(DISTINCT frame_type) = 1 AND MAX(CASE WHEN frame_type = 'iframe' THEN 1 ELSE 0 END) = 1 - THEN 'iframe-only' - WHEN COUNT(DISTINCT frame_id) >= 2 AND COUNT(DISTINCT frame_type) = 2 - THEN 'both' - END AS frame_presence - FROM page_frames - GROUP BY client, page_host, frame_host, tp_flag -), - -aggregated_counts AS ( - SELECT - client, - COUNT(DISTINCT page_host) AS distinct_publisher_count, - COUNT(DISTINCT CASE WHEN tp_flag THEN frame_host ELSE null END) AS distinct_third_party_count, - COUNT(DISTINCT CASE WHEN frame_presence = 'mainframe-only' AND tp_flag THEN page_host ELSE null END) AS distinct_publishers_mainframe_only, - COUNT(DISTINCT CASE WHEN frame_presence = 'iframe-only' AND tp_flag THEN page_host ELSE null END) AS distinct_publishers_iframe_only, - COUNT(DISTINCT CASE WHEN frame_presence = 'both' AND tp_flag THEN page_host ELSE null END) AS distinct_publishers_both, - COUNT(DISTINCT CASE WHEN frame_presence = 'mainframe-only' AND tp_flag THEN frame_host ELSE null END) AS distinct_mainframe_third_party_count, - COUNT(DISTINCT CASE WHEN frame_presence = 'iframe-only' AND tp_flag THEN frame_host ELSE null END) AS distinct_iframe_third_party_count, - COUNT(DISTINCT CASE WHEN frame_presence = 'both' AND tp_flag THEN frame_host ELSE null END) AS distinct_both_third_party_count - FROM combined_frame_counts - GROUP BY client -) - -SELECT - client, - distinct_publisher_count, - distinct_third_party_count, - distinct_publishers_mainframe_only, - distinct_publishers_iframe_only, - distinct_publishers_both, - distinct_mainframe_third_party_count, - distinct_mainframe_third_party_count / distinct_third_party_count AS pct_tps_in_mainframe_only, - distinct_iframe_third_party_count, - distinct_iframe_third_party_count / distinct_third_party_count AS pct_tps_in_iframe_only, - distinct_both_third_party_count, - distinct_both_third_party_count / distinct_third_party_count AS pct_tps_in_both -FROM aggregated_counts; diff --git a/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql b/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql deleted file mode 100644 index cda81450b4c..00000000000 --- a/sql/2025/third-parties/distribution_of_third_parties_by_number_of_websites.sql +++ /dev/null @@ -1,65 +0,0 @@ -#standardSQL -# Distribution of third parties by number of websites - -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - pageid AS page, - url - FROM - `httparchive.summary_requests.2025_06_01_*` -), - -third_party AS ( - SELECT - domain, - canonicalDomain, - category, - COUNT(DISTINCT page) AS page_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain, - canonicalDomain, - category - HAVING - page_usage >= 50 -), - -base AS ( - SELECT - client, - canonicalDomain, - COUNT(DISTINCT page) AS pages_per_third_party - FROM - requests - LEFT JOIN - third_party - ON - NET.HOST(requests.url) = NET.HOST(third_party.domain) - WHERE - canonicalDomain IS NOT NULL - GROUP BY - client, - canonicalDomain -) - -SELECT - client, - percentile, - APPROX_QUANTILES(pages_per_third_party, 1000)[OFFSET(percentile * 10)] AS approx_pages_per_third_party -FROM - base, - UNNEST([10, 25, 50, 75, 90]) AS percentile -GROUP BY - client, - percentile -ORDER BY - client, - percentile diff --git a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql b/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql deleted file mode 100644 index 44f0a4ffd6f..00000000000 --- a/sql/2025/third-parties/distribution_of_websites_by_number_of_third_party_providers.sql +++ /dev/null @@ -1,63 +0,0 @@ -#standardSQL -# Distribution of websites by number of third party - -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - pageid AS page, - url - FROM - `httparchive.summary_requests.2025_06_01_*` -), - -third_party AS ( - SELECT - domain, - canonicalDomain, - category, - COUNT(DISTINCT page) AS page_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain, - canonicalDomain, - category - HAVING - page_usage >= 50 -), - -base AS ( - SELECT - client, - page, - COUNT(DISTINCT canonicalDomain) AS third_parties_per_page - FROM - requests - LEFT JOIN - third_party - ON - NET.HOST(requests.url) = NET.HOST(third_party.domain) - GROUP BY - client, - page -) - -SELECT - client, - percentile, - APPROX_QUANTILES(third_parties_per_page, 1000)[OFFSET(percentile * 10)] AS approx_third_parties_per_page -FROM - base, - UNNEST([10, 25, 50, 75, 90]) AS percentile -GROUP BY - client, - percentile -ORDER BY - client, - percentile diff --git a/sql/2025/third-parties/iframe_allow_attribute.sql b/sql/2025/third-parties/iframe_allow_attribute.sql deleted file mode 100644 index 0d9e2aa47b9..00000000000 --- a/sql/2025/third-parties/iframe_allow_attribute.sql +++ /dev/null @@ -1,45 +0,0 @@ -# standardSQL -# usage of different directives for allow attribute on iframes - -CREATE TEMP FUNCTION getNumWithAllowAttribute(payload STRING) AS (( - SELECT - COUNT(0) - FROM - UNNEST(JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox')) AS iframeAttr - WHERE - JSON_EXTRACT_SCALAR(iframeAttr, '$.allow') IS NOT NULL -)); - -SELECT - client, - SPLIT(TRIM(allow_attr), ' ')[OFFSET(0)] AS directive, - total_iframes_with_allow, - COUNT(0) AS freq, - COUNT(0) / total_iframes_with_allow AS pct -FROM ( - SELECT - _TABLE_SUFFIX AS client, - JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox') AS iframeAttrs - FROM - `httparchive.pages.2025_06_01_*` -), - UNNEST(iframeAttrs) AS iframeAttr, - UNNEST(REGEXP_EXTRACT_ALL(JSON_EXTRACT_SCALAR(iframeAttr, '$.allow'), r'(?i)([^,;]+)')) AS allow_attr -JOIN ( - SELECT - _TABLE_SUFFIX AS client, - SUM(getNumWithAllowAttribute(payload)) AS total_iframes_with_allow - FROM - `httparchive.pages.2025_06_01_*` - GROUP BY - client -) USING (client) -GROUP BY - client, - directive, - total_iframes_with_allow -HAVING - pct > 0.001 -ORDER BY - client, - pct DESC diff --git a/sql/2025/third-parties/iframe_attribute_popular_hosts.sql b/sql/2025/third-parties/iframe_attribute_popular_hosts.sql deleted file mode 100644 index 9a9de6cd71e..00000000000 --- a/sql/2025/third-parties/iframe_attribute_popular_hosts.sql +++ /dev/null @@ -1,54 +0,0 @@ -#standardSQL -# most common hostnames of iframes that have the allow or sandbox attribute - -CREATE TEMP FUNCTION hasPolicy(attr STRING, policy_type STRING) -RETURNS BOOL DETERMINISTIC -LANGUAGE js AS ''' - const $ = JSON.parse(attr); - return $[policy_type] !== null; -'''; - -SELECT - client, - policy_type, - hostname, - COUNTIF(has_policy) AS freq, - total_iframes, - COUNTIF(has_policy) / total_iframes AS pct -FROM ( - SELECT - client, - policy_type, - JSON_EXTRACT_SCALAR(iframeAttr, '$.hostname') AS hostname, - hasPolicy(iframeAttr, policy_type) AS has_policy - FROM ( - SELECT - _TABLE_SUFFIX AS client, - JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox') AS iframeAttrs - FROM - `httparchive.pages.2025_06_01_*` - ), - UNNEST(iframeAttrs) AS iframeAttr, - UNNEST(['allow', 'sandbox']) AS policy_type -) -JOIN ( - SELECT - _TABLE_SUFFIX AS client, - SUM(ARRAY_LENGTH(JSON_EXTRACT_ARRAY(JSON_EXTRACT_SCALAR(payload, '$._security'), '$.iframe-allow-sandbox'))) AS total_iframes - FROM - `httparchive.pages.2025_06_01_*` - GROUP BY - client -) -USING (client) -GROUP BY - client, - total_iframes, - policy_type, - hostname -HAVING - pct > 0.001 -ORDER BY - client, - policy_type, - pct DESC diff --git a/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql b/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql deleted file mode 100644 index f2b38666ac2..00000000000 --- a/sql/2025/third-parties/lighthouse_average_unminified_css_by_3p.sql +++ /dev/null @@ -1,44 +0,0 @@ -#standardSQL -# Pages with unminified CSS by 1P/3P -CREATE TEMPORARY FUNCTION getUnminifiedJsUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(({url, wastedBytes}) => { - return {url, wastedBytes}; - }); -} catch (e) { - return []; -} -'''; - -SELECT - client, - AVG(pct_1p_wasted_bytes) AS avg_pct_1p_wasted_bytes, - AVG(pct_3p_wasted_bytes) AS avg_pct_3p_wasted_bytes -FROM ( - SELECT - client, - page, - SUM(IF(is_3p, 0, wasted_bytes)) / SUM(wasted_bytes) AS pct_1p_wasted_bytes, - SUM(IF(is_3p, wasted_bytes, 0)) / SUM(wasted_bytes) AS pct_3p_wasted_bytes - FROM ( - SELECT - client, - page, - NET.HOST(unminified.url) IS NOT NULL AND NET.HOST(unminified.url) IN ( - SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2025-07-01' AND category != 'hosting' - ) AS is_3p, - unminified.wastedBytes AS wasted_bytes - FROM - `httparchive.all.pages` AS allpages - CROSS JOIN - UNNEST(getUnminifiedJsUrls(JSON_EXTRACT(allpages.lighthouse, "$.audits['unminified-css']"))) AS unminified - WHERE allpages.date = '2025-07-01' AND allpages.is_root_page = TRUE - ) - GROUP BY - client, - page -) -GROUP BY - client diff --git a/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql b/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql deleted file mode 100644 index 43f5f152632..00000000000 --- a/sql/2025/third-parties/lighthouse_average_unminified_js_by_3p.sql +++ /dev/null @@ -1,42 +0,0 @@ -#standardSQL -# Pages with unminified JS by 1P/3P -CREATE TEMPORARY FUNCTION getUnminifiedJsUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(({url, wastedBytes}) => { - return {url, wastedBytes}; - }); -} catch (e) { - return []; -} -'''; - -SELECT - client, - AVG(pct_1p_wasted_bytes) AS avg_pct_1p_wasted_bytes, - AVG(pct_3p_wasted_bytes) AS avg_pct_3p_wasted_bytes -FROM ( - SELECT - client, - page, - SUM(IF(is_3p, 0, wasted_bytes)) / SUM(wasted_bytes) AS pct_1p_wasted_bytes, - SUM(IF(is_3p, wasted_bytes, 0)) / SUM(wasted_bytes) AS pct_3p_wasted_bytes - FROM ( - SELECT - _TABLE_SUFFIX AS client, - lighthouse.url AS page, - NET.HOST(unminified.url) IS NOT NULL AND NET.HOST(unminified.url) IN ( - SELECT domain FROM `httparchive.almanac.third_parties` WHERE date = '2025-07-01' AND category != 'hosting' - ) AS is_3p, - unminified.wastedBytes AS wasted_bytes - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUnminifiedJsUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS unminified - ) - GROUP BY - client, - page -) -GROUP BY - client diff --git a/sql/2025/third-parties/lighthouse_third_party_facades.sql b/sql/2025/third-parties/lighthouse_third_party_facades.sql deleted file mode 100644 index c3884dd3208..00000000000 --- a/sql/2025/third-parties/lighthouse_third_party_facades.sql +++ /dev/null @@ -1,20 +0,0 @@ -SELECT - client, - fail, - total, - pct -FROM ( - SELECT - _TABLE_SUFFIX AS client, - COUNTIF(SAFE_CAST(JSON_VALUE(report, '$.audits.third-party-facades.score') AS FLOAT64) < 0.9) AS fail, - SUM(COUNT(0)) OVER (PARTITION BY _TABLE_SUFFIX) AS total, - COUNTIF(SAFE_CAST(JSON_VALUE(report, '$.audits.third-party-facades.score') AS FLOAT64) < 0.9) / SUM(COUNT(0)) OVER (PARTITION BY _TABLE_SUFFIX) AS pct - FROM - `httparchive.lighthouse.2025_06_01_*` - GROUP BY - client -) -WHERE - total > 100 -ORDER BY - client diff --git a/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql b/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql deleted file mode 100644 index 3e14aa6394c..00000000000 --- a/sql/2025/third-parties/lighthouse_unminified_css_by_3p.sql +++ /dev/null @@ -1,70 +0,0 @@ -#standardSQL -# Third-party pages with unminified CSS - -CREATE TEMPORARY FUNCTION getUnminifiedCssUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(({url, wastedBytes, totalBytes}) => { - return {url, wastedBytes, totalBytes}; - }); -} catch (e) { - return []; -} -'''; - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - potential_third_parties.domain AS domain, - SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, - SUM(IF(third_party_domains.domain IS NOT NULL, transfer_size, 0)) AS third_party_transfer_size - FROM ( - SELECT - _TABLE_SUFFIX AS client, - NET.HOST(data.url) AS domain, - lighthouse.url AS page, - data.wastedBytes AS potential_savings, - data.totalBytes AS transfer_size - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUnminifiedCssUrls(JSON_EXTRACT(report, "$.audits['unminified-css']"))) AS data - ) AS potential_third_parties - LEFT OUTER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page, - domain -) - -SELECT - client, - domain, - COUNT(DISTINCT page) AS total_pages, - SUM(third_party_transfer_size) AS third_party_transfer_size_bytes, - SUM(potential_third_party_savings) AS potential_third_party_savings_bytes, - SUM(potential_third_party_savings) / SUM(third_party_transfer_size) AS pct_potential_third_party_savings, - SUM(potential_third_party_savings) / COUNT(DISTINCT page) AS potential_third_party_savings_bytes_per_page -FROM - base -WHERE - potential_third_party_savings > 0 -GROUP BY - client, - domain -ORDER BY - client, - total_pages DESC, - potential_third_party_savings_bytes_per_page DESC, - domain diff --git a/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql b/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql deleted file mode 100644 index 652b5ebc784..00000000000 --- a/sql/2025/third-parties/lighthouse_unminified_js_by_3p.sql +++ /dev/null @@ -1,70 +0,0 @@ -#standardSQL -# Third-party pages with unminified JavaScript - -CREATE TEMPORARY FUNCTION getUnminifiedJavascriptUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(({url, wastedBytes, totalBytes}) => { - return {url, wastedBytes, totalBytes}; - }); -} catch (e) { - return []; -} -'''; - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - potential_third_parties.domain AS domain, - SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, - SUM(IF(third_party_domains.domain IS NOT NULL, transfer_size, 0)) AS third_party_transfer_size - FROM ( - SELECT - _TABLE_SUFFIX AS client, - NET.HOST(data.url) AS domain, - lighthouse.url AS page, - data.wastedBytes AS potential_savings, - data.totalBytes AS transfer_size - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUnminifiedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS data - ) AS potential_third_parties - LEFT OUTER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page, - domain -) - -SELECT - client, - domain, - COUNT(DISTINCT page) AS total_pages, - SUM(third_party_transfer_size) AS third_party_transfer_size_bytes, - SUM(potential_third_party_savings) AS potential_third_party_savings_bytes, - SUM(potential_third_party_savings) / SUM(third_party_transfer_size) AS pct_potential_third_party_savings, - SUM(potential_third_party_savings) / COUNT(DISTINCT page) AS potential_third_party_savings_bytes_per_page -FROM - base -WHERE - potential_third_party_savings > 0 -GROUP BY - client, - domain -ORDER BY - client, - total_pages DESC, - potential_third_party_savings_bytes_per_page DESC, - domain diff --git a/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql b/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql deleted file mode 100644 index 4398e6c290b..00000000000 --- a/sql/2025/third-parties/lighthouse_unminified_js_by_3p_by_url.sql +++ /dev/null @@ -1,75 +0,0 @@ -#standardSQL -# Third-party pages with unminified JavaScript - -CREATE TEMPORARY FUNCTION getUnminifiedJavascriptUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(({url, wastedBytes, totalBytes}) => { - return {url, wastedBytes, totalBytes}; - }); -} catch (e) { - return []; -} -'''; - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - potential_third_parties.domain AS domain, - potential_third_parties.url AS url, - SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, - SUM(IF(third_party_domains.domain IS NOT NULL, transfer_size, 0)) AS third_party_transfer_size - FROM ( - SELECT - _TABLE_SUFFIX AS client, - NET.HOST(data.url) AS domain, - data.url AS url, - lighthouse.url AS page, - data.wastedBytes AS potential_savings, - data.totalBytes AS transfer_size - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUnminifiedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unminified-javascript']"))) AS data - ) AS potential_third_parties - LEFT OUTER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page, - domain, - url -) - -SELECT - client, - domain, - url, - COUNT(DISTINCT page) AS total_pages, - SUM(third_party_transfer_size) AS third_party_transfer_size_bytes, - SUM(potential_third_party_savings) AS potential_third_party_savings_bytes, - SUM(potential_third_party_savings) / SUM(third_party_transfer_size) AS pct_potential_third_party_savings, - SUM(potential_third_party_savings) / COUNT(DISTINCT page) AS potential_third_party_savings_bytes_per_page -FROM - base -WHERE - potential_third_party_savings > 0 -GROUP BY - client, - domain, - url -ORDER BY - client, - total_pages DESC, - potential_third_party_savings_bytes_per_page DESC, - domain diff --git a/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql b/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql deleted file mode 100644 index 3bd90aa259f..00000000000 --- a/sql/2025/third-parties/lighthouse_unminified_uses_optimized_images_by_3p.sql +++ /dev/null @@ -1,70 +0,0 @@ -#standardSQL -# Third-party pages with unoptimized images - -CREATE TEMPORARY FUNCTION getUnminifiedImageUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(({url, wastedBytes, totalBytes}) => { - return {url, wastedBytes, totalBytes}; - }); -} catch (e) { - return []; -} -'''; - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - potential_third_parties.domain AS domain, - SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, - SUM(IF(third_party_domains.domain IS NOT NULL, transfer_size, 0)) AS third_party_transfer_size - FROM ( - SELECT - _TABLE_SUFFIX AS client, - NET.HOST(data.url) AS domain, - lighthouse.url AS page, - data.wastedBytes AS potential_savings, - data.totalBytes AS transfer_size - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUnminifiedImageUrls(JSON_EXTRACT(report, "$.audits['uses-optimized-images']"))) AS data - ) AS potential_third_parties - LEFT OUTER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page, - domain -) - -SELECT - client, - domain, - COUNT(DISTINCT page) AS total_pages, - SUM(third_party_transfer_size) AS third_party_transfer_size_bytes, - SUM(potential_third_party_savings) AS potential_third_party_savings_bytes, - SUM(potential_third_party_savings) / SUM(third_party_transfer_size) AS pct_potential_third_party_savings, - SUM(potential_third_party_savings) / COUNT(DISTINCT page) AS potential_third_party_savings_bytes_per_page -FROM - base -WHERE - potential_third_party_savings > 0 -GROUP BY - client, - domain -ORDER BY - client, - total_pages DESC, - potential_third_party_savings_bytes_per_page DESC, - domain diff --git a/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql b/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql deleted file mode 100644 index e31fe6af59e..00000000000 --- a/sql/2025/third-parties/lighthouse_unused_css_bytes_by_3p.sql +++ /dev/null @@ -1,70 +0,0 @@ -#bq-tandardSQL -# Third-party pages with unused CSS - -CREATE TEMPORARY FUNCTION getUnusedCssUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(({url, wastedBytes, totalBytes}) => { - return {url, wastedBytes, totalBytes}; - }); -} catch (e) { - return []; -} -'''; - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - potential_third_parties.domain AS domain, - SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, - SUM(IF(third_party_domains.domain IS NOT NULL, transfer_size, 0)) AS third_party_transfer_size - FROM ( - SELECT - _TABLE_SUFFIX AS client, - NET.HOST(data.url) AS domain, - lighthouse.url AS page, - data.wastedBytes AS potential_savings, - data.totalBytes AS transfer_size - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUnusedCssUrls(JSON_EXTRACT(report, "$.audits['unused-css-rules']"))) AS data - ) AS potential_third_parties - LEFT OUTER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page, - domain -) - -SELECT - client, - domain, - COUNT(DISTINCT page) AS total_pages, - SUM(third_party_transfer_size) AS third_party_transfer_size_bytes, - SUM(potential_third_party_savings) AS potential_third_party_savings_bytes, - SUM(potential_third_party_savings) / SUM(third_party_transfer_size) AS pct_potential_third_party_savings, - SUM(potential_third_party_savings) / COUNT(DISTINCT page) AS potential_third_party_savings_bytes_per_page -FROM - base -WHERE - potential_third_party_savings > 0 -GROUP BY - client, - domain -ORDER BY - client, - total_pages DESC, - potential_third_party_savings_bytes_per_page DESC, - domain diff --git a/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql b/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql deleted file mode 100644 index fe45635652e..00000000000 --- a/sql/2025/third-parties/lighthouse_unused_js_bytes_by_3p.sql +++ /dev/null @@ -1,70 +0,0 @@ -#standardSQL -# Third-party pages with unused JavaScript - -CREATE TEMPORARY FUNCTION getUnusedJavascriptUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(({url, wastedBytes, totalBytes}) => { - return {url, wastedBytes, totalBytes}; - }); -} catch (e) { - return []; -} -'''; - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - potential_third_parties.domain AS domain, - SUM(IF(third_party_domains.domain IS NOT NULL, potential_savings, 0)) AS potential_third_party_savings, - SUM(IF(third_party_domains.domain IS NOT NULL, transfer_size, 0)) AS third_party_transfer_size - FROM ( - SELECT - _TABLE_SUFFIX AS client, - NET.HOST(data.url) AS domain, - lighthouse.url AS page, - data.wastedBytes AS potential_savings, - data.totalBytes AS transfer_size - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUnusedJavascriptUrls(JSON_EXTRACT(report, "$.audits['unused-javascript']"))) AS data - ) AS potential_third_parties - LEFT OUTER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page, - domain -) - -SELECT - client, - domain, - COUNT(DISTINCT page) AS total_pages, - SUM(third_party_transfer_size) AS third_party_transfer_size_bytes, - SUM(potential_third_party_savings) AS potential_third_party_savings_bytes, - SUM(potential_third_party_savings) / SUM(third_party_transfer_size) AS pct_potential_third_party_savings, - SUM(potential_third_party_savings) / COUNT(DISTINCT page) AS potential_third_party_savings_bytes_per_page -FROM - base -WHERE - potential_third_party_savings > 0 -GROUP BY - client, - domain -ORDER BY - client, - total_pages DESC, - potential_third_party_savings_bytes_per_page DESC, - domain diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql index 47955ae72cd..b6591449749 100644 --- a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql +++ b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql @@ -3,20 +3,26 @@ WITH requests AS ( SELECT - _TABLE_SUFFIX AS client, - pageid AS page, + client, + page, url FROM - `httparchive.summary_requests.2025_06_01_*` + `httparchive.crawl.requests` + WHERE + date = '2025-07-01' AND + is_root_page ), pages AS ( SELECT - _TABLE_SUFFIX AS client, - pageid AS page, + client, + page, rank FROM - `httparchive.summary_pages.2025_06_01_*` + `httparchive.crawl.pages` + WHERE + date = '2025-07-01' AND + is_root_page ), third_party AS ( @@ -68,10 +74,14 @@ SELECT client, category, rank_grouping, + CASE + WHEN rank_grouping = 100000000 THEN 'all' + ELSE FORMAT("%'d", rank_grouping) + END AS ranking, APPROX_QUANTILES(third_parties_per_page, 1000)[OFFSET(500)] AS p50_third_parties_per_page FROM base, - UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping + UNNEST([1000, 10000, 100000, 1000000, 10000000, 100000000]) AS rank_grouping WHERE rank <= rank_grouping GROUP BY @@ -81,4 +91,4 @@ GROUP BY ORDER BY client, category, - rank_grouping + rank_grouping \ No newline at end of file diff --git a/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql b/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql deleted file mode 100644 index 53832d48692..00000000000 --- a/sql/2025/third-parties/number_of_third_party_providers_by_rank.sql +++ /dev/null @@ -1,78 +0,0 @@ -#standardSQL -# Number of distinct third-party providers per websites by rank -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - pageid AS page, - url - FROM - `httparchive.summary_requests.2025_06_01_*` -), - -pages AS ( - SELECT - _TABLE_SUFFIX AS client, - pageid AS page, - rank - FROM - `httparchive.summary_pages.2025_06_01_*` -), - -third_party AS ( - SELECT - domain, - canonicalDomain, - category, - COUNT(DISTINCT page) AS page_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain, - canonicalDomain, - category - HAVING - page_usage >= 50 -), - -base AS ( - SELECT - client, - page, - rank, - COUNT(DISTINCT canonicalDomain) AS third_parties_per_page - FROM - requests - LEFT JOIN - third_party - ON - NET.HOST(requests.url) = NET.HOST(third_party.domain) - INNER JOIN - pages - USING (client, page) - GROUP BY - client, - page, - rank -) - -SELECT - client, - rank_grouping, - APPROX_QUANTILES(third_parties_per_page, 1000)[OFFSET(500)] AS p50_third_parties_per_page -FROM - base, - UNNEST([1000, 10000, 100000, 1000000, 10000000]) AS rank_grouping -WHERE - rank <= rank_grouping -GROUP BY - client, - rank_grouping -ORDER BY - client, - rank_grouping diff --git a/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql b/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql deleted file mode 100644 index 6684f52575b..00000000000 --- a/sql/2025/third-parties/percent_of_third_parties_using_document_write.sql +++ /dev/null @@ -1,71 +0,0 @@ -#standardSQL -# Percent of third-parties that use document.write - -CREATE TEMPORARY FUNCTION -getUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(i => ({url: i.source.url})); -} catch(e) { - return []; -} -'''; - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - third_party_domains.domain AS domain - FROM - ( - SELECT - _TABLE_SUFFIX AS client, - NET.HOST(data.url) AS domain, - lighthouse.url AS page - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['no-document-write']"))) AS data - ) AS potential_third_parties - INNER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page, - domain -) - -SELECT - base.client AS client, - domain, - COUNT(0) AS freq, - total, - COUNT(0) / total AS pct -FROM - base -JOIN ( - SELECT - _TABLE_SUFFIX AS client, - COUNT(DISTINCT url) AS total - FROM - `httparchive.lighthouse.2025_06_01_*` - GROUP BY - _TABLE_SUFFIX -) -USING (client) -GROUP BY - client, - domain, - total -ORDER BY - client, - freq DESC diff --git a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql deleted file mode 100644 index a1a821420eb..00000000000 --- a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript.sql +++ /dev/null @@ -1,71 +0,0 @@ -#standardSQL -# Percent third-party scripts that use legacy JavaScript - -CREATE TEMPORARY FUNCTION -getUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(i => ({url: i.url})); -} catch(e) { - return []; -} -'''; - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - third_party_domains.domain AS domain - FROM - ( - SELECT - _TABLE_SUFFIX AS client, - NET.HOST(data.url) AS domain, - lighthouse.url AS page - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['legacy-javascript']"))) AS data - ) AS potential_third_parties - INNER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page, - domain -) - -SELECT - base.client AS client, - domain, - COUNT(0) AS freq, - total, - COUNT(0) / total AS pct -FROM - base -JOIN ( - SELECT - _TABLE_SUFFIX AS client, - COUNT(DISTINCT url) AS total - FROM - `httparchive.lighthouse.2025_06_01_*` - GROUP BY - _TABLE_SUFFIX -) -USING (client) -GROUP BY - client, - domain, - total -ORDER BY - client, - freq DESC diff --git a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql b/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql deleted file mode 100644 index 6dc4152465c..00000000000 --- a/sql/2025/third-parties/percent_of_third_parties_using_legacy_javascript_by_url.sql +++ /dev/null @@ -1,88 +0,0 @@ -#standardSQL -# Percent third-party scripts that use legacy JavaScript by URLs - -CREATE TEMPORARY FUNCTION -getUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(i => ({url: i.url})); -} catch(e) { - return []; -} -'''; - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - third_party_domains.domain AS domain, - url - FROM - ( - SELECT - _TABLE_SUFFIX AS client, - data.url AS url, - NET.HOST(data.url) AS domain, - lighthouse.url AS page - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['legacy-javascript']"))) AS data - ) AS potential_third_parties - INNER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page, - domain, - url -) - -SELECT - client, - domain, - url, - freq, - total, - pct -FROM ( - SELECT - base.client AS client, - domain, - url, - COUNT(0) AS freq, - total, - COUNT(0) / total AS pct, - RANK() OVER (PARTITION BY base.client ORDER BY COUNT(0) DESC) AS url_rank - FROM - base - JOIN ( - SELECT - _TABLE_SUFFIX AS client, - COUNT(DISTINCT url) AS total - FROM - `httparchive.lighthouse.2025_06_01_*` - GROUP BY - _TABLE_SUFFIX - ) - USING (client) - GROUP BY - client, - domain, - url, - total -) -WHERE - url_rank <= 100 -ORDER BY - client, - freq DESC diff --git a/sql/2025/third-parties/percent_of_third_party_cache.sql b/sql/2025/third-parties/percent_of_third_party_cache.sql deleted file mode 100644 index 445052b36be..00000000000 --- a/sql/2025/third-parties/percent_of_third_party_cache.sql +++ /dev/null @@ -1,75 +0,0 @@ -#standardSQL -# Percent of third party requests cached -# Cache-Control documentation: https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control#Directives - -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - resp_cache_control, - status, - respOtherHeaders, - reqOtherHeaders, - type, - url, - pageid AS page - FROM - `httparchive.summary_requests.2025_06_01_*` -), - -third_party AS ( - SELECT - domain, - category, - COUNT(DISTINCT page) AS page_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain, - category - HAVING - page_usage >= 50 -), - -base AS ( - SELECT - client, - type, - IF( - ( - status IN (301, 302, 307, 308, 410) AND - NOT REGEXP_CONTAINS(resp_cache_control, r'(?i)private|no-store') AND - NOT REGEXP_CONTAINS(reqOtherHeaders, r'Authorization') - ) OR - ( - status IN (301, 302, 307, 308, 410) OR - REGEXP_CONTAINS(resp_cache_control, r'public|max-age|s-maxage') OR - REGEXP_CONTAINS(respOtherHeaders, r'Expires') - ), 1, 0 - ) AS cached - FROM - requests - LEFT JOIN - third_party - ON - NET.HOST(requests.url) = NET.HOST(third_party.domain) - WHERE - domain IS NOT NULL -) - -SELECT - client, - type, - SUM(cached) AS cached_requests, - COUNT(0) AS total_requests, - SUM(cached) / COUNT(0) AS pct_cached_requests -FROM - base -GROUP BY - client, - type diff --git a/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql b/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql deleted file mode 100644 index 058ca0ac10e..00000000000 --- a/sql/2025/third-parties/percent_of_third_party_requests_and_bytes_by_category_and_content_type.sql +++ /dev/null @@ -1,86 +0,0 @@ -#standardSQL -# Percent of third party requests and bytes by category and content type. - -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - pageid AS page, - url, - type AS contentType, - respBodySize AS body_size - FROM - `httparchive.summary_requests.2025_06_01_*` -), - -third_party AS ( - SELECT - domain, - category, - COUNT(DISTINCT page) AS page_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain, - category - HAVING - page_usage >= 50 -), - -base AS ( - SELECT - client, - page, - category, - contentType, - body_size - FROM - requests - INNER JOIN - third_party - ON - NET.HOST(requests.url) = NET.HOST(third_party.domain) -), - -requests_per_page_and_category AS ( - SELECT - client, - page, - category, - contentType, - SUM(SUM(body_size)) OVER (PARTITION BY page) AS total_page_size, - SUM(body_size) AS body_size, - SUM(COUNT(0)) OVER (PARTITION BY page) AS total_page_requests, - COUNT(0) AS requests - FROM - base - GROUP BY - client, - page, - category, - contentType -) - -SELECT - client, - category, - contentType, - SUM(requests) AS requests, - SAFE_DIVIDE(SUM(requests), SUM(SUM(requests)) OVER (PARTITION BY client, category)) AS pct_requests, - SUM(body_size) AS body_size, - SAFE_DIVIDE(SUM(body_size), SUM(SUM(body_size)) OVER (PARTITION BY client, category)) AS pct_body_size -FROM - requests_per_page_and_category -GROUP BY - client, - category, - contentType -ORDER BY - client, - category, - contentType diff --git a/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql b/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql deleted file mode 100644 index cf340e8d90d..00000000000 --- a/sql/2025/third-parties/percent_of_third_party_with_security_headers.sql +++ /dev/null @@ -1,73 +0,0 @@ -#standardSQL -# Percent of third-party requests with security headers - -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - pageid AS page, - url, - RTRIM(urlShort, '/') AS origin, - respOtherHeaders - FROM - `httparchive.summary_requests.2025_06_01_*` -), - -third_party AS ( - SELECT - domain, - category, - COUNT(DISTINCT page) AS page_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain, - category - HAVING - page_usage >= 50 -), - -headers AS ( - SELECT - client, - requests.origin AS req_origin, - LOWER(respOtherHeaders) AS respOtherHeaders, - third_party.category AS req_category - FROM requests - INNER JOIN third_party - ON NET.HOST(requests.origin) = NET.HOST(third_party.domain) -), - -base AS ( - SELECT - client, - req_origin, - req_category, - IF(STRPOS(respOtherHeaders, 'strict-transport-security') > 0, 1, 0) AS hsts_header, - IF(STRPOS(respOtherHeaders, 'x-content-type-options') > 0, 1, 0) AS x_content_type_options_header, - IF(STRPOS(respOtherHeaders, 'x-frame-options') > 0, 1, 0) AS x_frame_options_header, - IF(STRPOS(respOtherHeaders, 'x-xss-protection') > 0, 1, 0) AS x_xss_protection_header - FROM headers -) - -SELECT - client, - req_category, - COUNT(0) AS total_requests, - SUM(hsts_header) / COUNT(0) AS pct_hsts_header_requests, - SUM(x_content_type_options_header) / COUNT(0) AS pct_x_content_type_options_header_requests, - SUM(x_frame_options_header) / COUNT(0) AS pct_x_frame_options_header_requests, - SUM(x_xss_protection_header) / COUNT(0) AS pct_x_xss_protection_header_requests -FROM - base -GROUP BY - client, - req_category -ORDER BY - client, - req_category diff --git a/sql/2025/third-parties/percent_of_websites_with_third_party.sql b/sql/2025/third-parties/percent_of_websites_with_third_party.sql deleted file mode 100644 index 6bada8aa52c..00000000000 --- a/sql/2025/third-parties/percent_of_websites_with_third_party.sql +++ /dev/null @@ -1,51 +0,0 @@ -#standardSQL -# Percent of websites with third parties - -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - pageid AS page, - url, - respBodySize - FROM - `httparchive.summary_requests.2025_06_01_*` -), - -third_party AS ( - SELECT - domain, - category, - COUNT(DISTINCT page) AS page_usage, - COUNT(0) AS request_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain, - category - HAVING - page_usage > 50 -) - -SELECT - client, - COUNT(DISTINCT IF(domain IS NOT NULL, page, NULL)) AS pages_with_third_party, - COUNT(DISTINCT page) AS total_pages, - COUNT(DISTINCT IF(domain IS NOT NULL, page, NULL)) / COUNT(DISTINCT page) AS pct_pages_with_third_party, - COUNTIF(domain IS NOT NULL) AS third_party_requests, - COUNT(0) AS total_requests, - COUNTIF(domain IS NOT NULL) / COUNT(0) AS pct_third_party_requests, - SUM(IF(domain IS NOT NULL, respBodySize, 0)) AS third_party_body_size, - SUM(respBodySize) AS total_body_size, - SUM(IF(domain IS NOT NULL, respBodySize, 0)) / SUM(respBodySize) AS pct_body_size -FROM - requests -LEFT JOIN third_party -ON NET.HOST(requests.url) = NET.HOST(third_party.domain) -GROUP BY - client diff --git a/sql/2025/third-parties/scripts_using_async_defer.sql b/sql/2025/third-parties/scripts_using_async_defer.sql deleted file mode 100644 index 26c32fb8416..00000000000 --- a/sql/2025/third-parties/scripts_using_async_defer.sql +++ /dev/null @@ -1,76 +0,0 @@ -#standardSQL -# 3P scripts using async or defer -# (capped to 1 hit per domain per page) -CREATE TEMPORARY FUNCTION getScripts(str STRING) -RETURNS ARRAY> -LANGUAGE js AS ''' - try { - var almanac = JSON.parse(str); - - if (Array.isArray(almanac) || typeof almanac != "object") { - return result; - } - - if (almanac.scripts && almanac.scripts.nodes) { - return almanac.scripts.nodes.map((n) => ({ - src: n.src, - isAsync: n.hasOwnProperty("async"), - isDefer: n.hasOwnProperty("defer"), - })); - } - - return []; - } catch (e) { - return []; - } -'''; - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - third_party_domains.domain AS domain, - COUNTIF(isAsync) AS async_count, - COUNTIF(isDefer) AS defer_count - FROM - ( - SELECT - _TABLE_SUFFIX AS client, - NET.HOST(data.src) AS domain, - data.isAsync AS isAsync, - data.isDefer AS isDefer, - pages.url AS page - FROM - `httparchive.pages.2025_06_01_*` AS pages, - UNNEST(getScripts(JSON_EXTRACT_SCALAR(payload, '$._almanac'))) AS data - ) AS potential_third_parties - INNER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page, - domain -) - -SELECT - base.client AS client, - COUNTIF(async_count > 0) AS freq_async, - COUNTIF(defer_count > 0) AS freq_defer, - COUNT(0) AS total, - COUNTIF(async_count > 0) / COUNT(0) AS pct_async, - COUNTIF(defer_count > 0) / COUNT(0) AS pct_defer -FROM - base -GROUP BY - client -ORDER BY - client diff --git a/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql b/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql deleted file mode 100644 index 950bfdc7d27..00000000000 --- a/sql/2025/third-parties/scripts_using_async_defer_by_3p.sql +++ /dev/null @@ -1,81 +0,0 @@ -#standardSQL -# 3P scripts using async or defer -# (capped to 1 hit per domain per page) -CREATE TEMPORARY FUNCTION getScripts(str STRING) -RETURNS ARRAY> -LANGUAGE js AS ''' - try { - var almanac = JSON.parse(str); - - if (Array.isArray(almanac) || typeof almanac != "object") { - return result; - } - - if (almanac.scripts && almanac.scripts.nodes) { - return almanac.scripts.nodes.map((n) => ({ - src: n.src, - isAsync: n.hasOwnProperty("async"), - isDefer: n.hasOwnProperty("defer"), - })); - } - - return []; - } catch (e) { - return []; - } -'''; - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - third_party_domains.domain AS domain, - COUNTIF(isAsync) AS async_count, - COUNTIF(isDefer) AS defer_count - FROM - ( - SELECT - _TABLE_SUFFIX AS client, - NET.HOST(data.src) AS domain, - data.isAsync AS isAsync, - data.isDefer AS isDefer, - pages.url AS page - FROM - `httparchive.pages.2025_06_01_*` AS pages, - UNNEST(getScripts(JSON_EXTRACT_SCALAR(payload, '$._almanac'))) AS data - ) AS potential_third_parties - INNER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page, - domain -) - -SELECT - base.client AS client, - domain, - COUNTIF(async_count > 0) AS freq_async, - COUNTIF(defer_count > 0) AS freq_defer, - COUNT(DISTINCT page) AS page_usage, - COUNTIF(async_count > 0) / COUNT(DISTINCT page) AS pct_async, - COUNTIF(defer_count > 0) / COUNT(DISTINCT page) AS pct_defer -FROM - base -GROUP BY - client, - domain -HAVING - page_usage > 50 -ORDER BY - client, - page_usage DESC diff --git a/sql/2025/third-parties/tao_by_third_party.sql b/sql/2025/third-parties/tao_by_third_party.sql deleted file mode 100644 index 541a32cef8f..00000000000 --- a/sql/2025/third-parties/tao_by_third_party.sql +++ /dev/null @@ -1,105 +0,0 @@ -#standardSQL -# Percent of third-party requests with "Timing-Allow-Origin" headers -# Header reference: https://developer.mozilla.org/docs/Web/HTTP/Headers/Timing-Allow-Origin - -CREATE TEMP FUNCTION get_tao(headers STRING) -RETURNS STRING LANGUAGE js AS ''' - try { - const regex = /timing-allow-origin = (\\*|(http.*?,? )+)/gm; - output = regex.exec(headers)[1]+", "; - output = output.replace(/, , $/, ", "); - return output; - } catch (e) { - return false; - } -'''; - -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - pageid AS page, - url, - RTRIM(urlShort, '/') AS origin, - respOtherHeaders - FROM - `httparchive.summary_requests.2025_06_01_*` -), - -pages AS ( - SELECT - _TABLE_SUFFIX AS client, - url, - pageid AS page, - RTRIM(urlShort, '/') AS origin - FROM - `httparchive.summary_pages.2025_06_01_*` -), - -third_party AS ( - SELECT - domain, - category, - COUNT(DISTINCT page) AS page_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain, - category - HAVING - page_usage >= 50 -), - -headers AS ( - SELECT - requests.client AS client, - requests.origin AS req_origin, - pages.origin AS page_origin, - get_tao(LOWER(respOtherHeaders)) AS timing_allow_origin, - respOtherHeaders, - third_party.category AS req_category - FROM requests - LEFT JOIN pages - USING (client, page) - INNER JOIN third_party - ON NET.HOST(requests.origin) = NET.HOST(third_party.domain) -), - -base AS ( - SELECT - client, - IF(respOtherHeaders LIKE '%timing-allow-origin = %', 1, 0) AS tao_header_present, - IF( - page_origin = req_origin OR - timing_allow_origin = '*' OR - timing_allow_origin LIKE '*,%' OR - timing_allow_origin LIKE '%,*' OR - timing_allow_origin LIKE '%,*,%' OR - timing_allow_origin LIKE '%, *,%' OR - timing_allow_origin = page_origin OR - timing_allow_origin LIKE page_origin || ',' OR - timing_allow_origin LIKE '%,' || page_origin OR - timing_allow_origin LIKE '%, ' || page_origin OR - timing_allow_origin LIKE '%,' || page_origin || ',%' OR - timing_allow_origin LIKE '%, ' || page_origin || ',%', - 1, 0 - ) AS timing_allowed - FROM headers -) - -SELECT - client, - SUM(tao_header_present) AS tao_requests, - SUM(timing_allowed) AS timing_allowed_requests, - COUNT(0) AS total_requests, - SUM(tao_header_present) / COUNT(0) AS pct_tao_requests, - SUM(timing_allowed) / COUNT(0) AS pct_timing_allowed_requests -FROM - base -GROUP BY - client diff --git a/sql/2025/third-parties/third_parties_blocking_main_thread.sql b/sql/2025/third-parties/third_parties_blocking_main_thread.sql deleted file mode 100644 index 58dbc45ffa3..00000000000 --- a/sql/2025/third-parties/third_parties_blocking_main_thread.sql +++ /dev/null @@ -1,71 +0,0 @@ -#standardSQL -# Third-Party domains which block the main thread -# -# As Lighthouse measures all impact there is no need to do a separate total -# Lighthouse also gives a useable category. So no need to use almanac.third-parties table -# -# Based heavily on research by Houssein Djirdeh: -# https://docs.google.com/spreadsheets/d/1Td-4qFjuBzxp8af_if5iBC0Lkqm_OROb7_2OcbxrU_g/edit?resourcekey=0-ZCfve5cngWxF0-sv5pLRzg#gid=1628564987 - -SELECT - client, - domain, - category, - total_pages, - blocking_pages, - non_blocking_pages, - pct_blocking_pages, - pct_non_blocking_pages, - p50_transfer_size_kib, - p50_blocking_time, - total_pages_rank -FROM ( - SELECT - client, - domain, - category, - COUNT(DISTINCT page) AS total_pages, - COUNTIF(blocking > 0) AS blocking_pages, - COUNT(DISTINCT page) - COUNTIF(blocking > 0) AS non_blocking_pages, - COUNTIF(blocking > 0) / COUNT(0) AS pct_blocking_pages, - (COUNT(DISTINCT page) - COUNTIF(blocking > 0)) / COUNT(0) AS pct_non_blocking_pages, - APPROX_QUANTILES(transfer_size_kib, 1000)[OFFSET(500)] AS p50_transfer_size_kib, - APPROX_QUANTILES(blocking_time, 1000)[OFFSET(500)] AS p50_blocking_time, - RANK() OVER (PARTITION BY client ORDER BY COUNT(DISTINCT page) DESC) AS total_pages_rank - FROM ( - SELECT - client, - JSON_VALUE(third_party_items, '$.entity.url') AS domain, - page, - JSON_VALUE(third_party_items, '$.entity.text') AS category, - COUNTIF(SAFE_CAST(JSON_VALUE(report, '$.audits.third-party-summary.details.summary.wastedMs') AS FLOAT64) > 250) AS blocking, - SUM(SAFE_CAST(JSON_VALUE(third_party_items, '$.blockingTime') AS FLOAT64)) AS blocking_time, - SUM(SAFE_CAST(JSON_VALUE(third_party_items, '$.transferSize') AS FLOAT64) / 1024) AS transfer_size_kib - FROM - ( - SELECT - _TABLE_SUFFIX AS client, - url AS page, - report - FROM - `httparchive.lighthouse.2025_06_01_*` - ), - UNNEST(JSON_QUERY_ARRAY(report, '$.audits.third-party-summary.details.items')) AS third_party_items - GROUP BY - client, - domain, - page, - category - ) - GROUP BY - client, - domain, - category - HAVING - total_pages >= 50 -) -WHERE - total_pages_rank <= 200 -ORDER BY - client, - total_pages DESC diff --git a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql deleted file mode 100644 index 81b1777c678..00000000000 --- a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles.sql +++ /dev/null @@ -1,59 +0,0 @@ -#standardSQL -# Total of Third-Party domains which block the main thread by percentile -# -# As Lighthouse measures all impact there is no need to do a separate total -# Lighthouse also gives a useable category. So no need to use almanac.third-parties table -# -# Based heavily on research by Houssein Djirdeh: -# https://docs.google.com/spreadsheets/d/1Td-4qFjuBzxp8af_if5iBC0Lkqm_OROb7_2OcbxrU_g/edit?resourcekey=0-ZCfve5cngWxF0-sv5pLRzg#gid=1628564987 - -SELECT - client, - total_pages, - blocking_pages, - percentile, - p50_transfer_size_kib, - p50_blocking_time -FROM ( - SELECT - client, - COUNT(DISTINCT page) AS total_pages, - COUNTIF(blocking > 0) AS blocking_pages, - percentile, - APPROX_QUANTILES(transfer_size_kib, 1000)[OFFSET(percentile * 10)] AS p50_transfer_size_kib, - APPROX_QUANTILES(blocking_time, 1000)[OFFSET(percentile * 10)] AS p50_blocking_time, - RANK() OVER (PARTITION BY client ORDER BY COUNT(DISTINCT page) DESC) AS total_pages_rank - FROM ( - SELECT - client, - page, - COUNTIF(SAFE_CAST(JSON_VALUE(report, '$.audits.third-party-summary.details.summary.wastedMs') AS FLOAT64) > 250) AS blocking, - SUM(SAFE_CAST(JSON_VALUE(third_party_items, '$.blockingTime') AS FLOAT64)) AS blocking_time, - SUM(SAFE_CAST(JSON_VALUE(third_party_items, '$.transferSize') AS FLOAT64) / 1024) AS transfer_size_kib - FROM - ( - SELECT - _TABLE_SUFFIX AS client, - url AS page, - report - FROM - `httparchive.lighthouse.2025_06_01_*` - ), - UNNEST(JSON_QUERY_ARRAY(report, '$.audits.third-party-summary.details.items')) AS third_party_items - GROUP BY - client, - page - ), - UNNEST([10, 25, 50, 75, 90, 100]) AS percentile - GROUP BY - client, - percentile - HAVING - total_pages >= 50 -) -WHERE - total_pages_rank <= 200 -ORDER BY - client, - total_pages DESC, - percentile diff --git a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql b/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql deleted file mode 100644 index 56905b2577d..00000000000 --- a/sql/2025/third-parties/third_parties_blocking_main_thread_percentiles_by_host.sql +++ /dev/null @@ -1,70 +0,0 @@ -#standardSQL -# Third-Party domains which block the main thread by percentile -# -# As Lighthouse measures all impact there is no need to do a separate total -# Lighthouse also gives a useable category. So no need to use almanac.third-parties table -# -# Based heavily on research by Houssein Djirdeh: -# https://docs.google.com/spreadsheets/d/1Td-4qFjuBzxp8af_if5iBC0Lkqm_OROb7_2OcbxrU_g/edit?resourcekey=0-ZCfve5cngWxF0-sv5pLRzg#gid=1628564987 - -SELECT - client, - domain, - category, - total_pages, - blocking_pages, - percentile, - p50_transfer_size_kib, - p50_blocking_time -FROM ( - SELECT - client, - domain, - category, - COUNT(DISTINCT page) AS total_pages, - COUNTIF(blocking > 0) AS blocking_pages, - percentile, - APPROX_QUANTILES(transfer_size_kib, 1000)[OFFSET(percentile * 10)] AS p50_transfer_size_kib, - APPROX_QUANTILES(blocking_time, 1000)[OFFSET(percentile * 10)] AS p50_blocking_time, - RANK() OVER (PARTITION BY client ORDER BY COUNT(DISTINCT page) DESC) AS total_pages_rank - FROM ( - SELECT - client, - JSON_VALUE(third_party_items, '$.entity.url') AS domain, - page, - JSON_VALUE(third_party_items, '$.entity.text') AS category, - COUNTIF(SAFE_CAST(JSON_VALUE(report, '$.audits.third-party-summary.details.summary.wastedMs') AS FLOAT64) > 250) AS blocking, - SUM(SAFE_CAST(JSON_VALUE(third_party_items, '$.blockingTime') AS FLOAT64)) AS blocking_time, - SUM(SAFE_CAST(JSON_VALUE(third_party_items, '$.transferSize') AS FLOAT64) / 1024) AS transfer_size_kib - FROM - ( - SELECT - _TABLE_SUFFIX AS client, - url AS page, - report - FROM - `httparchive.lighthouse.2025_06_01_*` - ), - UNNEST(JSON_QUERY_ARRAY(report, '$.audits.third-party-summary.details.items')) AS third_party_items - GROUP BY - client, - domain, - page, - category - ), - UNNEST([10, 25, 50, 75, 90, 100]) AS percentile - GROUP BY - client, - domain, - category, - percentile - HAVING - total_pages >= 50 -) -WHERE - total_pages_rank <= 200 -ORDER BY - client, - total_pages DESC, - category, - percentile diff --git a/sql/2025/third-parties/third_parties_blocking_rendering.sql b/sql/2025/third-parties/third_parties_blocking_rendering.sql deleted file mode 100644 index a0c6072bee4..00000000000 --- a/sql/2025/third-parties/third_parties_blocking_rendering.sql +++ /dev/null @@ -1,116 +0,0 @@ -#standardSQL -# Third-Party domains which render block paint -# -# Unlike the blocking main thread queries, lighthouse only contains details if the -# third-party is render blocking (i.e. wastedMs/total_bytes are never 0) -# And also there are no categories given to each third-party -# So we join to the usual almanac.third_parties table to get those totals and categories -# -# Based heavily on research by Houssein Djirdeh: -# https://docs.google.com/spreadsheets/d/1Td-4qFjuBzxp8af_if5iBC0Lkqm_OROb7_2OcbxrU_g/edit?resourcekey=0-ZCfve5cngWxF0-sv5pLRzg#gid=1628564987 - -WITH total_third_party_usage AS ( - SELECT - _TABLE_SUFFIX AS client, - canonicalDomain, - category, - COUNT(DISTINCT pages.url) AS total_pages - FROM - `httparchive.summary_pages.2025_06_01_*` AS pages - INNER JOIN ( - SELECT - _TABLE_SUFFIX AS client, - pageid, - url - FROM - `httparchive.summary_requests.2025_06_01_*` - ) AS requests - ON ( - pages._TABLE_SUFFIX = requests.client AND - pages.pageid = requests.pageid - ) - INNER JOIN - `httparchive.almanac.third_parties` - ON - NET.HOST(requests.url) = NET.HOST(domain) AND - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - client, - canonicalDomain, - category - HAVING - total_pages >= 50 -) - -SELECT - client, - canonicalDomain, - category, - total_pages, - blocking_pages, - non_blocking_pages, - blocking_pages_pct, - non_blocking_pages_pct, - p50_wastedMs, - p50_total_bytes_kib -FROM ( - SELECT - client, - canonicalDomain, - category, - total_pages, - COUNT(DISTINCT page) AS blocking_pages, - total_pages - COUNT(DISTINCT page) AS non_blocking_pages, - COUNT(DISTINCT page) / total_pages AS blocking_pages_pct, - (total_pages - COUNT(DISTINCT page)) / total_pages AS non_blocking_pages_pct, - APPROX_QUANTILES(wasted_ms, 1000)[OFFSET(500)] AS p50_wastedMs, - APPROX_QUANTILES(total_bytes_kib, 1000)[OFFSET(500)] AS p50_total_bytes_kib, - RANK() OVER (PARTITION BY client ORDER BY COUNT(DISTINCT page) DESC) AS total_pages_rank - FROM ( - SELECT - client, - canonicalDomain, - domain, - page, - category, - SUM(SAFE_CAST(JSON_VALUE(renderBlockingItems, '$.wastedMs') AS FLOAT64)) AS wasted_ms, - SUM(SAFE_CAST(JSON_VALUE(renderBlockingItems, '$.totalBytes') AS FLOAT64) / 1024) AS total_bytes_kib - FROM - ( - SELECT - _TABLE_SUFFIX AS client, - url AS page, - report - FROM - `httparchive.lighthouse.2025_06_01_*` - ), - UNNEST(JSON_QUERY_ARRAY(report, '$.audits.render-blocking-resources.details.items')) AS renderBlockingItems - INNER JOIN - `httparchive.almanac.third_parties` - ON - NET.HOST(JSON_VALUE(renderBlockingItems, '$.url')) = domain - GROUP BY - client, - canonicalDomain, - domain, - page, - category - ) - INNER JOIN - total_third_party_usage - USING (client, canonicalDomain, category) - GROUP BY - client, - canonicalDomain, - category, - total_pages - HAVING - total_pages >= 50 -) -WHERE - total_pages_rank <= 200 -ORDER BY - client, - total_pages DESC, - category diff --git a/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql b/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql deleted file mode 100644 index 3811b1865e1..00000000000 --- a/sql/2025/third-parties/third_parties_blocking_rendering_percentiles.sql +++ /dev/null @@ -1,114 +0,0 @@ -#standardSQL -# Third-Party domains which render block paint by percentile -# -# Unlike the blocking main thread queries, lighthouse only contains details if the -# third-party is render blocking (i.e. wastedMs/total_bytes are never 0) -# And also there are no categories given to each third-party -# So we join to the usual almanac.third_parties table to get those totals and categories -# -# Based heavily on research by Houssein Djirdeh: -# https://docs.google.com/spreadsheets/d/1Td-4qFjuBzxp8af_if5iBC0Lkqm_OROb7_2OcbxrU_g/edit?resourcekey=0-ZCfve5cngWxF0-sv5pLRzg#gid=1628564987 - -WITH total_third_party_usage AS ( - SELECT - _TABLE_SUFFIX AS client, - canonicalDomain, - category, - COUNT(DISTINCT pages.url) AS total_pages - FROM - `httparchive.summary_pages.2025_06_01_*` AS pages - INNER JOIN ( - SELECT - _TABLE_SUFFIX AS client, - pageid, - url - FROM - `httparchive.summary_requests.2025_06_01_*` - ) AS requests - ON ( - pages._TABLE_SUFFIX = requests.client AND - pages.pageid = requests.pageid - ) - INNER JOIN - `httparchive.almanac.third_parties` - ON - NET.HOST(requests.url) = NET.HOST(domain) AND - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - client, - canonicalDomain, - category - HAVING - total_pages >= 50 -) - -SELECT - client, - canonicalDomain, - category, - total_pages, - blocking_pages, - percentile, - wasted_ms, - total_bytes_kib -FROM ( - SELECT - client, - canonicalDomain, - category, - total_pages, - COUNT(DISTINCT page) AS blocking_pages, - percentile, - APPROX_QUANTILES(wasted_ms, 1000)[OFFSET(percentile * 10)] AS wasted_ms, - APPROX_QUANTILES(total_bytes_kib, 1000)[OFFSET(percentile * 10)] AS total_bytes_kib, - RANK() OVER (PARTITION BY client ORDER BY COUNT(DISTINCT page) DESC) AS total_pages_rank - FROM ( - SELECT - client, - canonicalDomain, - page, - category, - SUM(SAFE_CAST(JSON_VALUE(render_blocking_items, '$.wastedMs') AS FLOAT64)) AS wasted_ms, - SUM(SAFE_CAST(JSON_VALUE(render_blocking_items, '$.totalBytes') AS FLOAT64) / 1024) AS total_bytes_kib - FROM - ( - SELECT - _TABLE_SUFFIX AS client, - url AS page, - report - FROM - `httparchive.lighthouse.2025_06_01_*` - ), - UNNEST(JSON_QUERY_ARRAY(report, '$.audits.render-blocking-resources.details.items')) AS render_blocking_items - INNER JOIN - `httparchive.almanac.third_parties` - ON - NET.HOST(JSON_VALUE(render_blocking_items, '$.url')) = domain AND - date = '2025-07-01' - GROUP BY - client, - canonicalDomain, - page, - category - ) - INNER JOIN - total_third_party_usage - USING (client, canonicalDomain, category), - UNNEST([10, 25, 50, 75, 90, 100]) AS percentile - GROUP BY - client, - canonicalDomain, - category, - total_pages, - percentile - HAVING - total_pages >= 50 -) -WHERE - total_pages_rank <= 200 -ORDER BY - client, - total_pages DESC, - category, - percentile diff --git a/sql/2025/third-parties/third_parties_using_legacy_javascript.sql b/sql/2025/third-parties/third_parties_using_legacy_javascript.sql deleted file mode 100644 index 7dbe24800c3..00000000000 --- a/sql/2025/third-parties/third_parties_using_legacy_javascript.sql +++ /dev/null @@ -1,57 +0,0 @@ -#standardSQL -# Third-parties that use legacy JavaScript - -CREATE TEMPORARY FUNCTION -getUrls(audit STRING) -RETURNS ARRAY> LANGUAGE js AS ''' -try { - var $ = JSON.parse(audit); - return $.details.items.map(i => ({url: i.url})); -} catch(e) { - return []; -} -'''; - - -WITH third_party_domains AS ( - SELECT DISTINCT - NET.HOST(domain) AS domain - FROM - `httparchive.almanac.third_parties` -), - -base AS ( - SELECT - client, - page, - COUNTIF(third_party_domains.domain IS NULL) / COUNT(0) AS pct_1p_legacy, - COUNTIF(third_party_domains.domain IS NOT NULL) / COUNT(0) AS pct_3p_legacy - FROM - ( - SELECT - _TABLE_SUFFIX AS client, - NET.HOST(data.url) AS domain, - lighthouse.url AS page - FROM - `httparchive.lighthouse.2025_06_01_*` AS lighthouse, - UNNEST(getUrls(JSON_EXTRACT(report, "$.audits['legacy-javascript']"))) AS data - ) AS potential_third_parties - LEFT OUTER JOIN - third_party_domains - ON - potential_third_parties.domain = third_party_domains.domain - GROUP BY - client, - page -) - -SELECT - client, - AVG(pct_1p_legacy) AS avg_pct_1p_legacy, - AVG(pct_3p_legacy) AS avg_pct_3p_legacy -FROM - base -GROUP BY - client -ORDER BY - client diff --git a/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql b/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql deleted file mode 100644 index 73b65d25682..00000000000 --- a/sql/2025/third-parties/top100_third_parties_by_median_body_size_and_time.sql +++ /dev/null @@ -1,87 +0,0 @@ -#standardSQL -# Top 100 third parties by median response body size, time - -WITH requests AS ( - SELECT - _TABLE_SUFFIX AS client, - url, - pageid AS page, - respBodySize AS body_size, - time - FROM - `httparchive.summary_requests.2025_06_01_*` -), - -third_party AS ( - SELECT - domain, - category, - canonicalDomain, - COUNT(DISTINCT page) AS page_usage - FROM - `httparchive.almanac.third_parties` tp - JOIN - requests r - ON NET.HOST(r.url) = NET.HOST(tp.domain) - WHERE - date = '2025-07-01' AND - category != 'hosting' - GROUP BY - domain, - canonicalDomain, - category - HAVING - page_usage >= 50 -), - -base AS ( - SELECT - client, - category, - canonicalDomain, - APPROX_QUANTILES(body_size, 1000)[OFFSET(500)] / 1024 AS median_body_size_kb, - APPROX_QUANTILES(time, 1000)[OFFSET(500)] / 1000 AS median_time_s -- noqa: L010 - FROM - requests - INNER JOIN - third_party - ON - NET.HOST(requests.url) = NET.HOST(third_party.domain) - GROUP BY - client, - category, - canonicalDomain -) - -SELECT - ranking, - client, - category, - canonicalDomain, - metric, - sorted_order -FROM ( - SELECT - 'median_body_size_kb' AS ranking, - client, - category, - canonicalDomain, - median_body_size_kb AS metric, - DENSE_RANK() OVER (PARTITION BY client ORDER BY median_body_size_kb DESC) AS sorted_order - FROM base - UNION ALL - SELECT - 'median_time_s' AS ranking, - client, - category, - canonicalDomain, - median_time_s AS metric, - DENSE_RANK() OVER (PARTITION BY client ORDER BY median_time_s DESC) AS sorted_order - FROM base -) -WHERE - sorted_order <= 100 -ORDER BY - ranking, - client, - metric DESC diff --git a/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql b/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql deleted file mode 100644 index 548aa20da24..00000000000 --- a/sql/2025/third-parties/top20_third_parties_by_client_and_frame_location.sql +++ /dev/null @@ -1,106 +0,0 @@ -#standardSQL -# Top 20 third-parties embedded in mainframe vs. in iframes - -WITH document_frameid AS ( - SELECT - client, - NET.HOST(page) AS page_host, - NET.HOST(url) AS frame_host, - CASE - WHEN is_main_document = true - THEN JSON_EXTRACT_SCALAR(payload, '$._frame_id') - END AS mainframe_id, - JSON_EXTRACT_SCALAR(payload, '$._frame_id') AS frame_id, - is_main_document - FROM `httparchive.all.requests` AS requests - WHERE requests.date = '2025-07-01' AND requests.is_root_page = true -), - -page_frames AS ( - SELECT - client, - page_host, - frame_host, - CASE - WHEN frame_host != page_host - THEN true - ELSE false - END AS tp_flag, - is_main_document, - frame_id, - COALESCE(mainframe_id, FIRST_VALUE(mainframe_id) OVER (PARTITION BY page_host ORDER BY is_main_document DESC)) AS mainframe_id, - CASE - WHEN frame_id = COALESCE(mainframe_id, FIRST_VALUE(mainframe_id) OVER (PARTITION BY page_host ORDER BY is_main_document DESC)) - THEN 'mainframe' - ELSE 'iframe' - END AS frame_type - FROM document_frameid -), - -combined_frame_counts AS ( - SELECT - client, - page_host, - frame_host, - tp_flag, - CASE - WHEN COUNT(DISTINCT frame_type) = 1 AND MAX(CASE WHEN frame_type = 'mainframe' THEN 1 ELSE 0 END) = 1 - THEN 'mainframe-only' - WHEN COUNT(DISTINCT frame_type) = 1 AND MAX(CASE WHEN frame_type = 'iframe' THEN 1 ELSE 0 END) = 1 - THEN 'iframe-only' - WHEN COUNT(DISTINCT frame_id) >= 2 AND COUNT(DISTINCT frame_type) = 2 - THEN 'both' - END AS frame_presence - FROM page_frames - GROUP BY client, page_host, frame_host, tp_flag -), - -grouped_data AS ( - SELECT - client, - frame_host, - COUNT(DISTINCT page_host) AS total_distinct_publisher_count, - COUNT(DISTINCT CASE WHEN frame_presence = 'mainframe-only' AND tp_flag THEN page_host ELSE null END) AS num_distinct_publishers_mainframe_only, - COUNT(DISTINCT CASE WHEN frame_presence = 'iframe-only' AND tp_flag THEN page_host ELSE null END) AS num_distinct_publishers_iframe_only, - COUNT(DISTINCT CASE WHEN frame_presence = 'both' AND tp_flag THEN page_host ELSE null END) AS num_distinct_publishers_both - FROM combined_frame_counts - GROUP BY client, frame_host -), - -ranked_publishers AS ( - SELECT - client, - frame_host, - num_distinct_publishers_mainframe_only, - num_distinct_publishers_iframe_only, - num_distinct_publishers_both, - ROW_NUMBER() OVER (PARTITION BY client ORDER BY num_distinct_publishers_mainframe_only DESC) AS rank_mainframe, - ROW_NUMBER() OVER (PARTITION BY client ORDER BY num_distinct_publishers_iframe_only DESC) AS rank_iframe, - ROW_NUMBER() OVER (PARTITION BY client ORDER BY num_distinct_publishers_both DESC) AS rank_both - FROM grouped_data -) - -SELECT - client, - frame_host, - num_distinct_publishers_mainframe_only AS num_distinct_publishers, - 'mainframe' AS category -FROM ranked_publishers -WHERE rank_mainframe <= 20 AND num_distinct_publishers_mainframe_only > 0 -UNION ALL -SELECT - client, - frame_host, - num_distinct_publishers_iframe_only AS num_distinct_publishers, - 'iframe' AS category -FROM ranked_publishers -WHERE rank_iframe <= 20 AND num_distinct_publishers_iframe_only > 0 -UNION ALL -SELECT - client, - frame_host, - num_distinct_publishers_both AS num_distinct_publishers, - 'both' AS category -FROM ranked_publishers -WHERE rank_both <= 20 AND num_distinct_publishers_both > 0 -ORDER BY client, category, num_distinct_publishers DESC; diff --git a/sql/2025/third-parties/usage_of_lite_youtube_embed.sql b/sql/2025/third-parties/usage_of_lite_youtube_embed.sql deleted file mode 100644 index fa70b5741b4..00000000000 --- a/sql/2025/third-parties/usage_of_lite_youtube_embed.sql +++ /dev/null @@ -1,37 +0,0 @@ -#standardSQL -# Percent of pages using lite-youtube-embed - -WITH totals AS ( - SELECT - _TABLE_SUFFIX AS client, - COUNT(DISTINCT url) AS total_pages - FROM - `httparchive.summary_pages.2025_06_01_*` - GROUP BY - client -), - -youtube_embed AS ( - SELECT - _TABLE_SUFFIX AS client, - COUNT(DISTINCT url) AS youtube_embed_pages - FROM - `httparchive.technologies.2025_06_01_*` - WHERE - app = 'lite-youtube-embed' - GROUP BY - client -) - -SELECT - client, - youtube_embed_pages, - total_pages, - youtube_embed_pages / total_pages AS pct_youtube_embed_pages -FROM - totals -JOIN - youtube_embed -USING (client) -ORDER BY - client diff --git a/sql/2025/third-parties/usage_of_partytown.sql b/sql/2025/third-parties/usage_of_partytown.sql deleted file mode 100644 index 251830b1870..00000000000 --- a/sql/2025/third-parties/usage_of_partytown.sql +++ /dev/null @@ -1,37 +0,0 @@ -#standardSQL -# Percent of pages using Partytown - -WITH totals AS ( - SELECT - _TABLE_SUFFIX AS client, - COUNT(DISTINCT url) AS total_pages - FROM - `httparchive.summary_pages.2025_06_01_*` - GROUP BY - client -), - -partytown AS ( - SELECT - _TABLE_SUFFIX AS client, - COUNT(DISTINCT url) AS partytown_pages - FROM - `httparchive.technologies.2025_06_01_*` - WHERE - app = 'Partytown' - GROUP BY - client -) - -SELECT - client, - partytown_pages, - total_pages, - partytown_pages / total_pages AS pct_partytown_pages -FROM - totals -JOIN - partytown -USING (client) -ORDER BY - client From 750602cb7fb3e889bf1df95dd48fa25465546858 Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Sun, 11 Jan 2026 07:29:21 -0800 Subject: [PATCH 32/37] Fixed sql file name for third party providers --- src/content/en/2025/third-parties.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/en/2025/third-parties.md b/src/content/en/2025/third-parties.md index a12d518d9ac..038243cfc87 100644 --- a/src/content/en/2025/third-parties.md +++ b/src/content/en/2025/third-parties.md @@ -131,7 +131,7 @@ Year-over-year, third-party requests have increased across all ranks. The top 1, description="Bar chart showing distribution of third party categories by rank groups. Top categories are consent provider, video, and customer success.", chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=1133634663&format=interactive", sheets_gid="445864775", - sql_file="number_of_third_parties_by_rank_and_category.sql" + sql_file="number_of_third_party_providers_by_rank_and_category.sql" ) }} From a794c8b30a11882a978931363fe96545493029e7 Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan Date: Sun, 11 Jan 2026 07:34:41 -0800 Subject: [PATCH 33/37] Added newline at the end of the file --- .../number_of_third_parties_by_rank_and_category.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql index b6591449749..b8cb7d06d6f 100644 --- a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql +++ b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql @@ -91,4 +91,5 @@ GROUP BY ORDER BY client, category, - rank_grouping \ No newline at end of file + rank_grouping + \ No newline at end of file From 80816302eaf49962e74fce2c79f39ee0ba4a7bd0 Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sun, 11 Jan 2026 16:05:39 +0000 Subject: [PATCH 34/37] Update sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql --- .../number_of_third_parties_by_rank_and_category.sql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql index b8cb7d06d6f..b6591449749 100644 --- a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql +++ b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql @@ -91,5 +91,4 @@ GROUP BY ORDER BY client, category, - rank_grouping - \ No newline at end of file + rank_grouping \ No newline at end of file From 11879928279ec05d159200b37b591ddaf4d48ccb Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Sun, 11 Jan 2026 16:12:03 +0000 Subject: [PATCH 35/37] Update sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql --- .../number_of_third_parties_by_rank_and_category.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql index b6591449749..0e3e8711004 100644 --- a/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql +++ b/sql/2025/third-parties/number_of_third_parties_by_rank_and_category.sql @@ -91,4 +91,4 @@ GROUP BY ORDER BY client, category, - rank_grouping \ No newline at end of file + rank_grouping From 9d6616b662637b1ac02a502526cb62ccf9f301b1 Mon Sep 17 00:00:00 2001 From: AbuBakar Aziz Date: Sun, 11 Jan 2026 15:24:32 -0500 Subject: [PATCH 36/37] Incorporate feedback in text --- src/content/en/2025/third-parties.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/content/en/2025/third-parties.md b/src/content/en/2025/third-parties.md index 038243cfc87..c8572a8e4e3 100644 --- a/src/content/en/2025/third-parties.md +++ b/src/content/en/2025/third-parties.md @@ -23,7 +23,7 @@ doi: TODO ## Introduction -Third parties are ubiquitous on the web. Website developers rely on them to implement key features such as advertising, analytics, social media integration, payment processing, and content delivery. This modular approach enables efficient, rapid deployment of rich functionality. However, it introduces potential privacy, security, and performance concerns. +Third parties are ubiquitous on the web. Website developers rely on them to implement key features such as advertising, analytics, social media integration, payment processing, and content delivery. This modular approach enables efficient and rapid deployment of rich functionality. However, it introduces potential privacy, security, and performance concerns. New this year, we analyze how user consent choices are propagated among third parties on the web, including the consent frameworks used and the third parties that receive these signals. In this chapter, we conduct an empirical analysis of third-party usage patterns on the web. We examine: @@ -128,14 +128,14 @@ Year-over-year, third-party requests have increased across all ranks. The top 1, {{ figure_markup( image="3p-req-categories-by-rank.png", caption="Distribution of the third-party request categories by rank.", - description="Bar chart showing distribution of third party categories by rank groups. Top categories are consent provider, video, and customer success.", + description="Bar chart showing distribution of third-party categories by rank group. The top categories are ad, analytics, and cdn.", chart_url="https://docs.google.com/spreadsheets/d/e/2PACX-1vTrElluFB6gvlkt65HjzZMJ4PtgJ53tVnez46cBrhQNtNxUjDxvNPuS_xmlQBUmhSHZkOMAjd0bTJyr/pubchart?oid=1133634663&format=interactive", sheets_gid="445864775", sql_file="number_of_third_party_providers_by_rank_and_category.sql" ) }} -The top categories include `ad`, `analytics` and `cdn` categories. +The bar chart shows the median number of third-party providers per page by rank and category. In the previous edition, this analysis focused on the number of third-party domains per page by rank and category, whereas this year we measure the number of unique third-party providers, which results in lower counts overall. This year, the top categories are `ad`, `analytics`, and `cdn`. {{ figure_markup( image="3p-req-types-by-rank.png", @@ -147,7 +147,7 @@ The top categories include `ad`, `analytics` and `cdn` categories. ) }} -The three most common third-party request types are `script`, `image`, and `other`. +The chart shows that third-party requests are dominated by `script`, `image`, and the `other` category. Together, `script`, `image`, and `other` account for more than half of all third-party request content types. This pattern is consistent with the 2024 edition, which also identified `script`, `image`, and `other` as the top request types, indicating little change since last year. {{ figure_markup( image="top-3p-by-num-pages.png", From 08386496478416a2c35865141d374413b4b492e1 Mon Sep 17 00:00:00 2001 From: Muhammad Jazlan <64159559+jazlan01@users.noreply.github.com> Date: Sun, 11 Jan 2026 15:08:38 -0800 Subject: [PATCH 37/37] Update src/content/en/2025/third-parties.md with link to 2024 chapter Co-authored-by: Barry Pollard --- src/content/en/2025/third-parties.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/en/2025/third-parties.md b/src/content/en/2025/third-parties.md index c8572a8e4e3..0a30297b786 100644 --- a/src/content/en/2025/third-parties.md +++ b/src/content/en/2025/third-parties.md @@ -147,7 +147,7 @@ The bar chart shows the median number of third-party providers per page by rank ) }} -The chart shows that third-party requests are dominated by `script`, `image`, and the `other` category. Together, `script`, `image`, and `other` account for more than half of all third-party request content types. This pattern is consistent with the 2024 edition, which also identified `script`, `image`, and `other` as the top request types, indicating little change since last year. +The chart shows that third-party requests are dominated by `script`, `image`, and the `other` category. Together, `script`, `image`, and `other` account for more than half of all third-party request content types. This pattern is consistent with [the 2024 edition](../2024/third-parties#fig-5), which also identified `script`, `image`, and `other` as the top request types, indicating little change since last year. {{ figure_markup( image="top-3p-by-num-pages.png",