// ==UserScript==
// @name Block All Client-Side Redirects (Quiet)
// @namespace https://github.com/osuobiem
// @version 1.1
// @description Silently blocks most client-side navigations (links, forms, JS redirects, meta refresh) in Safari/iOS or any browser with a userscript manager.
// @author Gabriel Osuobiem
// @match *://*/*
// @run-at document-start
// @grant none
// ==/UserScript==
(function () {
'use strict';
function logBlock(type, detail) {
console.warn(`[NoRedirect] Blocked ${type}:`, detail);
}
function blockMetaRefresh() {
document.querySelectorAll('meta[http-equiv="refresh"]').forEach(m => {
logBlock('meta refresh', m.content);
m.remove();
});
}
// Block clicks on links
document.addEventListener('click', e => {
const a = e.target.closest && e.target.closest('a[href]');
if (!a) return;
e.preventDefault();
logBlock('link click', a.href);
}, true);
// Block form submissions
document.addEventListener('submit', e => {
e.preventDefault();
logBlock('form submit', e.target && e.target.action || location.href);
}, true);
// Stop meta refresh tags (initial + dynamically added)
blockMetaRefresh();
new MutationObserver(blockMetaRefresh).observe(document.documentElement, { childList: true, subtree: true });
// Override location properties
try {
const L = location;
Object.defineProperty(window, 'location', {
configurable: false,
enumerable: true,
get: () => L,
set: (url) => {
logBlock('location set', url);
}
});
L.assign = new Proxy(L.assign, { apply(_t, _th, args) { logBlock('location.assign', args[0]); } });
L.replace = new Proxy(L.replace, { apply(_t, _th, args) { logBlock('location.replace', args[0]); } });
L.reload = new Proxy(L.reload, { apply() { logBlock('location.reload', '(reload)'); } });
} catch (_) { }
// Override window.open
try {
window.open = new Proxy(window.open, { apply(_t, _th, args) { logBlock('window.open', args[0]); return null; } });
} catch (_) { }
// Override History API
try {
history.pushState = new Proxy(history.pushState, { apply() { logBlock('history.pushState', arguments); } });
history.replaceState = new Proxy(history.replaceState, { apply() { logBlock('history.replaceState', arguments); } });
} catch (_) { }
// Block JS timers that navigate
const navPattern = /location\s*=\s*|location\.(assign|replace)\s*\(/i;
const origSetTimeout = window.setTimeout;
const origSetInterval = window.setInterval;
window.setTimeout = new Proxy(origSetTimeout, {
apply(target, thisArg, args) {
if (typeof args[0] === 'string' && navPattern.test(args[0])) {
logBlock('setTimeout redirect', args[0]);
return null;
}
return Reflect.apply(target, thisArg, args);
}
});
window.setInterval = new Proxy(origSetInterval, {
apply(target, thisArg, args) {
if (typeof args[0] === 'string' && navPattern.test(args[0])) {
logBlock('setInterval redirect', args[0]);
return null;
}
return Reflect.apply(target, thisArg, args);
}
});
// Stronger CSP
try {
const meta = document.createElement('meta');
meta.httpEquiv = 'Content-Security-Policy';
meta.content = "navigate-to 'none'; form-action 'none'";
document.documentElement.prepend(meta);
} catch (_) { }
console.log('[NoRedirect] Quiet redirect blocking active.');
})();