diff --git a/packages/desktop-client/src/browser-preload.browser.js b/packages/desktop-client/src/browser-preload.browser.js
index 75065ca521db9b8cbf1e6a359252a64626f95e38..2522fce90657c90e2a24bbc0693685de0cf93a70 100644
--- a/packages/desktop-client/src/browser-preload.browser.js
+++ b/packages/desktop-client/src/browser-preload.browser.js
@@ -51,6 +51,22 @@ global.Actual = {
     window.location.reload();
   },
 
+  reload: () => {
+    if (window.navigator.serviceWorker == null) return;
+
+    // Unregister the service worker handling routing and then reload. This should force the reload
+    // to query the actual server rather than delegating to the worker
+    return window.navigator.serviceWorker
+      .getRegistration('/')
+      .then(registration => {
+        if (registration == null) return;
+        return registration.unregister();
+      })
+      .then(() => {
+        window.location.reload();
+      });
+  },
+
   restartElectronServer: () => {},
 
   openFileDialog: async ({ filters = [] }) => {
diff --git a/packages/desktop-client/src/global-events.ts b/packages/desktop-client/src/global-events.ts
index 81de6392789c6fca08fed3ec00cad19bb4c72281..619f673b259c1ca381e89c289890a933951a93ae 100644
--- a/packages/desktop-client/src/global-events.ts
+++ b/packages/desktop-client/src/global-events.ts
@@ -142,4 +142,8 @@ export function handleGlobalEvents(actions: BoundActions, store: Store<State>) {
     actions.closeBudgetUI();
     actions.setAppState({ loadingText: null });
   });
+
+  listen('api-fetch-redirected', () => {
+    actions.reloadApp();
+  });
 }
diff --git a/packages/loot-core/src/client/actions/app.ts b/packages/loot-core/src/client/actions/app.ts
index 6f6ca76963714063a2a4bb7d6a3634a60dcf2739..e586f55dc69242e86256dab16e22d38e1a0a7ed5 100644
--- a/packages/loot-core/src/client/actions/app.ts
+++ b/packages/loot-core/src/client/actions/app.ts
@@ -46,3 +46,9 @@ export function focused() {
     return send('app-focused');
   };
 }
+
+export function reloadApp() {
+  return () => {
+    global.Actual.reload();
+  };
+}
diff --git a/packages/loot-core/src/platform/server/fetch/index.web.ts b/packages/loot-core/src/platform/server/fetch/index.web.ts
index db20b9acb807998f47ca1d11451f1a08812d2129..f93196b0f5fb5e6f80c276dca760fdfc397248ef 100644
--- a/packages/loot-core/src/platform/server/fetch/index.web.ts
+++ b/packages/loot-core/src/platform/server/fetch/index.web.ts
@@ -1 +1,19 @@
-export const fetch = globalThis.fetch;
+import * as connection from '../connection';
+
+export const fetch = async (
+  input: RequestInfo | URL,
+  options?: RequestInit,
+): Promise<Response> => {
+  const response = await globalThis.fetch(input, options);
+
+  // Detect if the API query has been redirected to a different origin. This may indicate that the
+  // request has been intercepted by an authentication proxy
+  const originalUrl = new URL(input instanceof Request ? input.url : input);
+  const responseUrl = new URL(response.url);
+  if (response.redirected && responseUrl.host !== originalUrl.host) {
+    connection.send('api-fetch-redirected');
+    throw new Error(`API request redirected to ${responseUrl.host}`);
+  }
+
+  return response;
+};
diff --git a/packages/loot-core/src/types/server-events.d.ts b/packages/loot-core/src/types/server-events.d.ts
index d256ddfd422abf4d7dd580648f434d754a3f6180..c53004a9cab5416aa947457e3d142768aa507c38 100644
--- a/packages/loot-core/src/types/server-events.d.ts
+++ b/packages/loot-core/src/types/server-events.d.ts
@@ -18,4 +18,5 @@ export interface ServerEvents {
   'start-load': unknown;
   'sync-event': { type; subtype; meta; tables; syncDisabled };
   'undo-event': UndoState;
+  'api-fetch-redirected': unknown;
 }
diff --git a/packages/loot-core/typings/window.d.ts b/packages/loot-core/typings/window.d.ts
index 870114cb64bf6e620900bf009bfa81740eb645ab..9dde1c2b45b93e4d44a926a2699e2097709f4095 100644
--- a/packages/loot-core/typings/window.d.ts
+++ b/packages/loot-core/typings/window.d.ts
@@ -15,6 +15,7 @@ declare global {
         opts: Parameters<import('electron').Dialog['showOpenDialogSync']>[0],
       ) => Promise<string[]>;
       relaunch: () => void;
+      reload: (() => Promise<void>) | undefined;
       restartElectronServer: () => void;
     };
 
diff --git a/upcoming-release-notes/3286.md b/upcoming-release-notes/3286.md
new file mode 100644
index 0000000000000000000000000000000000000000..d055ab0de8cde2bb12f4e37efb27941dfa9317a2
--- /dev/null
+++ b/upcoming-release-notes/3286.md
@@ -0,0 +1,6 @@
+---
+category: Bugfix
+authors: [TimQuelch]
+---
+
+Forcibly reload app when API request is redirected. This fixes issue #2793