diff --git a/qutebrowser/config/__init__.py b/qutebrowser/config/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py
index 914ed43..a2d97ae 100644
--- a/qutebrowser/config/config.py
+++ b/qutebrowser/config/config.py
@@ -3,16 +3,18 @@ from typing import cast
 
 # pylint: disable=C0111
 from qutebrowser.config.config import ConfigContainer  # noqa: F401
-from qutebrowser.config.configfiles import ConfigAPI  # noqa: F401
+from qutebrowser.config.configfiles import ConfigAPI
+
+from freedirect.freedirect import Redirects
 
 config: ConfigAPI = cast(ConfigAPI, config)  # noqa: F821 pylint: disable=E0602,C0103
 c: ConfigContainer = cast(ConfigContainer, c)  # noqa: F821 pylint: disable=E0602,C0103
 
+
 # Autogenerated config.py
 # Documentation:
 #   qute://help/configuring.html
 #   qute://help/settings.html
-
 # load additional settings configured via autoconfig.yml
 _ = config.load_autoconfig()
 
@@ -59,7 +61,9 @@ config.source("alias.py")
 config.source("maps.py")
 config.source("content.py")
 config.source("searchengines.py")
-config.source("redirects.py")
+
+_ = Redirects()
+
 
 # Tab-Bar
 # have tab bar on the right, not on the top
diff --git a/qutebrowser/config/freedirect/__init__.py b/qutebrowser/config/freedirect/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/qutebrowser/config/freedirect/freedirect.py b/qutebrowser/config/freedirect/freedirect.py
new file mode 100644
index 0000000..b18e3f0
--- /dev/null
+++ b/qutebrowser/config/freedirect/freedirect.py
@@ -0,0 +1,203 @@
+import random
+import re
+from dataclasses import dataclass, field
+from typing import Callable
+from urllib import parse
+
+from qutebrowser.api import interceptor
+from qutebrowser.extensions.interceptors import QUrl, RedirectException
+from qutebrowser.utils import message
+
+
+@dataclass
+class Service:
+    source: list[str] = field(default_factory=lambda: [])
+    target: list[str] = field(default_factory=lambda: [])
+    custom_targets: bool = False
+    preprocess: Callable[[QUrl], QUrl] | None = None
+    postprocess: Callable[[QUrl], QUrl] | None = None
+
+    def __contains__(self, item: str):
+        for source in self.source:
+            if re.search(source, item):
+                return True
+        return False
+
+
+def scribe_global_identity(url: QUrl):
+    """Fix external medium blog to scribe translation.
+    Some paths from medium will go through a 'global identity'
+    path which messes up the actual url path we want to go
+    to and puts it in queries. This puts it back on the path.
+    """
+    path = parse.unquote(f"{url.path()}{url.query()}", encoding="ascii")
+    url.setQuery(None)
+    new_path = re.sub(r"m/global-identity-2redirectUrl=", "", path)
+    url.setPath(
+        parse.quote(new_path),
+        mode=QUrl.ParsingMode.StrictMode,
+    )
+    return url
+
+
+def breezewiki_host_to_path(url: QUrl):
+    host = url.host()
+    if wiki := host[0 : host.find(".fandom.com")]:
+        url.setPath(f"/{wiki}{url.path()}")
+    return url
+
+
+default_services = [
+    Service(source=["youtube.com"], target=["invidious"]),
+    Service(source=["stackoverflow.com"], target=["anonymousoverflow"]),
+    Service(source=["odysee.com"], target=["librarian"]),
+    Service(source=["reddit.com"], target=["redlib"]),
+    Service(source=["instagram.com"], target=["proxigram"]),
+    Service(source=["twitter.com"], target=["nitter"]),
+    Service(source=["imdb.com"], target=["libremdb"]),
+    Service(source=["tiktok.com"], target=["proxitok"]),
+    Service(source=["imgur.com"], target=["rimgo"]),
+    Service(
+        source=["medium.com"], target=["scribe"], postprocess=scribe_global_identity
+    ),
+    Service(
+        source=["fandom.com"], target=["breezewiki"], preprocess=breezewiki_host_to_path
+    ),
+    Service(source=["quora.com"], target=["quetre"]),
+    Service(source=["google.com"], target=["whoogle"]),
+    Service(source=["genius.com"], target=["dumb"]),
+    Service(source=["translate.google.com"], target=["lingva", "simplytranslate"]),
+    Service(source=["deepl.com"], target=["simplytranslate"]),
+    Service(source=["bandcamp.com"], target=["tent"]),
+    Service(
+        custom_targets=True,
+        source=["pinterest.com"],
+        target=[
+            "pain.thirtysix.pw",
+            "pt.bloat.cat",
+            "painterest.gitro.xyz",
+        ],
+    ),
+    Service(
+        custom_targets=True,
+        source=["tumblr.com"],
+        target=[
+            "pb.bloat.cat",
+            "tb.opnxng.com",
+            "priviblur.pussthecat.org",
+            "priviblur.thebunny.zone",
+            "priviblur.gitro.xyz",
+            "priviblur.canine.tools",
+        ],
+    ),
+    Service(
+        custom_targets=True,
+        source=["twitch.com"],
+        target=[
+            "safetwitch.drgns.space",
+            "safetwitch.projectsegfau.lt",
+            "stream.whateveritworks.org",
+            "safetwitch.datura.network",
+            "ttv.vern.cc",
+            "safetwitch.frontendfriendly.xyz",
+            "ttv.femboy.band",
+            "twitch.seitan-ayoub.lol",
+            "twitch.sudovanilla.org",
+            "safetwitch.r4fo.com",
+            "safetwitch.ducks.party",
+            "safetwitch.privacyredirect.com",
+            "st.ngn.tf",
+            "safetwitch.darkness.services",
+            "safetwitch.adminforge.de",
+        ],
+    ),
+    Service(
+        custom_targets=True,
+        source=["goodreads.com"],
+        target=[
+            "biblioreads.eu.org",
+            "biblioreads.vercel.app",
+            "biblioreads.mooo.com",
+            "bl.vern.cc",
+            "biblioreads.lunar.icu",
+            "read.whateveritworks.org",
+            "biblioreads.privacyfucking.rocks",
+            "read.seitan-ayoub.lol",
+            "read.freedit.eu",
+            "biblioreads.ducks.party",
+            "biblioreads.snine.nl",
+            "biblioreads.privacyredirect.com",
+            "reads.nezumi.party",
+            "br.bloat.cat",
+            "read.canine.tools",
+        ],
+    ),
+]
+
+
+@dataclass
+class Redirects:
+    services: list[Service] = field(default_factory=lambda: default_services)
+    selector: Callable[[list[str]], str] = lambda c: c[
+        random.randint(0, len(c) - 1)
+    ]  # selection algorithm
+    farside_service: str = (
+        "farside.link"  # Contains url for farside-like redirector (e.g. 'fastsi.de')
+    )
+
+    def __post_init__(self):
+        interceptor.register(self.intercept)
+
+    def intercept(self, request: interceptor.Request) -> None:
+        # TODO: Implement config check (maybe 'privacy.redirect = False?')
+        # if config.get(name="content.oss_redirects") is False:
+        #     return
+
+        if (
+            request.resource_type != interceptor.ResourceType.main_frame
+            or request.request_url.scheme() in {"data", "blob"}
+        ):
+            return
+
+        url = request.request_url
+        if url in self:
+            try:
+                request.redirect(self.get(url))
+            except RedirectException as e:
+                message.error(str(e))
+
+    def get(self, url: QUrl) -> QUrl:
+        service = self.get_service(url)
+        if not service:
+            return url
+
+        foss_host = self.selector(service.target)
+        if service.preprocess:
+            url = service.preprocess(url)
+
+        try:
+            if service.custom_targets:
+                url.setHost(foss_host)
+            else:
+                url.setHost(self.farside_service)
+                url.setPath(f"/{foss_host}{url.path()}")
+        except RedirectException as e:
+            message.error(str(e))
+
+        if service.postprocess:
+            url = service.postprocess(url)
+        return url
+
+    def get_service(self, url: QUrl) -> Service | None:
+        host = url.host()
+        for service in self.services:
+            if host in service:
+                return service
+
+    def __contains__(self, item: QUrl):
+        if self.get_service(item):
+            return True
+        return False
+
+
+_ = Redirects()
diff --git a/qutebrowser/config/redirects.py b/qutebrowser/config/redirects.py
deleted file mode 100644
index d6db906..0000000
--- a/qutebrowser/config/redirects.py
+++ /dev/null
@@ -1,207 +0,0 @@
-import random
-import re
-from typing import Callable
-from urllib import parse
-
-from qutebrowser.api import interceptor
-from qutebrowser.extensions.interceptors import QUrl, RedirectException
-from qutebrowser.utils import message
-
-
-def fixScribePath(url: QUrl):
-    """Fix external medium blog to scribe translation.
-    Some paths from medium will go through a 'global identity'
-    path which messes up the actual url path we want to go
-    to and puts it in queries. This puts it back on the path.
-    """
-    # double unquoting necessary!
-    # I suppose we double-wrap it earlier somewhere?
-    # unquoted = parse.unquote(
-    #     url.path(options=QUrl.ComponentFormattingOption.FullyEncoded)
-    # )
-    path = parse.unquote(f"{url.path()}{url.query()}", encoding="ascii")
-    url.setQuery(None)
-    new_path = re.sub(r"m/global-identity-2redirectUrl=", "", path)
-    url.setPath(
-        parse.quote(new_path),
-        mode=QUrl.ParsingMode.StrictMode,
-    )
-    return url
-
-
-type Service = dict[str, list[str]]
-type Redirects = dict[str, Service]
-
-
-redirects: Redirects = {
-    "youtube": {
-        "source": ["youtube.com"],
-        "farside": ["invidious"],
-    },
-    "stackoverflow": {
-        "source": ["stackoverflow.com", "askubuntu.com"],
-        "farside": ["anonymousoverflow"],
-    },
-    "lbry": {
-        "source": ["odysee.com"],
-        "farside": ["librarian"],
-    },
-    "reddit": {
-        "source": ["reddit.com"],
-        "farside": ["redlib"],
-    },
-    "instagram": {
-        "source": ["instagram.com"],
-        "farside": ["proxigram"],
-    },
-    "twitter": {
-        "source": ["twitter.com"],
-        "farside": ["nitter"],
-    },
-    "imdb": {
-        "source": ["imdb.com"],
-        "farside": ["libremdb"],
-    },
-    "translate": {
-        "source": ["translate.google.com"],
-        "farside": ["lingva"],
-    },
-    "tiktok": {
-        "source": ["tiktok.com"],
-        "farside": ["proxitok"],
-    },
-    "imgur": {
-        "source": ["imgur.com"],
-        "farside": ["rimgo"],
-    },
-    "medium": {
-        "source": ["medium.com"],
-        "farside": ["scribe"],
-        # "postprocess": fixScribePath
-    },
-    "fandom": {
-        "source": ["fandom.com"],
-        "farside": ["breezewiki"],
-    },
-    "quora": {
-        "source": ["quora.com"],
-        "farside": ["quetre"],
-        # "postprocess": lambda url: message.info(f"CALLING QUORA WITH {url}")
-    },
-    "google": {
-        "source": ["google.com"],
-        "target": [
-            "search.albony.xyz",
-            "search.garudalinux.org",
-            "search.dr460nf1r3.org",
-            "s.tokhmi.xyz",
-            "search.sethforprivacy.com",
-            "whoogle.dcs0.hu",
-            "gowogle.voring.me",
-            "whoogle.privacydev.net",
-            "wg.vern.cc",
-            "whoogle.hxvy0.gq",
-            "whoogle.hostux.net",
-            "whoogle.lunar.icu",
-            "wgl.frail.duckdns.org",
-            "whoogle.no-logs.com",
-            "whoogle.ftw.lol",
-            "whoogle-search--replitcomreside.repl.co",
-            "search.notrustverify.ch",
-            "whoogle.datura.network",
-            "whoogle.yepserver.xyz",
-            "search.nezumi.party",
-        ],
-    },
-    "biblioreads": {
-        "source": ["goodreads.com"],
-        "target": [
-            "biblioreads.eu.org",
-            "biblioreads.vercel.app",
-            "biblioreads.mooo.com",
-            "bl.vern.cc",
-            "biblioreads.lunar.icu",
-            "read.whateveritworks.org",
-            "biblioreads.privacyfucking.rocks",
-            "read.seitan-ayoub.lol",
-            "read.freedit.eu",
-            "biblioreads.ducks.party",
-            "biblioreads.snine.nl",
-            "biblioreads.privacyredirect.com",
-        ],
-    },
-    "safetwitch": {
-        "source": ["twitch.tv"],
-        "target": [
-            "safetwitch.drgns.space",
-            "safetwitch.projectsegfau.lt",
-            "stream.whateveritworks.org",
-            "safetwitch.datura.network",
-            "ttv.vern.cc",
-            "safetwitch.frontendfriendly.xyz",
-            "ttv.femboy.band",
-            "twitch.seitan-ayoub.lol",
-            "st.ggtyler.dev",
-            "safetwitch.lunar.icu",
-            "twitch.sudovanilla.com",
-            "safetwitch.r4fo.com",
-            "safetwitch.ducks.party",
-            "safetwitch.nogafam.fr",
-            "safetwitch.privacyredirect.com",
-            "st.ngn.tf",
-        ],
-    },
-}
-
-
-def rewrite(request: interceptor.Request) -> None:
-    # if config.get(name="content.oss_redirects") is False:
-    #     return
-
-    if (
-        request.resource_type != interceptor.ResourceType.main_frame
-        or request.request_url.scheme() in {"data", "blob"}
-    ):
-        return
-
-    url = request.request_url
-
-    if service := _should_be_redirected(url.host()):
-        url = _farside_redirect(
-            url, _pick_random(service["farside" if "farside" in service else "target"])
-        )
-        try:
-            request.redirect(url)
-        except RedirectException as e:
-            message.error(str(e))
-
-        if "postprocess" in service and isinstance(service["postprocess"], Callable):
-            url = service["postprocess"](url)
-
-
-def _farside_redirect(url: QUrl, service: str, use_fastside: bool = True) -> QUrl:
-    try:
-        url.setHost("fastside.link" if use_fastside else "farside.link")
-        url.setPath(f"/{service}{url.path()}")
-    except RedirectException as e:
-        message.error(str(e))
-    return url
-
-
-def _pick_random[T](choices: list[T]) -> T:
-    return choices[random.randint(0, len(choices) - 1)]
-
-
-def _should_be_redirected(
-    # TODO: Update to use typedefs/classes instead of this jumble
-    host: str,
-    redirects: Redirects = redirects,
-) -> Service | None:
-    for service in redirects.values():
-        for source in service["source"]:
-            if re.search(source, host):
-                return service
-    return None
-
-
-interceptor.register(rewrite)