Repository files navigation

Timezone Guard

Firefox Add-on

Читать на русском

A Firefox extension that spoofs the time zone and (optionally) the User-Agent reported by the browser to websites — across all contexts: the main page, iframes (any nesting depth), Dedicated Worker, Shared Worker, and Service Worker.

Why

Websites infer approximate location and browser characteristics via Intl.DateTimeFormat, Date.prototype.getTimezoneOffset, navigator.userAgent, and similar APIs — and they do this not only in the main window but also inside workers, where ordinary time-zone spoofing extensions usually can't reach. This extension patches all these points consistently, so every context returns the same spoofed values.

Features

  • Time zone spoofingIntl.DateTimeFormat, Date.prototype.getTimezoneOffset, toString / toDateString / toTimeString / toLocaleString / toLocaleDateString / toLocaleTimeString
  • Optional User-Agent spoofingnavigator.userAgent, appVersion, platform, vendor, language, languages, maxTouchPoints, oscpu (Firefox), and related properties
  • HTTP header spoofing — optionally modifies outgoing Accept-Language and User-Agent headers to match the selected timezone and browser identifier
  • Consistent across all contexts — main window, iframe (including nested iframe-within-iframe), Dedicated Worker, Shared Worker, Service Worker
  • Real system clock untouchedDate.now() and the actual OS clock are not modified; only how the date is interpreted and displayed in the chosen time zone is changed
  • Cached formattersIntl.DateTimeFormat and Intl.NumberFormat formatters are cached to avoid timing detection (important against detectors like CreepJS)
  • CSP-safe — works on sites with strict Content-Security-Policy (Spotify Web Player and similar) via declarative injection through world: "MAIN"
  • No enumerable globals — all shared state lives inside the closure of a single file and is not enumerable from the outside
  • Temporal API support — patches Temporal.Now.timeZoneId and related methods when available
  • Number format spoofingIntl.NumberFormat and Number.prototype.toLocaleString use the spoofed locale

How it works

Main page and iframes — spoof/main.js

A single content script injected by the browser directly into the page's main world (world: "MAIN" in the manifest). This bypasses the page's CSP, since such injection is not treated as code added by the page itself.

  • Intl.DateTimeFormat, Intl.NumberFormat, and Date.prototype methods are patched once per window, with cached formatters.
  • navigator.userAgent and related properties are patched via Object.defineProperty on Navigator.prototype.
  • All patched functions are masked as [native code] via a custom toString wrapper.
  • iframe: instead of MutationObserver (which is always asynchronous), the getter contentWindow / contentDocument on HTMLIFrameElement.prototype is patched — the spoof is applied at the moment of access, not at DOM insertion. Recursive patching handles iframe-within-iframe.
  • Dedicated / Shared Worker: the Worker / SharedWorker constructors are wrapped so that our patch runs before the worker's original script, loaded via importScripts() — without blocking synchronous XHR on the main thread.
  • Service Worker: navigator.serviceWorker.register() is intercepted to learn the exact URL of the worker script, sent to the background via bridge.js.

bridge.js (isolated world)

Reads saved tz / ua / enabled / spoofHeaders from browser.storage.local and passes them to main.js through three mechanisms:

  1. DOM attributes on <html> — available immediately at document_start.
  2. sessionStorage / localStorage fallback (__tzGuardConfig) — survives page reloads and bfcache restores.
  3. CustomEvent (tz-guard-update-config) — for live updates when settings change in the popup.

background/background.js + background/build_worker.js

Service Workers cannot be patched via blob: URLs (forbidden by spec). Therefore network-response interception is used:

  • webRequest.onBeforeRequest + webRequest.filterResponseData (requires webRequestFilterResponse.serviceWorkerScript) splices the patch code into the beginning of the Service Worker script response.
  • The filter must be installed in onBeforeRequest, not later, to avoid Firefox's internal script byte-cache (Invalid request ID).
  • Known Service Worker URLs are persisted in storage.local (swUrls) so patches survive browser restarts.

HTTP Header Spoofing

When "Spoof HTTP Headers" is enabled in the popup, the extension modifies outgoing request headers via webRequest.onBeforeSendHeaders:

  • Accept-Language — derived from the selected timezone (e.g., Europe/Helsinkifi-FI,fi;q=0.9,en-US;q=0.8,en;q=0.7).
  • User-Agent — set to the custom UA if provided.

Popup settings

  • Extension Enabled — master toggle; disables all spoofing when turned off.
  • Spoof HTTP Headers — toggles modification of outgoing Accept-Language and User-Agent headers.
  • Timezone — select the timezone to report.
  • User-Agent — choose from presets or enter a custom string.

Installation (development / temporary extension)

  1. about:debugging#/runtime/this-firefoxLoad Temporary Add-on → select manifest.json.
  2. Open the popup, choose a time zone (and optionally a User-Agent), adjust toggles, Save & Apply.
  3. Fully close and reopen the tab (not just F5 — temporary extensions in Firefox get a new moz-extension:// UUID on every reload).

Project structure

manifest.json
bridge.js — isolated world: reads storage, passes config via DOM + storage events
spoof/
main.js — all patches (Intl/Date/UA/iframe/Worker/SharedWorker/Temporal), one file, one IIFE
background/
background.js — network response interception, header spoofing, SW URL tracking
build_worker.js — patch-code generator for Service Worker injection (module)
frontend/
popup.html
popup.js
icons/

Permissions

  • storage — saves the selected tz/ua and settings locally; persists known SW URLs.
  • <all_urls> (host permission) — the spoof must apply on any site, before the site's own scripts run.
  • webRequest, webRequestBlocking, webRequestFilterResponse, webRequestFilterResponse.serviceWorkerScript — required for patching Service Workers via network response interception and for HTTP header modification.

Known limitations

  • Indexed access window[n] / window.frames[n] — numeric index access is exotic internal WindowProxy behavior, not an ordinary JS property. Object.defineProperty cannot intercept it. The first such access returns real data; subsequent accesses through .contentWindow are already patched.
  • "Dead" (detached) iframes — a reference to a removed iframe's window, obtained through a non-standard access path, may remain unpatched.
  • Live updates require reload — after changing tz/ua in the popup, open tabs must be reloaded. The spoof is applied at page load time, not retroactively (although an already open tab will pick up new values for future API calls if the popup was opened before the page reload).
  • OS clock untouched — the extension only changes what is reported through JS APIs and HTTP headers. The operating system clock and time zone are not modified.

Comparison (Before / After)

BeforeAfter
BrowserLeaks — HTTP HeadersBeforeAfter
BrowserLeaks — JS APIsBeforeAfter
CreepJS — FingerprintBeforeAfter
CreepJS — TimezoneBeforeAfter
CreepJS — WorkersBeforeAfter

License

MIT

About

Firefox extension that spoofs the timezone reported to websites (Intl.DateTimeFormat, Date APIs) to protect against fingerprinting. Works alongside privacy.resistFingerprinting, tested on YouTube and Spotify.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Timezone Guard

Firefox Add-on

Читать на русском

A Firefox extension that spoofs the time zone and (optionally) the User-Agent reported by the browser to websites — across all contexts: the main page, iframes (any nesting depth), Dedicated Worker, Shared Worker, and Service Worker.

Why

Websites infer approximate location and browser characteristics via Intl.DateTimeFormat, Date.prototype.getTimezoneOffset, navigator.userAgent, and similar APIs — and they do this not only in the main window but also inside workers, where ordinary time-zone spoofing extensions usually can't reach. This extension patches all these points consistently, so every context returns the same spoofed values.

Features

  • Time zone spoofingIntl.DateTimeFormat, Date.prototype.getTimezoneOffset, toString / toDateString / toTimeString / toLocaleString / toLocaleDateString / toLocaleTimeString
  • Optional User-Agent spoofingnavigator.userAgent, appVersion, platform, vendor, language, languages, maxTouchPoints, oscpu (Firefox), and related properties
  • HTTP header spoofing — optionally modifies outgoing Accept-Language and User-Agent headers to match the selected timezone and browser identifier
  • Consistent across all contexts — main window, iframe (including nested iframe-within-iframe), Dedicated Worker, Shared Worker, Service Worker
  • Real system clock untouchedDate.now() and the actual OS clock are not modified; only how the date is interpreted and displayed in the chosen time zone is changed
  • Cached formattersIntl.DateTimeFormat and Intl.NumberFormat formatters are cached to avoid timing detection (important against detectors like CreepJS)
  • CSP-safe — works on sites with strict Content-Security-Policy (Spotify Web Player and similar) via declarative injection through world: "MAIN"
  • No enumerable globals — all shared state lives inside the closure of a single file and is not enumerable from the outside
  • Temporal API support — patches Temporal.Now.timeZoneId and related methods when available
  • Number format spoofingIntl.NumberFormat and Number.prototype.toLocaleString use the spoofed locale

How it works

Main page and iframes — spoof/main.js

A single content script injected by the browser directly into the page's main world (world: "MAIN" in the manifest). This bypasses the page's CSP, since such injection is not treated as code added by the page itself.

  • Intl.DateTimeFormat, Intl.NumberFormat, and Date.prototype methods are patched once per window, with cached formatters.
  • navigator.userAgent and related properties are patched via Object.defineProperty on Navigator.prototype.
  • All patched functions are masked as [native code] via a custom toString wrapper.
  • iframe: instead of MutationObserver (which is always asynchronous), the getter contentWindow / contentDocument on HTMLIFrameElement.prototype is patched — the spoof is applied at the moment of access, not at DOM insertion. Recursive patching handles iframe-within-iframe.
  • Dedicated / Shared Worker: the Worker / SharedWorker constructors are wrapped so that our patch runs before the worker's original script, loaded via importScripts() — without blocking synchronous XHR on the main thread.
  • Service Worker: navigator.serviceWorker.register() is intercepted to learn the exact URL of the worker script, sent to the background via bridge.js.

bridge.js (isolated world)

Reads saved tz / ua / enabled / spoofHeaders from browser.storage.local and passes them to main.js through three mechanisms:

  1. DOM attributes on <html> — available immediately at document_start.
  2. sessionStorage / localStorage fallback (__tzGuardConfig) — survives page reloads and bfcache restores.
  3. CustomEvent (tz-guard-update-config) — for live updates when settings change in the popup.

background/background.js + background/build_worker.js

Service Workers cannot be patched via blob: URLs (forbidden by spec). Therefore network-response interception is used:

  • webRequest.onBeforeRequest + webRequest.filterResponseData (requires webRequestFilterResponse.serviceWorkerScript) splices the patch code into the beginning of the Service Worker script response.
  • The filter must be installed in onBeforeRequest, not later, to avoid Firefox's internal script byte-cache (Invalid request ID).
  • Known Service Worker URLs are persisted in storage.local (swUrls) so patches survive browser restarts.

HTTP Header Spoofing

When "Spoof HTTP Headers" is enabled in the popup, the extension modifies outgoing request headers via webRequest.onBeforeSendHeaders:

  • Accept-Language — derived from the selected timezone (e.g., Europe/Helsinkifi-FI,fi;q=0.9,en-US;q=0.8,en;q=0.7).
  • User-Agent — set to the custom UA if provided.

Popup settings

  • Extension Enabled — master toggle; disables all spoofing when turned off.
  • Spoof HTTP Headers — toggles modification of outgoing Accept-Language and User-Agent headers.
  • Timezone — select the timezone to report.
  • User-Agent — choose from presets or enter a custom string.

Installation (development / temporary extension)

  1. about:debugging#/runtime/this-firefoxLoad Temporary Add-on → select manifest.json.
  2. Open the popup, choose a time zone (and optionally a User-Agent), adjust toggles, Save & Apply.
  3. Fully close and reopen the tab (not just F5 — temporary extensions in Firefox get a new moz-extension:// UUID on every reload).

Project structure

manifest.json
bridge.js — isolated world: reads storage, passes config via DOM + storage events
spoof/
main.js — all patches (Intl/Date/UA/iframe/Worker/SharedWorker/Temporal), one file, one IIFE
background/
background.js — network response interception, header spoofing, SW URL tracking
build_worker.js — patch-code generator for Service Worker injection (module)
frontend/
popup.html
popup.js
icons/

Permissions

  • storage — saves the selected tz/ua and settings locally; persists known SW URLs.
  • <all_urls> (host permission) — the spoof must apply on any site, before the site's own scripts run.
  • webRequest, webRequestBlocking, webRequestFilterResponse, webRequestFilterResponse.serviceWorkerScript — required for patching Service Workers via network response interception and for HTTP header modification.

Known limitations

  • Indexed access window[n] / window.frames[n] — numeric index access is exotic internal WindowProxy behavior, not an ordinary JS property. Object.defineProperty cannot intercept it. The first such access returns real data; subsequent accesses through .contentWindow are already patched.
  • "Dead" (detached) iframes — a reference to a removed iframe's window, obtained through a non-standard access path, may remain unpatched.
  • Live updates require reload — after changing tz/ua in the popup, open tabs must be reloaded. The spoof is applied at page load time, not retroactively (although an already open tab will pick up new values for future API calls if the popup was opened before the page reload).
  • OS clock untouched — the extension only changes what is reported through JS APIs and HTTP headers. The operating system clock and time zone are not modified.

Comparison (Before / After)

BeforeAfter
BrowserLeaks — HTTP HeadersBeforeAfter
BrowserLeaks — JS APIsBeforeAfter
CreepJS — FingerprintBeforeAfter
CreepJS — TimezoneBeforeAfter
CreepJS — WorkersBeforeAfter

License

MIT

About

Firefox extension that spoofs the timezone reported to websites (Intl.DateTimeFormat, Date APIs) to protect against fingerprinting. Works alongside privacy.resistFingerprinting, tested on YouTube and Spotify.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Timezone Guard

Firefox Add-on

Читать на русском

A Firefox extension that spoofs the time zone and (optionally) the User-Agent reported by the browser to websites — across all contexts: the main page, iframes (any nesting depth), Dedicated Worker, Shared Worker, and Service Worker.

Why

Websites infer approximate location and browser characteristics via Intl.DateTimeFormat, Date.prototype.getTimezoneOffset, navigator.userAgent, and similar APIs — and they do this not only in the main window but also inside workers, where ordinary time-zone spoofing extensions usually can't reach. This extension patches all these points consistently, so every context returns the same spoofed values.

Features

  • Time zone spoofingIntl.DateTimeFormat, Date.prototype.getTimezoneOffset, toString / toDateString / toTimeString / toLocaleString / toLocaleDateString / toLocaleTimeString
  • Optional User-Agent spoofingnavigator.userAgent, appVersion, platform, vendor, language, languages, maxTouchPoints, oscpu (Firefox), and related properties
  • HTTP header spoofing — optionally modifies outgoing Accept-Language and User-Agent headers to match the selected timezone and browser identifier
  • Consistent across all contexts — main window, iframe (including nested iframe-within-iframe), Dedicated Worker, Shared Worker, Service Worker
  • Real system clock untouchedDate.now() and the actual OS clock are not modified; only how the date is interpreted and displayed in the chosen time zone is changed
  • Cached formattersIntl.DateTimeFormat and Intl.NumberFormat formatters are cached to avoid timing detection (important against detectors like CreepJS)
  • CSP-safe — works on sites with strict Content-Security-Policy (Spotify Web Player and similar) via declarative injection through world: "MAIN"
  • No enumerable globals — all shared state lives inside the closure of a single file and is not enumerable from the outside
  • Temporal API support — patches Temporal.Now.timeZoneId and related methods when available
  • Number format spoofingIntl.NumberFormat and Number.prototype.toLocaleString use the spoofed locale

How it works

Main page and iframes — spoof/main.js

A single content script injected by the browser directly into the page's main world (world: "MAIN" in the manifest). This bypasses the page's CSP, since such injection is not treated as code added by the page itself.

  • Intl.DateTimeFormat, Intl.NumberFormat, and Date.prototype methods are patched once per window, with cached formatters.
  • navigator.userAgent and related properties are patched via Object.defineProperty on Navigator.prototype.
  • All patched functions are masked as [native code] via a custom toString wrapper.
  • iframe: instead of MutationObserver (which is always asynchronous), the getter contentWindow / contentDocument on HTMLIFrameElement.prototype is patched — the spoof is applied at the moment of access, not at DOM insertion. Recursive patching handles iframe-within-iframe.
  • Dedicated / Shared Worker: the Worker / SharedWorker constructors are wrapped so that our patch runs before the worker's original script, loaded via importScripts() — without blocking synchronous XHR on the main thread.
  • Service Worker: navigator.serviceWorker.register() is intercepted to learn the exact URL of the worker script, sent to the background via bridge.js.

bridge.js (isolated world)

Reads saved tz / ua / enabled / spoofHeaders from browser.storage.local and passes them to main.js through three mechanisms:

  1. DOM attributes on <html> — available immediately at document_start.
  2. sessionStorage / localStorage fallback (__tzGuardConfig) — survives page reloads and bfcache restores.
  3. CustomEvent (tz-guard-update-config) — for live updates when settings change in the popup.

background/background.js + background/build_worker.js

Service Workers cannot be patched via blob: URLs (forbidden by spec). Therefore network-response interception is used:

  • webRequest.onBeforeRequest + webRequest.filterResponseData (requires webRequestFilterResponse.serviceWorkerScript) splices the patch code into the beginning of the Service Worker script response.
  • The filter must be installed in onBeforeRequest, not later, to avoid Firefox's internal script byte-cache (Invalid request ID).
  • Known Service Worker URLs are persisted in storage.local (swUrls) so patches survive browser restarts.

HTTP Header Spoofing

When "Spoof HTTP Headers" is enabled in the popup, the extension modifies outgoing request headers via webRequest.onBeforeSendHeaders:

  • Accept-Language — derived from the selected timezone (e.g., Europe/Helsinkifi-FI,fi;q=0.9,en-US;q=0.8,en;q=0.7).
  • User-Agent — set to the custom UA if provided.

Popup settings

  • Extension Enabled — master toggle; disables all spoofing when turned off.
  • Spoof HTTP Headers — toggles modification of outgoing Accept-Language and User-Agent headers.
  • Timezone — select the timezone to report.
  • User-Agent — choose from presets or enter a custom string.

Installation (development / temporary extension)

  1. about:debugging#/runtime/this-firefoxLoad Temporary Add-on → select manifest.json.
  2. Open the popup, choose a time zone (and optionally a User-Agent), adjust toggles, Save & Apply.
  3. Fully close and reopen the tab (not just F5 — temporary extensions in Firefox get a new moz-extension:// UUID on every reload).

Project structure

manifest.json
bridge.js — isolated world: reads storage, passes config via DOM + storage events
spoof/
main.js — all patches (Intl/Date/UA/iframe/Worker/SharedWorker/Temporal), one file, one IIFE
background/
background.js — network response interception, header spoofing, SW URL tracking
build_worker.js — patch-code generator for Service Worker injection (module)
frontend/
popup.html
popup.js
icons/

Permissions

  • storage — saves the selected tz/ua and settings locally; persists known SW URLs.
  • <all_urls> (host permission) — the spoof must apply on any site, before the site's own scripts run.
  • webRequest, webRequestBlocking, webRequestFilterResponse, webRequestFilterResponse.serviceWorkerScript — required for patching Service Workers via network response interception and for HTTP header modification.

Known limitations

  • Indexed access window[n] / window.frames[n] — numeric index access is exotic internal WindowProxy behavior, not an ordinary JS property. Object.defineProperty cannot intercept it. The first such access returns real data; subsequent accesses through .contentWindow are already patched.
  • "Dead" (detached) iframes — a reference to a removed iframe's window, obtained through a non-standard access path, may remain unpatched.
  • Live updates require reload — after changing tz/ua in the popup, open tabs must be reloaded. The spoof is applied at page load time, not retroactively (although an already open tab will pick up new values for future API calls if the popup was opened before the page reload).
  • OS clock untouched — the extension only changes what is reported through JS APIs and HTTP headers. The operating system clock and time zone are not modified.

Comparison (Before / After)

BeforeAfter
BrowserLeaks — HTTP HeadersBeforeAfter
BrowserLeaks — JS APIsBeforeAfter
CreepJS — FingerprintBeforeAfter
CreepJS — TimezoneBeforeAfter
CreepJS — WorkersBeforeAfter

License

MIT

About

Firefox extension that spoofs the timezone reported to websites (Intl.DateTimeFormat, Date APIs) to protect against fingerprinting. Works alongside privacy.resistFingerprinting, tested on YouTube and Spotify.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Timezone Guard

Firefox Add-on

Читать на русском

A Firefox extension that spoofs the time zone and (optionally) the User-Agent reported by the browser to websites — across all contexts: the main page, iframes (any nesting depth), Dedicated Worker, Shared Worker, and Service Worker.

Why

Websites infer approximate location and browser characteristics via Intl.DateTimeFormat, Date.prototype.getTimezoneOffset, navigator.userAgent, and similar APIs — and they do this not only in the main window but also inside workers, where ordinary time-zone spoofing extensions usually can't reach. This extension patches all these points consistently, so every context returns the same spoofed values.

Features

  • Time zone spoofingIntl.DateTimeFormat, Date.prototype.getTimezoneOffset, toString / toDateString / toTimeString / toLocaleString / toLocaleDateString / toLocaleTimeString
  • Optional User-Agent spoofingnavigator.userAgent, appVersion, platform, vendor, language, languages, maxTouchPoints, oscpu (Firefox), and related properties
  • HTTP header spoofing — optionally modifies outgoing Accept-Language and User-Agent headers to match the selected timezone and browser identifier
  • Consistent across all contexts — main window, iframe (including nested iframe-within-iframe), Dedicated Worker, Shared Worker, Service Worker
  • Real system clock untouchedDate.now() and the actual OS clock are not modified; only how the date is interpreted and displayed in the chosen time zone is changed
  • Cached formattersIntl.DateTimeFormat and Intl.NumberFormat formatters are cached to avoid timing detection (important against detectors like CreepJS)
  • CSP-safe — works on sites with strict Content-Security-Policy (Spotify Web Player and similar) via declarative injection through world: "MAIN"
  • No enumerable globals — all shared state lives inside the closure of a single file and is not enumerable from the outside
  • Temporal API support — patches Temporal.Now.timeZoneId and related methods when available
  • Number format spoofingIntl.NumberFormat and Number.prototype.toLocaleString use the spoofed locale

How it works

Main page and iframes — spoof/main.js

A single content script injected by the browser directly into the page's main world (world: "MAIN" in the manifest). This bypasses the page's CSP, since such injection is not treated as code added by the page itself.

  • Intl.DateTimeFormat, Intl.NumberFormat, and Date.prototype methods are patched once per window, with cached formatters.
  • navigator.userAgent and related properties are patched via Object.defineProperty on Navigator.prototype.
  • All patched functions are masked as [native code] via a custom toString wrapper.
  • iframe: instead of MutationObserver (which is always asynchronous), the getter contentWindow / contentDocument on HTMLIFrameElement.prototype is patched — the spoof is applied at the moment of access, not at DOM insertion. Recursive patching handles iframe-within-iframe.
  • Dedicated / Shared Worker: the Worker / SharedWorker constructors are wrapped so that our patch runs before the worker's original script, loaded via importScripts() — without blocking synchronous XHR on the main thread.
  • Service Worker: navigator.serviceWorker.register() is intercepted to learn the exact URL of the worker script, sent to the background via bridge.js.

bridge.js (isolated world)

Reads saved tz / ua / enabled / spoofHeaders from browser.storage.local and passes them to main.js through three mechanisms:

  1. DOM attributes on <html> — available immediately at document_start.
  2. sessionStorage / localStorage fallback (__tzGuardConfig) — survives page reloads and bfcache restores.
  3. CustomEvent (tz-guard-update-config) — for live updates when settings change in the popup.

background/background.js + background/build_worker.js

Service Workers cannot be patched via blob: URLs (forbidden by spec). Therefore network-response interception is used:

  • webRequest.onBeforeRequest + webRequest.filterResponseData (requires webRequestFilterResponse.serviceWorkerScript) splices the patch code into the beginning of the Service Worker script response.
  • The filter must be installed in onBeforeRequest, not later, to avoid Firefox's internal script byte-cache (Invalid request ID).
  • Known Service Worker URLs are persisted in storage.local (swUrls) so patches survive browser restarts.

HTTP Header Spoofing

When "Spoof HTTP Headers" is enabled in the popup, the extension modifies outgoing request headers via webRequest.onBeforeSendHeaders:

  • Accept-Language — derived from the selected timezone (e.g., Europe/Helsinkifi-FI,fi;q=0.9,en-US;q=0.8,en;q=0.7).
  • User-Agent — set to the custom UA if provided.

Popup settings

  • Extension Enabled — master toggle; disables all spoofing when turned off.
  • Spoof HTTP Headers — toggles modification of outgoing Accept-Language and User-Agent headers.
  • Timezone — select the timezone to report.
  • User-Agent — choose from presets or enter a custom string.

Installation (development / temporary extension)

  1. about:debugging#/runtime/this-firefoxLoad Temporary Add-on → select manifest.json.
  2. Open the popup, choose a time zone (and optionally a User-Agent), adjust toggles, Save & Apply.
  3. Fully close and reopen the tab (not just F5 — temporary extensions in Firefox get a new moz-extension:// UUID on every reload).

Project structure

manifest.json
bridge.js — isolated world: reads storage, passes config via DOM + storage events
spoof/
main.js — all patches (Intl/Date/UA/iframe/Worker/SharedWorker/Temporal), one file, one IIFE
background/
background.js — network response interception, header spoofing, SW URL tracking
build_worker.js — patch-code generator for Service Worker injection (module)
frontend/
popup.html
popup.js
icons/

Permissions

  • storage — saves the selected tz/ua and settings locally; persists known SW URLs.
  • <all_urls> (host permission) — the spoof must apply on any site, before the site's own scripts run.
  • webRequest, webRequestBlocking, webRequestFilterResponse, webRequestFilterResponse.serviceWorkerScript — required for patching Service Workers via network response interception and for HTTP header modification.

Known limitations

  • Indexed access window[n] / window.frames[n] — numeric index access is exotic internal WindowProxy behavior, not an ordinary JS property. Object.defineProperty cannot intercept it. The first such access returns real data; subsequent accesses through .contentWindow are already patched.
  • "Dead" (detached) iframes — a reference to a removed iframe's window, obtained through a non-standard access path, may remain unpatched.
  • Live updates require reload — after changing tz/ua in the popup, open tabs must be reloaded. The spoof is applied at page load time, not retroactively (although an already open tab will pick up new values for future API calls if the popup was opened before the page reload).
  • OS clock untouched — the extension only changes what is reported through JS APIs and HTTP headers. The operating system clock and time zone are not modified.

Comparison (Before / After)

BeforeAfter
BrowserLeaks — HTTP HeadersBeforeAfter
BrowserLeaks — JS APIsBeforeAfter
CreepJS — FingerprintBeforeAfter
CreepJS — TimezoneBeforeAfter
CreepJS — WorkersBeforeAfter

License

MIT

About

Firefox extension that spoofs the timezone reported to websites (Intl.DateTimeFormat, Date APIs) to protect against fingerprinting. Works alongside privacy.resistFingerprinting, tested on YouTube and Spotify.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Timezone Guard

Firefox Add-on

Читать на русском

A Firefox extension that spoofs the time zone and (optionally) the User-Agent reported by the browser to websites — across all contexts: the main page, iframes (any nesting depth), Dedicated Worker, Shared Worker, and Service Worker.

Why

Websites infer approximate location and browser characteristics via Intl.DateTimeFormat, Date.prototype.getTimezoneOffset, navigator.userAgent, and similar APIs — and they do this not only in the main window but also inside workers, where ordinary time-zone spoofing extensions usually can't reach. This extension patches all these points consistently, so every context returns the same spoofed values.

Features

  • Time zone spoofingIntl.DateTimeFormat, Date.prototype.getTimezoneOffset, toString / toDateString / toTimeString / toLocaleString / toLocaleDateString / toLocaleTimeString
  • Optional User-Agent spoofingnavigator.userAgent, appVersion, platform, vendor, language, languages, maxTouchPoints, oscpu (Firefox), and related properties
  • HTTP header spoofing — optionally modifies outgoing Accept-Language and User-Agent headers to match the selected timezone and browser identifier
  • Consistent across all contexts — main window, iframe (including nested iframe-within-iframe), Dedicated Worker, Shared Worker, Service Worker
  • Real system clock untouchedDate.now() and the actual OS clock are not modified; only how the date is interpreted and displayed in the chosen time zone is changed
  • Cached formattersIntl.DateTimeFormat and Intl.NumberFormat formatters are cached to avoid timing detection (important against detectors like CreepJS)
  • CSP-safe — works on sites with strict Content-Security-Policy (Spotify Web Player and similar) via declarative injection through world: "MAIN"
  • No enumerable globals — all shared state lives inside the closure of a single file and is not enumerable from the outside
  • Temporal API support — patches Temporal.Now.timeZoneId and related methods when available
  • Number format spoofingIntl.NumberFormat and Number.prototype.toLocaleString use the spoofed locale

How it works

Main page and iframes — spoof/main.js

A single content script injected by the browser directly into the page's main world (world: "MAIN" in the manifest). This bypasses the page's CSP, since such injection is not treated as code added by the page itself.

  • Intl.DateTimeFormat, Intl.NumberFormat, and Date.prototype methods are patched once per window, with cached formatters.
  • navigator.userAgent and related properties are patched via Object.defineProperty on Navigator.prototype.
  • All patched functions are masked as [native code] via a custom toString wrapper.
  • iframe: instead of MutationObserver (which is always asynchronous), the getter contentWindow / contentDocument on HTMLIFrameElement.prototype is patched — the spoof is applied at the moment of access, not at DOM insertion. Recursive patching handles iframe-within-iframe.
  • Dedicated / Shared Worker: the Worker / SharedWorker constructors are wrapped so that our patch runs before the worker's original script, loaded via importScripts() — without blocking synchronous XHR on the main thread.
  • Service Worker: navigator.serviceWorker.register() is intercepted to learn the exact URL of the worker script, sent to the background via bridge.js.

bridge.js (isolated world)

Reads saved tz / ua / enabled / spoofHeaders from browser.storage.local and passes them to main.js through three mechanisms:

  1. DOM attributes on <html> — available immediately at document_start.
  2. sessionStorage / localStorage fallback (__tzGuardConfig) — survives page reloads and bfcache restores.
  3. CustomEvent (tz-guard-update-config) — for live updates when settings change in the popup.

background/background.js + background/build_worker.js

Service Workers cannot be patched via blob: URLs (forbidden by spec). Therefore network-response interception is used:

  • webRequest.onBeforeRequest + webRequest.filterResponseData (requires webRequestFilterResponse.serviceWorkerScript) splices the patch code into the beginning of the Service Worker script response.
  • The filter must be installed in onBeforeRequest, not later, to avoid Firefox's internal script byte-cache (Invalid request ID).
  • Known Service Worker URLs are persisted in storage.local (swUrls) so patches survive browser restarts.

HTTP Header Spoofing

When "Spoof HTTP Headers" is enabled in the popup, the extension modifies outgoing request headers via webRequest.onBeforeSendHeaders:

  • Accept-Language — derived from the selected timezone (e.g., Europe/Helsinkifi-FI,fi;q=0.9,en-US;q=0.8,en;q=0.7).
  • User-Agent — set to the custom UA if provided.

Popup settings

  • Extension Enabled — master toggle; disables all spoofing when turned off.
  • Spoof HTTP Headers — toggles modification of outgoing Accept-Language and User-Agent headers.
  • Timezone — select the timezone to report.
  • User-Agent — choose from presets or enter a custom string.

Installation (development / temporary extension)

  1. about:debugging#/runtime/this-firefoxLoad Temporary Add-on → select manifest.json.
  2. Open the popup, choose a time zone (and optionally a User-Agent), adjust toggles, Save & Apply.
  3. Fully close and reopen the tab (not just F5 — temporary extensions in Firefox get a new moz-extension:// UUID on every reload).

Project structure

manifest.json
bridge.js — isolated world: reads storage, passes config via DOM + storage events
spoof/
main.js — all patches (Intl/Date/UA/iframe/Worker/SharedWorker/Temporal), one file, one IIFE
background/
background.js — network response interception, header spoofing, SW URL tracking
build_worker.js — patch-code generator for Service Worker injection (module)
frontend/
popup.html
popup.js
icons/

Permissions

  • storage — saves the selected tz/ua and settings locally; persists known SW URLs.
  • <all_urls> (host permission) — the spoof must apply on any site, before the site's own scripts run.
  • webRequest, webRequestBlocking, webRequestFilterResponse, webRequestFilterResponse.serviceWorkerScript — required for patching Service Workers via network response interception and for HTTP header modification.

Known limitations

  • Indexed access window[n] / window.frames[n] — numeric index access is exotic internal WindowProxy behavior, not an ordinary JS property. Object.defineProperty cannot intercept it. The first such access returns real data; subsequent accesses through .contentWindow are already patched.
  • "Dead" (detached) iframes — a reference to a removed iframe's window, obtained through a non-standard access path, may remain unpatched.
  • Live updates require reload — after changing tz/ua in the popup, open tabs must be reloaded. The spoof is applied at page load time, not retroactively (although an already open tab will pick up new values for future API calls if the popup was opened before the page reload).
  • OS clock untouched — the extension only changes what is reported through JS APIs and HTTP headers. The operating system clock and time zone are not modified.

Comparison (Before / After)

BeforeAfter
BrowserLeaks — HTTP HeadersBeforeAfter
BrowserLeaks — JS APIsBeforeAfter
CreepJS — FingerprintBeforeAfter
CreepJS — TimezoneBeforeAfter
CreepJS — WorkersBeforeAfter

License

MIT

About

Firefox extension that spoofs the timezone reported to websites (Intl.DateTimeFormat, Date APIs) to protect against fingerprinting. Works alongside privacy.resistFingerprinting, tested on YouTube and Spotify.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Timezone Guard

Firefox Add-on

Читать на русском

A Firefox extension that spoofs the time zone and (optionally) the User-Agent reported by the browser to websites — across all contexts: the main page, iframes (any nesting depth), Dedicated Worker, Shared Worker, and Service Worker.

Why

Websites infer approximate location and browser characteristics via Intl.DateTimeFormat, Date.prototype.getTimezoneOffset, navigator.userAgent, and similar APIs — and they do this not only in the main window but also inside workers, where ordinary time-zone spoofing extensions usually can't reach. This extension patches all these points consistently, so every context returns the same spoofed values.

Features

  • Time zone spoofingIntl.DateTimeFormat, Date.prototype.getTimezoneOffset, toString / toDateString / toTimeString / toLocaleString / toLocaleDateString / toLocaleTimeString
  • Optional User-Agent spoofingnavigator.userAgent, appVersion, platform, vendor, language, languages, maxTouchPoints, oscpu (Firefox), and related properties
  • HTTP header spoofing — optionally modifies outgoing Accept-Language and User-Agent headers to match the selected timezone and browser identifier
  • Consistent across all contexts — main window, iframe (including nested iframe-within-iframe), Dedicated Worker, Shared Worker, Service Worker
  • Real system clock untouchedDate.now() and the actual OS clock are not modified; only how the date is interpreted and displayed in the chosen time zone is changed
  • Cached formattersIntl.DateTimeFormat and Intl.NumberFormat formatters are cached to avoid timing detection (important against detectors like CreepJS)
  • CSP-safe — works on sites with strict Content-Security-Policy (Spotify Web Player and similar) via declarative injection through world: "MAIN"
  • No enumerable globals — all shared state lives inside the closure of a single file and is not enumerable from the outside
  • Temporal API support — patches Temporal.Now.timeZoneId and related methods when available
  • Number format spoofingIntl.NumberFormat and Number.prototype.toLocaleString use the spoofed locale

How it works

Main page and iframes — spoof/main.js

A single content script injected by the browser directly into the page's main world (world: "MAIN" in the manifest). This bypasses the page's CSP, since such injection is not treated as code added by the page itself.

  • Intl.DateTimeFormat, Intl.NumberFormat, and Date.prototype methods are patched once per window, with cached formatters.
  • navigator.userAgent and related properties are patched via Object.defineProperty on Navigator.prototype.
  • All patched functions are masked as [native code] via a custom toString wrapper.
  • iframe: instead of MutationObserver (which is always asynchronous), the getter contentWindow / contentDocument on HTMLIFrameElement.prototype is patched — the spoof is applied at the moment of access, not at DOM insertion. Recursive patching handles iframe-within-iframe.
  • Dedicated / Shared Worker: the Worker / SharedWorker constructors are wrapped so that our patch runs before the worker's original script, loaded via importScripts() — without blocking synchronous XHR on the main thread.
  • Service Worker: navigator.serviceWorker.register() is intercepted to learn the exact URL of the worker script, sent to the background via bridge.js.

bridge.js (isolated world)

Reads saved tz / ua / enabled / spoofHeaders from browser.storage.local and passes them to main.js through three mechanisms:

  1. DOM attributes on <html> — available immediately at document_start.
  2. sessionStorage / localStorage fallback (__tzGuardConfig) — survives page reloads and bfcache restores.
  3. CustomEvent (tz-guard-update-config) — for live updates when settings change in the popup.

background/background.js + background/build_worker.js

Service Workers cannot be patched via blob: URLs (forbidden by spec). Therefore network-response interception is used:

  • webRequest.onBeforeRequest + webRequest.filterResponseData (requires webRequestFilterResponse.serviceWorkerScript) splices the patch code into the beginning of the Service Worker script response.
  • The filter must be installed in onBeforeRequest, not later, to avoid Firefox's internal script byte-cache (Invalid request ID).
  • Known Service Worker URLs are persisted in storage.local (swUrls) so patches survive browser restarts.

HTTP Header Spoofing

When "Spoof HTTP Headers" is enabled in the popup, the extension modifies outgoing request headers via webRequest.onBeforeSendHeaders:

  • Accept-Language — derived from the selected timezone (e.g., Europe/Helsinkifi-FI,fi;q=0.9,en-US;q=0.8,en;q=0.7).
  • User-Agent — set to the custom UA if provided.

Popup settings

  • Extension Enabled — master toggle; disables all spoofing when turned off.
  • Spoof HTTP Headers — toggles modification of outgoing Accept-Language and User-Agent headers.
  • Timezone — select the timezone to report.
  • User-Agent — choose from presets or enter a custom string.

Installation (development / temporary extension)

  1. about:debugging#/runtime/this-firefoxLoad Temporary Add-on → select manifest.json.
  2. Open the popup, choose a time zone (and optionally a User-Agent), adjust toggles, Save & Apply.
  3. Fully close and reopen the tab (not just F5 — temporary extensions in Firefox get a new moz-extension:// UUID on every reload).

Project structure

manifest.json
bridge.js — isolated world: reads storage, passes config via DOM + storage events
spoof/
main.js — all patches (Intl/Date/UA/iframe/Worker/SharedWorker/Temporal), one file, one IIFE
background/
background.js — network response interception, header spoofing, SW URL tracking
build_worker.js — patch-code generator for Service Worker injection (module)
frontend/
popup.html
popup.js
icons/

Permissions

  • storage — saves the selected tz/ua and settings locally; persists known SW URLs.
  • <all_urls> (host permission) — the spoof must apply on any site, before the site's own scripts run.
  • webRequest, webRequestBlocking, webRequestFilterResponse, webRequestFilterResponse.serviceWorkerScript — required for patching Service Workers via network response interception and for HTTP header modification.

Known limitations

  • Indexed access window[n] / window.frames[n] — numeric index access is exotic internal WindowProxy behavior, not an ordinary JS property. Object.defineProperty cannot intercept it. The first such access returns real data; subsequent accesses through .contentWindow are already patched.
  • "Dead" (detached) iframes — a reference to a removed iframe's window, obtained through a non-standard access path, may remain unpatched.
  • Live updates require reload — after changing tz/ua in the popup, open tabs must be reloaded. The spoof is applied at page load time, not retroactively (although an already open tab will pick up new values for future API calls if the popup was opened before the page reload).
  • OS clock untouched — the extension only changes what is reported through JS APIs and HTTP headers. The operating system clock and time zone are not modified.

Comparison (Before / After)

BeforeAfter
BrowserLeaks — HTTP HeadersBeforeAfter
BrowserLeaks — JS APIsBeforeAfter
CreepJS — FingerprintBeforeAfter
CreepJS — TimezoneBeforeAfter
CreepJS — WorkersBeforeAfter

License

MIT

About

Firefox extension that spoofs the timezone reported to websites (Intl.DateTimeFormat, Date APIs) to protect against fingerprinting. Works alongside privacy.resistFingerprinting, tested on YouTube and Spotify.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Timezone Guard

Firefox Add-on

Читать на русском

A Firefox extension that spoofs the time zone and (optionally) the User-Agent reported by the browser to websites — across all contexts: the main page, iframes (any nesting depth), Dedicated Worker, Shared Worker, and Service Worker.

Why

Websites infer approximate location and browser characteristics via Intl.DateTimeFormat, Date.prototype.getTimezoneOffset, navigator.userAgent, and similar APIs — and they do this not only in the main window but also inside workers, where ordinary time-zone spoofing extensions usually can't reach. This extension patches all these points consistently, so every context returns the same spoofed values.

Features

  • Time zone spoofingIntl.DateTimeFormat, Date.prototype.getTimezoneOffset, toString / toDateString / toTimeString / toLocaleString / toLocaleDateString / toLocaleTimeString
  • Optional User-Agent spoofingnavigator.userAgent, appVersion, platform, vendor, language, languages, maxTouchPoints, oscpu (Firefox), and related properties
  • HTTP header spoofing — optionally modifies outgoing Accept-Language and User-Agent headers to match the selected timezone and browser identifier
  • Consistent across all contexts — main window, iframe (including nested iframe-within-iframe), Dedicated Worker, Shared Worker, Service Worker
  • Real system clock untouchedDate.now() and the actual OS clock are not modified; only how the date is interpreted and displayed in the chosen time zone is changed
  • Cached formattersIntl.DateTimeFormat and Intl.NumberFormat formatters are cached to avoid timing detection (important against detectors like CreepJS)
  • CSP-safe — works on sites with strict Content-Security-Policy (Spotify Web Player and similar) via declarative injection through world: "MAIN"
  • No enumerable globals — all shared state lives inside the closure of a single file and is not enumerable from the outside
  • Temporal API support — patches Temporal.Now.timeZoneId and related methods when available
  • Number format spoofingIntl.NumberFormat and Number.prototype.toLocaleString use the spoofed locale

How it works

Main page and iframes — spoof/main.js

A single content script injected by the browser directly into the page's main world (world: "MAIN" in the manifest). This bypasses the page's CSP, since such injection is not treated as code added by the page itself.

  • Intl.DateTimeFormat, Intl.NumberFormat, and Date.prototype methods are patched once per window, with cached formatters.
  • navigator.userAgent and related properties are patched via Object.defineProperty on Navigator.prototype.
  • All patched functions are masked as [native code] via a custom toString wrapper.
  • iframe: instead of MutationObserver (which is always asynchronous), the getter contentWindow / contentDocument on HTMLIFrameElement.prototype is patched — the spoof is applied at the moment of access, not at DOM insertion. Recursive patching handles iframe-within-iframe.
  • Dedicated / Shared Worker: the Worker / SharedWorker constructors are wrapped so that our patch runs before the worker's original script, loaded via importScripts() — without blocking synchronous XHR on the main thread.
  • Service Worker: navigator.serviceWorker.register() is intercepted to learn the exact URL of the worker script, sent to the background via bridge.js.

bridge.js (isolated world)

Reads saved tz / ua / enabled / spoofHeaders from browser.storage.local and passes them to main.js through three mechanisms:

  1. DOM attributes on <html> — available immediately at document_start.
  2. sessionStorage / localStorage fallback (__tzGuardConfig) — survives page reloads and bfcache restores.
  3. CustomEvent (tz-guard-update-config) — for live updates when settings change in the popup.

background/background.js + background/build_worker.js

Service Workers cannot be patched via blob: URLs (forbidden by spec). Therefore network-response interception is used:

  • webRequest.onBeforeRequest + webRequest.filterResponseData (requires webRequestFilterResponse.serviceWorkerScript) splices the patch code into the beginning of the Service Worker script response.
  • The filter must be installed in onBeforeRequest, not later, to avoid Firefox's internal script byte-cache (Invalid request ID).
  • Known Service Worker URLs are persisted in storage.local (swUrls) so patches survive browser restarts.

HTTP Header Spoofing

When "Spoof HTTP Headers" is enabled in the popup, the extension modifies outgoing request headers via webRequest.onBeforeSendHeaders:

  • Accept-Language — derived from the selected timezone (e.g., Europe/Helsinkifi-FI,fi;q=0.9,en-US;q=0.8,en;q=0.7).
  • User-Agent — set to the custom UA if provided.

Popup settings

  • Extension Enabled — master toggle; disables all spoofing when turned off.
  • Spoof HTTP Headers — toggles modification of outgoing Accept-Language and User-Agent headers.
  • Timezone — select the timezone to report.
  • User-Agent — choose from presets or enter a custom string.

Installation (development / temporary extension)

  1. about:debugging#/runtime/this-firefoxLoad Temporary Add-on → select manifest.json.
  2. Open the popup, choose a time zone (and optionally a User-Agent), adjust toggles, Save & Apply.
  3. Fully close and reopen the tab (not just F5 — temporary extensions in Firefox get a new moz-extension:// UUID on every reload).

Project structure

manifest.json
bridge.js — isolated world: reads storage, passes config via DOM + storage events
spoof/
main.js — all patches (Intl/Date/UA/iframe/Worker/SharedWorker/Temporal), one file, one IIFE
background/
background.js — network response interception, header spoofing, SW URL tracking
build_worker.js — patch-code generator for Service Worker injection (module)
frontend/
popup.html
popup.js
icons/

Permissions

  • storage — saves the selected tz/ua and settings locally; persists known SW URLs.
  • <all_urls> (host permission) — the spoof must apply on any site, before the site's own scripts run.
  • webRequest, webRequestBlocking, webRequestFilterResponse, webRequestFilterResponse.serviceWorkerScript — required for patching Service Workers via network response interception and for HTTP header modification.

Known limitations

  • Indexed access window[n] / window.frames[n] — numeric index access is exotic internal WindowProxy behavior, not an ordinary JS property. Object.defineProperty cannot intercept it. The first such access returns real data; subsequent accesses through .contentWindow are already patched.
  • "Dead" (detached) iframes — a reference to a removed iframe's window, obtained through a non-standard access path, may remain unpatched.
  • Live updates require reload — after changing tz/ua in the popup, open tabs must be reloaded. The spoof is applied at page load time, not retroactively (although an already open tab will pick up new values for future API calls if the popup was opened before the page reload).
  • OS clock untouched — the extension only changes what is reported through JS APIs and HTTP headers. The operating system clock and time zone are not modified.

Comparison (Before / After)

BeforeAfter
BrowserLeaks — HTTP HeadersBeforeAfter
BrowserLeaks — JS APIsBeforeAfter
CreepJS — FingerprintBeforeAfter
CreepJS — TimezoneBeforeAfter
CreepJS — WorkersBeforeAfter

License

MIT

About

Firefox extension that spoofs the timezone reported to websites (Intl.DateTimeFormat, Date APIs) to protect against fingerprinting. Works alongside privacy.resistFingerprinting, tested on YouTube and Spotify.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Timezone Guard

Firefox Add-on

Читать на русском

A Firefox extension that spoofs the time zone and (optionally) the User-Agent reported by the browser to websites — across all contexts: the main page, iframes (any nesting depth), Dedicated Worker, Shared Worker, and Service Worker.

Why

Websites infer approximate location and browser characteristics via Intl.DateTimeFormat, Date.prototype.getTimezoneOffset, navigator.userAgent, and similar APIs — and they do this not only in the main window but also inside workers, where ordinary time-zone spoofing extensions usually can't reach. This extension patches all these points consistently, so every context returns the same spoofed values.

Features

  • Time zone spoofingIntl.DateTimeFormat, Date.prototype.getTimezoneOffset, toString / toDateString / toTimeString / toLocaleString / toLocaleDateString / toLocaleTimeString
  • Optional User-Agent spoofingnavigator.userAgent, appVersion, platform, vendor, language, languages, maxTouchPoints, oscpu (Firefox), and related properties
  • HTTP header spoofing — optionally modifies outgoing Accept-Language and User-Agent headers to match the selected timezone and browser identifier
  • Consistent across all contexts — main window, iframe (including nested iframe-within-iframe), Dedicated Worker, Shared Worker, Service Worker
  • Real system clock untouchedDate.now() and the actual OS clock are not modified; only how the date is interpreted and displayed in the chosen time zone is changed
  • Cached formattersIntl.DateTimeFormat and Intl.NumberFormat formatters are cached to avoid timing detection (important against detectors like CreepJS)
  • CSP-safe — works on sites with strict Content-Security-Policy (Spotify Web Player and similar) via declarative injection through world: "MAIN"
  • No enumerable globals — all shared state lives inside the closure of a single file and is not enumerable from the outside
  • Temporal API support — patches Temporal.Now.timeZoneId and related methods when available
  • Number format spoofingIntl.NumberFormat and Number.prototype.toLocaleString use the spoofed locale

How it works

Main page and iframes — spoof/main.js

A single content script injected by the browser directly into the page's main world (world: "MAIN" in the manifest). This bypasses the page's CSP, since such injection is not treated as code added by the page itself.

  • Intl.DateTimeFormat, Intl.NumberFormat, and Date.prototype methods are patched once per window, with cached formatters.
  • navigator.userAgent and related properties are patched via Object.defineProperty on Navigator.prototype.
  • All patched functions are masked as [native code] via a custom toString wrapper.
  • iframe: instead of MutationObserver (which is always asynchronous), the getter contentWindow / contentDocument on HTMLIFrameElement.prototype is patched — the spoof is applied at the moment of access, not at DOM insertion. Recursive patching handles iframe-within-iframe.
  • Dedicated / Shared Worker: the Worker / SharedWorker constructors are wrapped so that our patch runs before the worker's original script, loaded via importScripts() — without blocking synchronous XHR on the main thread.
  • Service Worker: navigator.serviceWorker.register() is intercepted to learn the exact URL of the worker script, sent to the background via bridge.js.

bridge.js (isolated world)

Reads saved tz / ua / enabled / spoofHeaders from browser.storage.local and passes them to main.js through three mechanisms:

  1. DOM attributes on <html> — available immediately at document_start.
  2. sessionStorage / localStorage fallback (__tzGuardConfig) — survives page reloads and bfcache restores.
  3. CustomEvent (tz-guard-update-config) — for live updates when settings change in the popup.

background/background.js + background/build_worker.js

Service Workers cannot be patched via blob: URLs (forbidden by spec). Therefore network-response interception is used:

  • webRequest.onBeforeRequest + webRequest.filterResponseData (requires webRequestFilterResponse.serviceWorkerScript) splices the patch code into the beginning of the Service Worker script response.
  • The filter must be installed in onBeforeRequest, not later, to avoid Firefox's internal script byte-cache (Invalid request ID).
  • Known Service Worker URLs are persisted in storage.local (swUrls) so patches survive browser restarts.

HTTP Header Spoofing

When "Spoof HTTP Headers" is enabled in the popup, the extension modifies outgoing request headers via webRequest.onBeforeSendHeaders:

  • Accept-Language — derived from the selected timezone (e.g., Europe/Helsinkifi-FI,fi;q=0.9,en-US;q=0.8,en;q=0.7).
  • User-Agent — set to the custom UA if provided.

Popup settings

  • Extension Enabled — master toggle; disables all spoofing when turned off.
  • Spoof HTTP Headers — toggles modification of outgoing Accept-Language and User-Agent headers.
  • Timezone — select the timezone to report.
  • User-Agent — choose from presets or enter a custom string.

Installation (development / temporary extension)

  1. about:debugging#/runtime/this-firefoxLoad Temporary Add-on → select manifest.json.
  2. Open the popup, choose a time zone (and optionally a User-Agent), adjust toggles, Save & Apply.
  3. Fully close and reopen the tab (not just F5 — temporary extensions in Firefox get a new moz-extension:// UUID on every reload).

Project structure

manifest.json
bridge.js — isolated world: reads storage, passes config via DOM + storage events
spoof/
main.js — all patches (Intl/Date/UA/iframe/Worker/SharedWorker/Temporal), one file, one IIFE
background/
background.js — network response interception, header spoofing, SW URL tracking
build_worker.js — patch-code generator for Service Worker injection (module)
frontend/
popup.html
popup.js
icons/

Permissions

  • storage — saves the selected tz/ua and settings locally; persists known SW URLs.
  • <all_urls> (host permission) — the spoof must apply on any site, before the site's own scripts run.
  • webRequest, webRequestBlocking, webRequestFilterResponse, webRequestFilterResponse.serviceWorkerScript — required for patching Service Workers via network response interception and for HTTP header modification.

Known limitations

  • Indexed access window[n] / window.frames[n] — numeric index access is exotic internal WindowProxy behavior, not an ordinary JS property. Object.defineProperty cannot intercept it. The first such access returns real data; subsequent accesses through .contentWindow are already patched.
  • "Dead" (detached) iframes — a reference to a removed iframe's window, obtained through a non-standard access path, may remain unpatched.
  • Live updates require reload — after changing tz/ua in the popup, open tabs must be reloaded. The spoof is applied at page load time, not retroactively (although an already open tab will pick up new values for future API calls if the popup was opened before the page reload).
  • OS clock untouched — the extension only changes what is reported through JS APIs and HTTP headers. The operating system clock and time zone are not modified.

Comparison (Before / After)

BeforeAfter
BrowserLeaks — HTTP HeadersBeforeAfter
BrowserLeaks — JS APIsBeforeAfter
CreepJS — FingerprintBeforeAfter
CreepJS — TimezoneBeforeAfter
CreepJS — WorkersBeforeAfter

License

MIT

About

Firefox extension that spoofs the timezone reported to websites (Intl.DateTimeFormat, Date APIs) to protect against fingerprinting. Works alongside privacy.resistFingerprinting, tested on YouTube and Spotify.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages