Greasy Fork is available in English.
Enhance privacy when using search engines: strip tracking params, remove redirect wrappers, set no-referrer, force HTTPS on links, and add noreferrer/noopener. Works on Google, Bing, DuckDuckGo, Yahoo and common search pages. Toggle features from the Tampermonkey menu.
// ==UserScript==
// @name Search Enhanced Protection (Tampermonkey)
// @namespace http://tampermonkey.net/
// @version 1.3
// @description Enhance privacy when using search engines: strip tracking params, remove redirect wrappers, set no-referrer, force HTTPS on links, and add noreferrer/noopener. Works on Google, Bing, DuckDuckGo, Yahoo and common search pages. Toggle features from the Tampermonkey menu.
// @author Skibidi555
// @match *://*.google.*/*
// @match *://*.bing.com/*
// @match *://*.duckduckgo.com/*
// @match *://search.yahoo.com/*
// @match *://*.brave.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @run-at document-idle
// @license You can only change the name
// ==/UserScript==
(function () {
'use strict';
const DEBUG = false;
// ---- Configuration (persistent via GM_setValue with defaults) ----
const defaults = {
stripTrackingParams: true,
fixRedirectLinks: true,
setNoReferrerMeta: true,
forceHTTPS: true,
addNoreferrerRel: true
};
// Load/save helpers (use GM_getValue default arg to avoid undefined)
function getOpt(key) {
try {
return GM_getValue(key, defaults[key]);
} catch (e) {
// fallback - Tampermonkey should support GM_getValue with default but be defensive
return (typeof window['__sep_defaults__'] !== 'undefined' && key in window["__sep_defaults__"])
? window['__sep_defaults__'][key] : defaults[key];
}
}
function setOpt(key, val) {
try {
GM_setValue(key, !!val);
if (DEBUG) console.info('SEP: setOpt', key, !!val);
} catch (e) {
if (DEBUG) console.warn('SEP: GM_setValue failed', e);
}
}
// Register menu commands to toggle features (re-run without reload)
function registerMenu() {
try {
GM_registerMenuCommand('Toggle stripTrackingParams (' + (getOpt('stripTrackingParams') ? 'ON' : 'OFF') + ')', () => {
setOpt('stripTrackingParams', !getOpt('stripTrackingParams'));
runProtection(true);
});
GM_registerMenuCommand('Toggle fixRedirectLinks (' + (getOpt('fixRedirectLinks') ? 'ON' : 'OFF') + ')', () => {
setOpt('fixRedirectLinks', !getOpt('fixRedirectLinks'));
runProtection(true);
});
GM_registerMenuCommand('Toggle setNoReferrerMeta (' + (getOpt('setNoReferrerMeta') ? 'ON' : 'OFF') + ')', () => {
setOpt('setNoReferrerMeta', !getOpt('setNoReferrerMeta'));
runProtection(true);
});
GM_registerMenuCommand('Toggle forceHTTPS (' + (getOpt('forceHTTPS') ? 'ON' : 'OFF') + ')', () => {
setOpt('forceHTTPS', !getOpt('forceHTTPS'));
runProtection(true);
});
GM_registerMenuCommand('Toggle addNoreferrerRel (' + (getOpt('addNoreferrerRel') ? 'ON' : 'OFF') + ')', () => {
setOpt('addNoreferrerRel', !getOpt('addNoreferrerRel'));
runProtection(true);
});
} catch (e) {
if (DEBUG) console.warn('SEP: registerMenu failed', e);
}
}
registerMenu();
// ---- Utilities ----
const TRACKING_PARAMS = [
/^utm_/i,
/^gclid$/i,
/^fbclid$/i,
/^mc_cid$/i,
/^mc_eid$/i,
/^igshid$/i,
/^ref$/i,
/^ref_src$/i,
/^msclkid$/i,
/^trk_?/i,
/^icid$/i,
/^_hsenc$/i,
/^_hsmi$/i
];
function isTrackingParam(key) {
return TRACKING_PARAMS.some(rx => rx.test(key));
}
function stripTrackingParamsFromSearchParams(sp) {
const toDelete = [];
for (const key of sp.keys()) {
if (isTrackingParam(key)) toDelete.push(key);
}
for (const k of toDelete) sp.delete(k);
}
function cleanUrlString(urlStr) {
try {
// resolve relative URLs
const u = new URL(urlStr, document.baseURI);
if (getOpt('stripTrackingParams')) {
stripTrackingParamsFromSearchParams(u.searchParams);
if (u.hash && u.hash.includes('=')) {
try {
const h = new URLSearchParams(u.hash.replace(/^#/, ''));
let changed = false;
for (const key of Array.from(h.keys())) {
if (isTrackingParam(key)) { h.delete(key); changed = true; }
}
if (changed) u.hash = h.toString() ? '#' + h.toString() : '';
} catch (e) { /* ignore malformed hash */ }
}
}
if (getOpt('forceHTTPS') && u.protocol === 'http:') {
if (/\.(com|org|net|io|gov|edu|co|uk|de|fr|es|nl|ca|au|info|me)$/i.test(u.hostname)) {
u.protocol = 'https:';
}
}
return u.toString();
} catch (e) {
if (DEBUG) console.warn('SEP: cleanUrlString parse fail for', urlStr, e);
return urlStr;
}
}
function unwrapGoogleRedirect(href) {
try {
const u = new URL(href, document.baseURI);
// common redirect endpoints
if ((/\/url$/i).test(u.pathname) && u.searchParams.has('q')) {
return u.searchParams.get('q');
}
for (const p of ['url', 'u', 'q']) {
if (u.searchParams.has(p)) return u.searchParams.get(p);
}
} catch (e) {
if (DEBUG) console.debug('SEP: unwrap failed', href, e);
}
return href;
}
function addRel(existing, addition) {
const set = new Set((existing || '').split(/\s+/).filter(Boolean));
addition.split(/\s+/).forEach(t => set.add(t));
return Array.from(set).join(' ');
}
function cleanAnchor(a) {
try {
if (!(a instanceof HTMLAnchorElement)) return;
const orig = a.getAttribute('href');
if (!orig) return;
if (/^\s*(javascript:|#|mailto:|tel:)/i.test(orig)) {
if (getOpt('addNoreferrerRel')) a.rel = addRel(a.rel, 'noreferrer noopener');
return;
}
let cleaned = orig;
if (getOpt('fixRedirectLinks')) {
cleaned = unwrapGoogleRedirect(cleaned);
}
cleaned = cleanUrlString(cleaned);
if (cleaned !== orig) {
a.setAttribute('data-sep-cleaned-href', '1');
a.setAttribute('href', cleaned);
}
if (getOpt('addNoreferrerRel')) {
a.rel = addRel(a.rel, 'noreferrer noopener');
}
} catch (e) {
if (DEBUG) console.warn('SEP: cleanAnchor error', e);
}
}
function processAnchors(root = document) {
try {
const anchors = (root && root.querySelectorAll) ? root.querySelectorAll('a[href]') : [];
for (const a of anchors) {
if (a.getAttribute('data-sep-cleaned-href') === '1') continue;
cleanAnchor(a);
}
} catch (e) {
if (DEBUG) console.warn('SEP: processAnchors error', e);
}
}
// prevent handling repeated clicks on same link (e.g., search engine handlers)
let recentlyIntercepted = new WeakSet();
function installClickInterceptor() {
document.addEventListener('click', function (ev) {
try {
// ignore if user used modifier keys to open context or special clicks
if (ev.defaultPrevented) return;
const target = ev.target;
if (!target || typeof target.closest !== 'function') return;
const a = target.closest('a[href]');
if (!a) return;
if (recentlyIntercepted.has(a)) return;
const href = a.getAttribute('href');
if (!href || /^\s*(javascript:|#)/i.test(href)) return;
if (getOpt('fixRedirectLinks') || getOpt('stripTrackingParams')) {
const cleaned = cleanUrlString(unwrapGoogleRedirect(href));
if (cleaned && cleaned !== href) {
// middle click / ctrl/meta should open in new tab
const openInNew = (ev.button === 1) || ev.ctrlKey || ev.metaKey || a.target === '_blank';
ev.preventDefault();
ev.stopPropagation();
recentlyIntercepted.add(a);
if (openInNew) {
// open with no opener/referrer where possible
try { window.open(cleaned, '_blank', 'noopener,noreferrer'); }
catch (e) { window.open(cleaned, '_blank'); }
} else {
location.href = cleaned;
}
// remove mark after short time to allow normal subsequent clicks
setTimeout(() => { recentlyIntercepted.delete(a); }, 1000);
}
}
} catch (e) {
if (DEBUG) console.warn('SEP: click interceptor error', e);
}
}, true);
}
function setNoReferrerMeta() {
if (!getOpt('setNoReferrerMeta')) return;
try {
let meta = document.querySelector('meta[name="referrer"]');
if (!meta) {
meta = document.createElement('meta');
meta.name = 'referrer';
if (document.head) {
document.head.prepend(meta);
} else if (document.documentElement) {
// fallback - append to documentElement if head absent (rare)
document.documentElement.insertBefore(meta, document.documentElement.firstChild);
}
}
meta.setAttribute('content', 'no-referrer');
} catch (e) {
if (DEBUG) console.warn('SEP: setNoReferrerMeta failed', e);
}
}
let observer = null;
function observeAndClean() {
try {
if (observer) observer.disconnect();
observer = new MutationObserver((mutations) => {
for (const m of mutations) {
if (m.addedNodes && m.addedNodes.length) {
for (const n of m.addedNodes) {
if (n.nodeType === 1) {
if (n.querySelector && n.querySelector('a[href]')) {
processAnchors(n);
} else if (n.matches && n.matches('a[href]')) {
cleanAnchor(n);
}
}
}
}
}
});
observer.observe(document.documentElement || document.body, { childList: true, subtree: true });
processAnchors(document);
} catch (e) {
if (DEBUG) console.warn('SEP: observeAndClean failed', e);
}
}
function runProtection(forceReprocess = false) {
try {
if (getOpt('setNoReferrerMeta')) setNoReferrerMeta();
if (getOpt('fixRedirectLinks') || getOpt('stripTrackingParams') || getOpt('addNoreferrerRel') || getOpt('forceHTTPS')) {
if (forceReprocess) processAnchors(document);
installClickInterceptor();
observeAndClean();
}
if (DEBUG) console.info('Search Enhanced Protection: active (features: ' +
(getOpt('stripTrackingParams') ? 'stripTrackingParams ' : '') +
(getOpt('fixRedirectLinks') ? 'fixRedirectLinks ' : '') +
(getOpt('setNoReferrerMeta') ? 'setNoReferrerMeta ' : '') +
(getOpt('forceHTTPS') ? 'forceHTTPS ' : '') +
(getOpt('addNoreferrerRel') ? 'addNoreferrerRel ' : '') + ')');
} catch (e) {
if (DEBUG) console.error('SEP: runProtection failed', e);
}
}
runProtection();
// SPA navigation hooks
(function hijackHistory() {
try {
const push = history.pushState;
const replace = history.replaceState;
history.pushState = function () {
push.apply(this, arguments);
setTimeout(() => runProtection(true), 200);
};
history.replaceState = function () {
replace.apply(this, arguments);
setTimeout(() => runProtection(true), 200);
};
window.addEventListener('popstate', () => setTimeout(() => runProtection(true), 200));
} catch (e) {
if (DEBUG) console.warn('SEP: hijackHistory failed', e);
}
})();
})();