diff --git a/src/rewriters/custom/head.js b/src/rewriters/custom/head.js index 61f845b..fdd6b6e 100644 --- a/src/rewriters/custom/head.js +++ b/src/rewriters/custom/head.js @@ -100,27 +100,66 @@ window.history.replaceState = new Proxy(window.history.replaceState, { }, }); (function () { - const replacedUrl = function (url) { + // Notion fires fire-and-forget telemetry and analytics to these hosts. Nooxy + // previously bounced them off the proxy via a same-origin pseudo endpoint, + // which produced a noisy 200 log line for every request. We now drop them + // entirely in the browser so no network request is made and nothing reaches + // the proxy. These responses were already discarded, so the page is unaffected. + const pathOf = function (url) { + const noScheme = url.replace(/^https?:\/\/[^\\/]*/, ''); + return noScheme.split('?')[0].split('#')[0]; + }; + + const isSuppressedUrl = function (url) { + // Same-origin Notion keepalive ping is non-essential and only adds proxy log noise. + if (pathOf(url) === '/api/v3/ping') { + return true; + } const match = /^https?:\/\/([^\\/]*)/.exec(url); const domain = match ? match[1] : ''; - if ( + // file.notion.so and file.notion.com serve signed PDF and attachment downloads. + // They are real content, not telemetry, so they must not be dropped. The blanket + // notion.so rule below would otherwise leave react-pdf with an empty body. Keep + // this comment slash-free: the converter mis-parses a bare slash as a regex. + if (domain === 'file.notion.so' || domain === 'file.notion.com') { + return false; + } + return ( (domain.endsWith('notion.so') && !domain.endsWith('msgstore.www.notion.so')) || domain.endsWith('splunkcloud.com') || domain.endsWith('statsigapi.net') - ) { - // console.info('[NOOXY]', 'Suppress request:', url); - return url.replace(/^.*:(.*)\/\//, '/200/$1'); + ); + }; + + const urlOf = function (input) { + if (typeof input === 'string') { + return input; + } + // Request object + if (input && typeof input.url === 'string') { + return input.url; + } + try { + return String(input); + } catch (_e) { + return ''; } - return url; }; window.fetch = new Proxy(window.fetch, { apply: function (target, that, args) { - let url = args[0]; - const rest = Array.prototype.slice.call(args, 1); - url = replacedUrl(url); - // console.info('[NOOXY]', 'Fetch Request:', url); - return Reflect.apply(target, that, [url].concat(rest)); + if (isSuppressedUrl(urlOf(args[0]))) { + // Silently drop: resolve with a synthetic success, make no network call. + // console.info('[NOOXY]', 'Dropped request:', urlOf(args[0])); + return Promise.resolve( + new Response('{}', { + status: 200, + statusText: 'OK', + headers: { 'content-type': 'application/json' }, + }), + ); + } + return Reflect.apply(target, that, args); }, }); @@ -130,10 +169,14 @@ window.history.replaceState = new Proxy(window.history.replaceState, { xhr.open = new Proxy(xhr.open, { apply: function (target, that, args) { const method = args[0]; - let url = args[1]; + const url = args[1]; const rest = Array.prototype.slice.call(args, 2); - url = replacedUrl(url); - // console.info('[NOOXY]', 'XML Request:', url); + if (isSuppressedUrl(urlOf(url))) { + // Resolve locally via a data: URL (allowed by connect-src 'data:') so + // the request never leaves the browser and never hits the proxy. + // console.info('[NOOXY]', 'Dropped request:', urlOf(url)); + return Reflect.apply(target, that, ['GET', 'data:application/json,%7B%7D'].concat(rest)); + } return Reflect.apply(target, that, [method, url].concat(rest)); }, });