Hide Referrer

Hide HTTP Referrer on all websites to prevent destination sites from tracking your origin page

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         Hide Referrer
// @namespace    https://github.com/planetoid/userscripts
// @version      1.0.0
// @description  Hide HTTP Referrer on all websites to prevent destination sites from tracking your origin page
// @author       Planetoid
// @match        *://*/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // ============================================================
    // Configuration
    // ============================================================

    const CONFIG = {
        // Whether to inject <meta> referrer tag (primary protection)
        injectMetaTag: true,

        // Whether to add rel="noreferrer" to all <a> tags
        patchLinks: true,

        // Whether to intercept window.open so it doesn't send referrer
        patchWindowOpen: true,

        // Whether to observe DOM changes and patch dynamically added links (for SPAs)
        observeDom: true,

        // Excluded domains (referrer hiding will be skipped on these sites)
        // e.g. ['example.com', 'mysite.org']
        excludedDomains: [],

        // Whether to show debug messages in the console
        debug: false,
    };

    // ============================================================
    // Utilities
    // ============================================================

    function log(...args) {
        if (CONFIG.debug) {
            console.log('[Hide Referrer]', ...args);
        }
    }

    function isExcluded() {
        return CONFIG.excludedDomains.some(
            (domain) =>
                location.hostname === domain ||
                location.hostname.endsWith('.' + domain)
        );
    }

    function isExternalLink(anchor) {
        try {
            return anchor.href && new URL(anchor.href).hostname !== location.hostname;
        } catch {
            return false;
        }
    }

    // ============================================================
    // 1. Inject <meta name="referrer" content="no-referrer">
    //    Injected as early as possible during document-start
    // ============================================================

    function injectMetaTag() {
        if (!CONFIG.injectMetaTag) return;

        const meta = document.createElement('meta');
        meta.name = 'referrer';
        meta.content = 'no-referrer';

        // <head> may not exist yet at document-start, try multiple mount points
        const parent =
            document.head ||
            document.documentElement ||
            document.querySelector('head');

        if (parent) {
            parent.insertBefore(meta, parent.firstChild);
            log('Meta tag injected into', parent.tagName);
        } else {
            // Very early stage — wait for the DOM to appear before injecting
            const observer = new MutationObserver(() => {
                const target = document.head || document.documentElement;
                if (target) {
                    target.insertBefore(meta, target.firstChild);
                    log('Meta tag injected (deferred)');
                    observer.disconnect();
                }
            });
            observer.observe(document, { childList: true, subtree: true });
        }
    }

    // ============================================================
    // 2. Add rel="noreferrer noopener" to all <a> tags
    // ============================================================

    function patchAnchor(anchor) {
        if (!anchor.href || anchor.dataset.referrerPatched) return;

        // Only patch external links — remove this check to patch all links
        if (!isExternalLink(anchor)) return;

        const relValues = new Set((anchor.rel || '').split(/\s+/).filter(Boolean));
        relValues.add('noreferrer');
        relValues.add('noopener');
        anchor.rel = [...relValues].join(' ');
        anchor.dataset.referrerPatched = '1';

        log('Patched link:', anchor.href);
    }

    function patchAllLinks() {
        if (!CONFIG.patchLinks) return;
        document.querySelectorAll('a[href]').forEach(patchAnchor);
    }

    // ============================================================
    // 3. Intercept window.open — use an intermediate page to strip referrer
    // ============================================================

    function patchWindowOpen() {
        if (!CONFIG.patchWindowOpen) return;

        const originalOpen = window.open;

        window.open = function (url, target, features) {
            if (url) {
                try {
                    const parsed = new URL(url, location.href);
                    if (parsed.hostname !== location.hostname) {
                        // Use a Blob URL as an intermediate page to strip referrer
                        const html = `
              <!DOCTYPE html>
              <html>
              <head>
                <meta name="referrer" content="no-referrer">
                <meta http-equiv="refresh" content="0;url=${parsed.href}">
              </head>
              <body></body>
              </html>`;
                        const blob = new Blob([html], { type: 'text/html' });
                        const blobUrl = URL.createObjectURL(blob);
                        const win = originalOpen.call(window, blobUrl, target, features);

                        // Revoke Blob URL after a short delay
                        setTimeout(() => URL.revokeObjectURL(blobUrl), 3000);
                        log('window.open intercepted:', url);
                        return win;
                    }
                } catch (e) {
                    log('window.open parse error:', e);
                }
            }
            return originalOpen.call(window, url, target, features);
        };

        log('window.open overridden');
    }

    // ============================================================
    // 4. MutationObserver — watch for dynamically added links
    // ============================================================

    function observeDom() {
        if (!CONFIG.observeDom || !CONFIG.patchLinks) return;

        const observer = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType !== Node.ELEMENT_NODE) continue;

                    // If the added node itself is an <a>
                    if (node.tagName === 'A') {
                        patchAnchor(node);
                    }

                    // Check the subtree of the added node for <a> elements
                    if (node.querySelectorAll) {
                        node.querySelectorAll('a[href]').forEach(patchAnchor);
                    }
                }
            }
        });

        // Wait for <body> to exist before observing
        function startObserving() {
            if (document.body) {
                observer.observe(document.body, { childList: true, subtree: true });
                log('DOM Observer started');
            } else {
                requestAnimationFrame(startObserving);
            }
        }

        startObserving();
    }

    // ============================================================
    // Main
    // ============================================================

    if (isExcluded()) {
        log('Current domain is excluded, skipping');
        return;
    }

    // Phase 1: document-start (as early as possible)
    injectMetaTag();
    patchWindowOpen();

    // Phase 2: Patch links after DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            patchAllLinks();
            observeDom();
        });
    } else {
        patchAllLinks();
        observeDom();
    }

    log('Hide Referrer userscript loaded');
})();