Skip to content

Instantly share code, notes, and snippets.

@0xDVC
Last active February 17, 2026 15:46
Show Gist options
  • Select an option

  • Save 0xDVC/4af7dd292067016831e53cd5ae1b6c22 to your computer and use it in GitHub Desktop.

Select an option

Save 0xDVC/4af7dd292067016831e53cd5ae1b6c22 to your computer and use it in GitHub Desktop.
Medium-anti-member-only script for Safari browsers on macOS and iOS

medium-anti-member-only script for Safari browsers on macOS and iOS

i have enjoyed using this version by AmoabaKelvin/Medium-Anti-Member-Only on my laptop for a while now. it works well on chromium and firefox browsers. however, i found it exhausting to have to manually do it on mobile browser(safari) every single time. just had the last of it and whipped this up. you'd need an extension app for safari that supports tampermonkey or greasemonkey really. chose to go with Addons. the attached script is found below this guide.

installation on macOS

  1. install Addons - Scripts & Adblock
  2. toggle to enable the 'Scripts' option. you can disable other options there if you have no need of it.
  3. open the scripts option and click the '+' button at the top right corner of the window to add script.
  4. select manually write, and copy-pasta the code right there(avoid changing stuff unless you know what you're doing)
  5. hit the check button to save it. you can safely close the app now
  6. proceed back to the safari browser, and allow permissions for the extension.

installation on iOS

  1. install Addons - Scripts & Adblock
  2. toggle to enable the 'Scripts' option. you can disable other options there if you have no need of it.
  3. open the scripts option and click the '+' button at the top.
  4. select manually write, and copy-pasta the code right there(avoid changing stuff unless you know what you're doing)
  5. hit the check button to save it. you can safely close the app now
  6. proceed back to the safari browser, and allow permissions for the extension.
  7. by clicking on the button close to the url in the address bar, it should show a menu as you see below.

8. proceed to select the 'Addons' extension there, and ensure that it is toggled on. 9. as you proceed to visit medium articles or pages, you would be requested to allow permissions for the extension.

NB: ensure you allow permissions for medium.com and freedium.cfd in order to allow it work as it should

in all, it should work pretty fine, unless otherwise. feel free to share or contribute to make it better🙂

disclaimer: i only tested this out on my macos 15.4 and ios 26(beta). would like any feedback if it behaves differently on different platforms.

// ==UserScript==
// @name medium anti member only script for safari
// @version 0.1
// @description redirect medium articles to freedium.cfd and rewrite links
// @author 0xdvc
// @match *://*/*
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
const freediumUrl = "https://freedium.cfd/";
if (location.hostname.endsWith('freedium.cfd')) return;
function isMediumSite() {
return /\.medium\.com$/i.test(location.hostname) ||
location.hostname === 'medium.com' ||
document.querySelector('meta[property="og:site_name"][content*="Medium" i]') ||
document.querySelector('meta[name="twitter:app:name:iphone"][content="Medium"]') ||
document.querySelector('article[data-post-id]') ||
document.querySelector('[data-module-result-id]');
}
function hasPaywallContent() {
const paywallSelectors = [
'div[class*="paywall"]',
'div[class*="isPaywalled"]',
'div[class*="meteredContent"]',
'div[class*="membershipPaywall"]',
'div[id*="paywall"]',
'[data-testid*="paywall"]',
'.paywall',
'.js-postMeteredContent',
'div[class*="overlay"]'
];
return paywallSelectors.some(selector => document.querySelector(selector));
}
function hasMembershipText() {
const membershipTexts = [
'Member-only story',
'Members only',
'This story is published in',
'Become a member',
'Sign up to continue'
];
const bodyText = document.body?.textContent || '';
return membershipTexts.some(text => bodyText.includes(text));
}
function redirectToFreedium() {
const currentUrl = location.href;
const freediumRedirect = freediumUrl + encodeURIComponent(currentUrl);
console.log('Redirecting to:', freediumRedirect);
location.replace(freediumRedirect);
}
function rewriteLink(link) {
if (link?.href && link.href.includes('medium.com') && !link.href.includes('freedium.cfd')) {
const originalHref = link.href;
link.href = freediumUrl + encodeURIComponent(originalHref);
link.target = '_blank';
link.rel = 'noopener noreferrer';
link.addEventListener('click', (e) => {
e.preventDefault();
window.open(link.href, '_blank');
}, { capture: true, once: true });
}
}
function scanAndRewriteLinks(root = document) {
const linkSelectors = [
'a[href*="medium.com"]',
'article a',
'[data-post-id] a',
'.js-postArticle a'
];
linkSelectors.forEach(selector => {
root.querySelectorAll(selector).forEach(rewriteLink);
});
}
function setupMutationObserver() {
if (document.body) {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList' && mutation.addedNodes.length) {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1) {
if (isMediumSite() && (hasPaywallContent() || hasMembershipText())) {
redirectToFreedium();
return;
}
if (node.tagName === 'A' && node.href?.includes('medium.com')) {
rewriteLink(node);
} else {
scanAndRewriteLinks(node);
}
}
});
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class', 'data-testid']
});
}
}
if (isMediumSite()) {
console.log('Medium site detected');
if (hasPaywallContent() || hasMembershipText()) {
redirectToFreedium();
return;
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
if (hasPaywallContent() || hasMembershipText()) {
redirectToFreedium();
} else {
scanAndRewriteLinks();
setupMutationObserver();
}
});
} else {
scanAndRewriteLinks();
setupMutationObserver();
}
} else {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
scanAndRewriteLinks();
setupMutationObserver();
});
} else {
scanAndRewriteLinks();
setupMutationObserver();
}
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment