Automatically redirects AMP pages to their original canonical links, and strips AMP hijacking from Google Search and News
// ==UserScript==
// @name Disable AMP Pages
// @namespace RGlzYWJsZSBBTVAgUGFnZXM
// @version 1.0
// @description Automatically redirects AMP pages to their original canonical links, and strips AMP hijacking from Google Search and News
// @author smed79
// @license GPLv3
// @icon https://i25.servimg.com/u/f25/11/94/21/24/amp10.png
// @match *://*/*
// @run-at document-start
// @grant none
// ==/UserScript==
(function() {
'use strict';
const host = window.location.hostname;
const href = window.location.href;
// Helper: Safely extract Canonical URL
const getCanonicalUrl = () => {
const link = document.querySelector('link[rel="canonical"]');
if (!link || !link.href) return null;
try {
const url = new URL(link.href);
// Remove lingering amp query parameters
if (url.searchParams.has("amp")) url.searchParams.delete("amp");
return url.href;
} catch (e) {
return null;
}
};
// --- 1. Clean Google Search & News Links ---
const cleanGoogleLinks = () => {
if (!host.includes('google.')) return;
// Clean Google Search results (Removes AMP hijacking and tracking)
const hijackAttrs = ['ping', 'data-ved', 'data-amp-cur', 'data-amp-title', 'data-amp', 'data-amp-vgi', 'jsaction', 'data-amp-cdn'];
document.querySelectorAll('a[data-amp], a[data-amp-cdn], a[data-ved]:has(span + svg)').forEach(a => {
hijackAttrs.forEach(attr => a.removeAttribute(attr));
// Fix hardcoded AMP CDN URLs
if (a.href.includes('cdn.ampproject.org/')) {
const match = a.href.match(/cdn\.ampproject\.org\/(?:v|wp|c)\/s\/(.+)/);
if (match && match[1]) {
a.href = 'https://' + decodeURIComponent(match[1]).split('?')[0];
}
}
// Hide the AMP logo icon next to the link
const icon = a.querySelector('[aria-label="AMP logo"], [aria-label="Logo AMP"]');
if (icon) icon.style.display = 'none';
});
// Clean Google News (Decodes base64 jslog to find the real URL)
if (host.includes('news.google.')) {
document.querySelectorAll('a[jslog]:not([data-cleaned])').forEach(a => {
const jslog = a.getAttribute('jslog');
if (!jslog) return;
try {
const b64 = jslog.substring(jslog.indexOf(":") + 1, jslog.indexOf(";"));
const decodedStr = atob(b64);
// Match any valid http/https URL inside the decoded payload
const urlMatch = decodedStr.match(/(https?:\/\/[^"'\s\]]+)/g);
if (urlMatch) {
const realUrl = urlMatch.find(url => !url.includes('amp') && !url.includes('google.com'));
if (realUrl) {
a.href = realUrl;
a.dataset.cleaned = "true"; // Prevent processing twice
// Force native click to bypass Google's JS listeners
a.addEventListener('click', (e) => {
e.preventDefault();
e.stopImmediatePropagation();
window.location.href = realUrl;
}, true);
}
}
} catch (e) {}
});
}
};
// --- 2. Redirect standard AMP pages ---
const checkAMPPage = () => {
// Detects standard AMP properties natively rather than guessing URLs
const isAMP = (document.documentElement && (
document.documentElement.hasAttribute('amp') ||
document.documentElement.hasAttribute('⚡'))) ||
document.querySelector('script[src*="cdn.ampproject.org"]');
if (isAMP) {
const canonical = getCanonicalUrl();
if (canonical && canonical !== window.location.href) {
window.location.replace(canonical);
return true; // Indicates we are redirecting
}
}
return false;
};
// --- 3. Redirect Yandex Turbo Pages ---
const checkYandexTurbo = () => {
if ((host.includes('yandex.ru') && href.includes('/turbo')) || host.includes('turbopages.org')) {
const canonical = getCanonicalUrl();
if (canonical) {
window.location.replace(canonical);
return true;
}
}
return false;
};
// --- Execution ---
const observer = new MutationObserver(() => {
// 1. Run Google Checks First
cleanGoogleLinks();
// 2. Check standard AMP site redirects (If redirecting, stop processing)
if (checkAMPPage()) return;
// 3. Check Yandex Turbo
checkYandexTurbo();
});
// Safely attach observer as early as possible
if (document.documentElement) {
observer.observe(document.documentElement, { childList: true, subtree: true });
} else {
const initObserver = new MutationObserver((mutations, me) => {
if (document.documentElement) {
observer.observe(document.documentElement, { childList: true, subtree: true });
me.disconnect();
}
});
initObserver.observe(document, { childList: true, subtree: true });
}
})();