diff --git a/api/funkwhale_api/common/utils.py b/api/funkwhale_api/common/utils.py
index bd9fd87c54de936f642cafc85e535cf7d8e50871..3a54b718af0bed70dc76dbd84b5e691691f76a13 100644
--- a/api/funkwhale_api/common/utils.py
+++ b/api/funkwhale_api/common/utils.py
@@ -147,3 +147,28 @@ def order_for_search(qs, field):
     this function will order the given qs based on the length of the given field
     """
     return qs.annotate(__size=models.functions.Length(field)).order_by("__size")
+
+
+def replace_prefix(queryset, field, old, new):
+    """
+    Given a queryset of objects and a field name, will find objects
+    for which the field have the given value, and replace the old prefix by
+    the new one.
+
+    This is especially useful to find/update bad federation ids, to replace:
+
+    http://wrongprotocolanddomain/path
+
+    by
+
+    https://goodprotocalanddomain/path
+
+    on a whole table with a single query.
+    """
+    qs = queryset.filter(**{"{}__startswith".format(field): old})
+    # we extract the part after the old prefix, and Concat it with our new prefix
+    update = models.functions.Concat(
+        models.Value(new),
+        models.functions.Substr(field, len(old) + 1, output_field=models.CharField()),
+    )
+    return qs.update(**{field: update})
diff --git a/api/funkwhale_api/federation/management/__init__.py b/api/funkwhale_api/federation/management/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/api/funkwhale_api/federation/management/commands/__init__.py b/api/funkwhale_api/federation/management/commands/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/api/funkwhale_api/federation/management/commands/fix_federation_ids.py b/api/funkwhale_api/federation/management/commands/fix_federation_ids.py
new file mode 100644
index 0000000000000000000000000000000000000000..e53eb62b43db818c6996c738f5292f4e90ba8527
--- /dev/null
+++ b/api/funkwhale_api/federation/management/commands/fix_federation_ids.py
@@ -0,0 +1,122 @@
+from django.core.management.base import BaseCommand, CommandError
+
+from funkwhale_api.common import utils
+from funkwhale_api.federation import models as federation_models
+from funkwhale_api.music import models as music_models
+
+
+MODELS = [
+    (music_models.Artist, ["fid"]),
+    (music_models.Album, ["fid"]),
+    (music_models.Track, ["fid"]),
+    (music_models.Upload, ["fid"]),
+    (music_models.Library, ["fid", "followers_url"]),
+    (
+        federation_models.Actor,
+        [
+            "fid",
+            "url",
+            "outbox_url",
+            "inbox_url",
+            "following_url",
+            "followers_url",
+            "shared_inbox_url",
+        ],
+    ),
+    (federation_models.Activity, ["fid"]),
+    (federation_models.Follow, ["fid"]),
+    (federation_models.LibraryFollow, ["fid"]),
+]
+
+
+class Command(BaseCommand):
+    help = """
+    Find and replace wrong protocal/domain in local federation ids.
+
+    Use with caution and only if you know what you are doing.
+    """
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            "old_base_url",
+            type=str,
+            help="The invalid url prefix you want to find and replace, e.g 'http://baddomain'",
+        )
+        parser.add_argument(
+            "new_base_url",
+            type=str,
+            help="The url prefix you want to use in place of the bad one, e.g 'https://gooddomain'",
+        )
+        parser.add_argument(
+            "--noinput",
+            "--no-input",
+            action="store_false",
+            dest="interactive",
+            help="Do NOT prompt the user for input of any kind.",
+        )
+
+        parser.add_argument(
+            "--no-dry-run",
+            action="store_false",
+            dest="dry_run",
+            help="Commit the changes to the database",
+        )
+
+    def handle(self, *args, **options):
+        results = {}
+        old_prefix, new_prefix = options["old_base_url"], options["new_base_url"]
+        for kls, fields in MODELS:
+            results[kls] = {}
+            for field in fields:
+                candidates = kls.objects.filter(
+                    **{"{}__startswith".format(field): old_prefix}
+                )
+                results[kls][field] = candidates.count()
+
+        total = sum([t for k in results.values() for t in k.values()])
+        self.stdout.write("")
+        if total:
+            self.stdout.write(
+                self.style.WARNING(
+                    "Will replace {} found occurences of '{}' by '{}':".format(
+                        total, old_prefix, new_prefix
+                    )
+                )
+            )
+            self.stdout.write("")
+            for kls, fields in results.items():
+                for field, count in fields.items():
+                    self.stdout.write(
+                        "- {}/{} {}.{}".format(
+                            count, kls.objects.count(), kls._meta.label, field
+                        )
+                    )
+
+        else:
+            self.stdout.write(
+                "No objects found with prefix {}, exiting.".format(old_prefix)
+            )
+            return
+        if options["dry_run"]:
+            self.stdout.write(
+                "Run this command with --no-dry-run to perform the replacement."
+            )
+            return
+        self.stdout.write("")
+        if options["interactive"]:
+            message = (
+                "Are you sure you want to perform the replacement on {} objects?\n\n"
+                "Type 'yes' to continue, or 'no' to cancel: "
+            ).format(total)
+            if input("".join(message)) != "yes":
+                raise CommandError("Command canceled.")
+
+        for kls, fields in results.items():
+            for field, count in fields.items():
+                self.stdout.write(
+                    "Replacing {} on {} {}…".format(field, count, kls._meta.label)
+                )
+                candidates = kls.objects.all()
+                utils.replace_prefix(candidates, field, old=old_prefix, new=new_prefix)
+        self.stdout.write("")
+        self.stdout.write(self.style.SUCCESS("Done!"))
diff --git a/api/tests/common/test_utils.py b/api/tests/common/test_utils.py
index 28d757eb56f98f70716a57b938742d4cab6f8738..c5c4a8e239b9e24f1699b3d29b36e2d0b720e0f8 100644
--- a/api/tests/common/test_utils.py
+++ b/api/tests/common/test_utils.py
@@ -8,3 +8,37 @@ def test_chunk_queryset(factories):
 
     assert list(chunks[0]) == actors[0:2]
     assert list(chunks[1]) == actors[2:4]
+
+
+def test_update_prefix(factories):
+    actors = []
+    fid = "http://hello.world/actor/{}/"
+    for i in range(3):
+        actors.append(factories["federation.Actor"](fid=fid.format(i)))
+    noop = [
+        factories["federation.Actor"](fid="https://hello.world/actor/witness/"),
+        factories["federation.Actor"](fid="http://another.world/actor/witness/"),
+        factories["federation.Actor"](fid="http://foo.bar/actor/witness/"),
+    ]
+
+    qs = actors[0].__class__.objects.filter(fid__startswith="http://hello.world")
+    assert qs.count() == 3
+
+    result = utils.replace_prefix(
+        actors[0].__class__.objects.all(),
+        "fid",
+        "http://hello.world",
+        "https://hello.world",
+    )
+
+    assert result == 3
+
+    for n in noop:
+        old = n.fid
+        n.refresh_from_db()
+        assert old == n.fid
+
+    for n in actors:
+        old = n.fid
+        n.refresh_from_db()
+        assert n.fid == old.replace("http://", "https://")
diff --git a/api/tests/federation/test_commands.py b/api/tests/federation/test_commands.py
new file mode 100644
index 0000000000000000000000000000000000000000..621b8cf64da8d175a4d2ac252d6478dbd72d0863
--- /dev/null
+++ b/api/tests/federation/test_commands.py
@@ -0,0 +1,55 @@
+from django.core.management import call_command
+
+from funkwhale_api.federation import models as federation_models
+from funkwhale_api.federation.management.commands import fix_federation_ids
+from funkwhale_api.music import models as music_models
+
+
+def test_fix_fids_dry_run(factories, mocker):
+    replace_prefix = mocker.patch("funkwhale_api.common.utils.replace_prefix")
+
+    call_command("fix_federation_ids", "http://old/", "https://new/", interactive=False)
+
+    replace_prefix.assert_not_called()
+
+
+def test_fix_fids_no_dry_run(factories, mocker, queryset_equal_queries):
+    replace_prefix = mocker.patch("funkwhale_api.common.utils.replace_prefix")
+    factories["federation.Actor"](fid="http://old/test")
+    call_command(
+        "fix_federation_ids",
+        "http://old",
+        "https://new",
+        interactive=False,
+        dry_run=False,
+    )
+
+    models = [
+        (music_models.Artist, ["fid"]),
+        (music_models.Album, ["fid"]),
+        (music_models.Track, ["fid"]),
+        (music_models.Upload, ["fid"]),
+        (music_models.Library, ["fid", "followers_url"]),
+        (
+            federation_models.Actor,
+            [
+                "fid",
+                "url",
+                "outbox_url",
+                "inbox_url",
+                "following_url",
+                "followers_url",
+                "shared_inbox_url",
+            ],
+        ),
+        (federation_models.Activity, ["fid"]),
+        (federation_models.Follow, ["fid"]),
+        (federation_models.LibraryFollow, ["fid"]),
+    ]
+    assert models == fix_federation_ids.MODELS
+
+    for kls, fields in models:
+        for field in fields:
+            replace_prefix.assert_any_call(
+                kls.objects.all(), field, old="http://old", new="https://new"
+            )
diff --git a/changes/changelog.d/706.enhancement b/changes/changelog.d/706.enhancement
new file mode 100644
index 0000000000000000000000000000000000000000..7ede46b983b75cdfb92d910f865b2d9726b18198
--- /dev/null
+++ b/changes/changelog.d/706.enhancement
@@ -0,0 +1,2 @@
+Added a 'fix_federation_ids' management command to deal with protocol/domain issues in federation
+IDs after deployments (#706)