diff --git a/web/src/components/dialog/SavingDialog.svelte b/web/src/components/dialog/SavingDialog.svelte
index f71e2b9f..ec719aed 100644
--- a/web/src/components/dialog/SavingDialog.svelte
+++ b/web/src/components/dialog/SavingDialog.svelte
@@ -10,6 +10,8 @@
         shareFile,
     } from "$lib/download";
 
+    import type { CobaltFileUrlType } from "$lib/types/api";
+
     import DialogContainer from "$components/dialog/DialogContainer.svelte";
 
     import Meowbalt from "$components/misc/Meowbalt.svelte";
@@ -22,12 +24,14 @@
     import IconFileDownload from "@tabler/icons-svelte/IconFileDownload.svelte";
 
     import CopyIcon from "$components/misc/CopyIcon.svelte";
+
     export let id: string;
     export let dismissable = true;
     export let bodyText: string = "";
 
     export let url: string = "";
     export let file: File | undefined = undefined;
+    export let urlType: CobaltFileUrlType | undefined = undefined;
 
     let close: () => void;
 
@@ -55,7 +59,7 @@
             </div>
 
             <div class="action-buttons">
-                {#if device.supports.directDownload}
+                {#if device.supports.directDownload && !(device.is.iOS && urlType === "redirect")}
                     <VerticalActionButton
                         id="save-download"
                         fill
diff --git a/web/src/components/save/buttons/DownloadButton.svelte b/web/src/components/save/buttons/DownloadButton.svelte
index e8d24f0f..7dec3a36 100644
--- a/web/src/components/save/buttons/DownloadButton.svelte
+++ b/web/src/components/save/buttons/DownloadButton.svelte
@@ -88,6 +88,7 @@
 
             return downloadFile({
                 url: response.url,
+                urlType: "redirect",
             });
         }
 
diff --git a/web/src/lib/download.ts b/web/src/lib/download.ts
index 78e465de..827b33b1 100644
--- a/web/src/lib/download.ts
+++ b/web/src/lib/download.ts
@@ -4,16 +4,31 @@ import settings from "$lib/state/settings";
 
 import { device } from "$lib/device";
 import { t } from "$lib/i18n/translations";
-
 import { createDialog } from "$lib/dialogs";
-import type { DialogInfo } from "$lib/types/dialog";
 
-const openSavingDialog = ({ url, file, body }: { url?: string, file?: File, body?: string }) => {
+import type { DialogInfo } from "$lib/types/dialog";
+import type { CobaltFileUrlType } from "$lib/types/api";
+
+type DownloadFileParams = {
+    url?: string,
+    file?: File,
+    urlType?: CobaltFileUrlType,
+}
+
+type SavingDialogParams = {
+    url?: string,
+    file?: File,
+    body?: string,
+    urlType?: CobaltFileUrlType,
+}
+
+const openSavingDialog = ({ url, file, body, urlType }: SavingDialogParams) => {
     const dialogData: DialogInfo = {
         type: "saving",
         id: "saving",
         file,
         url,
+        urlType,
     }
     if (body) dialogData.bodyText = body;
 
@@ -60,13 +75,13 @@ export const copyURL = async (url: string) => {
     return await navigator?.clipboard?.writeText(url);
 }
 
-export const downloadFile = ({ url, file }: { url?: string, file?: File }) => {
+export const downloadFile = ({ url, file, urlType }: DownloadFileParams) => {
     if (!url && !file) throw new Error("attempted to download void");
 
     const pref = get(settings).save.savingMethod;
 
     if (pref === "ask") {
-        return openSavingDialog({ url, file });
+        return openSavingDialog({ url, file, urlType });
     }
 
     /*
@@ -85,6 +100,7 @@ export const downloadFile = ({ url, file }: { url?: string, file?: File }) => {
             url,
             file,
             body: get(t)("dialog.saving.timeout"),
+            urlType
         });
     }
 
@@ -100,7 +116,8 @@ export const downloadFile = ({ url, file }: { url?: string, file?: File }) => {
         if (url) {
             if (pref === "share" && device.supports.share) {
                 return shareURL(url);
-            } else if (pref === "download" && device.supports.directDownload) {
+            } else if (pref === "download" && device.supports.directDownload
+                    && !(device.is.iOS && urlType === "redirect")) {
                 return openURL(url);
             } else if (pref === "copy" && !file) {
                 return copyURL(url);
@@ -108,5 +125,5 @@ export const downloadFile = ({ url, file }: { url?: string, file?: File }) => {
         }
     } catch { /* catch & ignore */ }
 
-    return openSavingDialog({ url, file });
+    return openSavingDialog({ url, file, urlType });
 }
diff --git a/web/src/lib/types/api.ts b/web/src/lib/types/api.ts
index e7e4307d..20161775 100644
--- a/web/src/lib/types/api.ts
+++ b/web/src/lib/types/api.ts
@@ -40,6 +40,8 @@ type CobaltTunnelResponse = {
     status: CobaltResponseType.Tunnel,
 } & CobaltPartialURLResponse;
 
+export type CobaltFileUrlType = "redirect" | "tunnel";
+
 export type CobaltSession = {
     token: string,
     exp: number,
@@ -65,6 +67,6 @@ export type CobaltSessionResponse = CobaltSession | CobaltErrorResponse;
 export type CobaltServerInfoResponse = CobaltServerInfo | CobaltErrorResponse;
 
 export type CobaltAPIResponse = CobaltErrorResponse
-                            | CobaltPickerResponse
-                            | CobaltRedirectResponse
-                            | CobaltTunnelResponse;
+    | CobaltPickerResponse
+    | CobaltRedirectResponse
+    | CobaltTunnelResponse;
diff --git a/web/src/lib/types/dialog.ts b/web/src/lib/types/dialog.ts
index fa0df5fe..69e27b5d 100644
--- a/web/src/lib/types/dialog.ts
+++ b/web/src/lib/types/dialog.ts
@@ -1,3 +1,4 @@
+import type { CobaltFileUrlType } from "$lib/types/api";
 import type { MeowbaltEmotions } from "$lib/types/meowbalt";
 
 export type DialogButton = {
@@ -43,6 +44,7 @@ type SavingDialog = Dialog & {
     bodyText?: string,
     url?: string,
     file?: File,
+    urlType?: CobaltFileUrlType,
 };
 
 export type DialogInfo = SmallDialog | PickerDialog | SavingDialog;