Block All Client-Side Redirects (Quiet)

Silently blocks most client-side navigations (links, forms, JS redirects, meta refresh) in Safari/iOS or any browser with a userscript manager.

Από την 15/08/2025. Δείτε την τελευταία έκδοση.

// ==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.');
})();
长期地址
遇到问题?请前往 GitHub 提 Issues。