diff --git a/e2e/test/scenarios/dashboard/dashboard.cy.spec.js b/e2e/test/scenarios/dashboard/dashboard.cy.spec.js
index 611a12f5b040fbf2add0a80eb3aef0e662360533..abff4256578f81be92c1ef114229b247f31822ee 100644
--- a/e2e/test/scenarios/dashboard/dashboard.cy.spec.js
+++ b/e2e/test/scenarios/dashboard/dashboard.cy.spec.js
@@ -336,6 +336,9 @@ describe("scenarios > dashboard", () => {
         cy.log("Should revert the title change if editing is cancelled");
         cy.findByTestId("dashboard-name-heading").clear().type(newTitle).blur();
         cy.findByTestId("edit-bar").button("Cancel").click();
+        modal().within(() => {
+          cy.button("Leave anyway").click();
+        });
         cy.findByTestId("edit-bar").should("not.exist");
         cy.get("@updateDashboardSpy").should("not.have.been.called");
         cy.findByDisplayValue(originalDashboardName);
diff --git a/e2e/test/scenarios/question/settings.cy.spec.js b/e2e/test/scenarios/question/settings.cy.spec.js
index 7e45d08d3a49df2533909c088e36ab3df3797a60..e20b752dec181e98a5274c536618476c3194b94c 100644
--- a/e2e/test/scenarios/question/settings.cy.spec.js
+++ b/e2e/test/scenarios/question/settings.cy.spec.js
@@ -5,6 +5,7 @@ import {
   openNavigationSidebar,
   visitQuestionAdhoc,
   popover,
+  modal,
   sidebar,
   moveColumnDown,
 } from "e2e/support/helpers";
@@ -427,6 +428,9 @@ describe("scenarios > question > settings", () => {
       cy.contains("Orders in a dashboard").click();
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Cancel").click();
+      modal().within(() => {
+        cy.button("Leave anyway").click();
+      });
 
       // create a new question to see if the "add to a dashboard" modal is still there
       openNavigationSidebar();
diff --git a/frontend/src/metabase/dashboard/containers/DashboardApp/DashboardApp.unit.spec.tsx b/frontend/src/metabase/dashboard/containers/DashboardApp/DashboardApp.unit.spec.tsx
index 90ea9fc3a032d3a3d540962342504d9dab40eadd..1de6005f847fa3bb5003a857fa233bd74baa4811 100644
--- a/frontend/src/metabase/dashboard/containers/DashboardApp/DashboardApp.unit.spec.tsx
+++ b/frontend/src/metabase/dashboard/containers/DashboardApp/DashboardApp.unit.spec.tsx
@@ -161,6 +161,8 @@ describe("DashboardApp", function () {
         screen.queryAllByTestId("loading-spinner"),
       );
 
+      userEvent.click(screen.getByLabelText("Edit dashboard"));
+
       history.goBack();
 
       expect(
@@ -197,6 +199,41 @@ describe("DashboardApp", function () {
         ),
       ).toBeInTheDocument();
     });
+
+    it("does not show custom warning modal when leaving with no changes via Cancel button", async () => {
+      await setup();
+
+      userEvent.click(screen.getByLabelText("Edit dashboard"));
+
+      userEvent.click(screen.getByRole("button", { name: "Cancel" }));
+
+      expect(
+        screen.queryByText("Changes were not saved"),
+      ).not.toBeInTheDocument();
+      expect(
+        screen.queryByText(
+          "Navigating away from here will cause you to lose any changes you have made.",
+        ),
+      ).not.toBeInTheDocument();
+    });
+
+    it("shows custom warning modal when leaving with unsaved changes via Cancel button", async () => {
+      await setup();
+
+      userEvent.click(screen.getByLabelText("Edit dashboard"));
+      userEvent.click(screen.getByTestId("dashboard-name-heading"));
+      userEvent.type(screen.getByTestId("dashboard-name-heading"), "a");
+      userEvent.tab(); // need to click away from the input to trigger the isDirty flag
+
+      userEvent.click(screen.getByRole("button", { name: "Cancel" }));
+
+      expect(screen.getByText("Changes were not saved")).toBeInTheDocument();
+      expect(
+        screen.getByText(
+          "Navigating away from here will cause you to lose any changes you have made.",
+        ),
+      ).toBeInTheDocument();
+    });
   });
 
   describe("empty dashboard", () => {
diff --git a/frontend/src/metabase/dashboard/containers/DashboardHeader.jsx b/frontend/src/metabase/dashboard/containers/DashboardHeader.jsx
index 3ed6901f8c86cc3edecca6ab0b25a9c85bd5ab9f..817108b5ddac2a52c92229a90a746ef4ce3cbcac 100644
--- a/frontend/src/metabase/dashboard/containers/DashboardHeader.jsx
+++ b/frontend/src/metabase/dashboard/containers/DashboardHeader.jsx
@@ -10,6 +10,8 @@ import { trackExportDashboardToPDF } from "metabase/dashboard/analytics";
 import { getIsNavbarOpen } from "metabase/selectors/app";
 
 import ActionButton from "metabase/components/ActionButton";
+import ConfirmContent from "metabase/components/ConfirmContent";
+import Modal from "metabase/components/Modal";
 import Button from "metabase/core/components/Button";
 import { Icon } from "metabase/core/components/Icon";
 import Tooltip from "metabase/core/components/Tooltip";
@@ -81,7 +83,7 @@ class DashboardHeader extends Component {
   }
 
   state = {
-    modal: null,
+    showCancelWarning: false,
   };
 
   static propTypes = {
@@ -89,6 +91,7 @@ class DashboardHeader extends Component {
     fetchPulseFormInput: PropTypes.func.isRequired,
     formInput: PropTypes.object.isRequired,
     isAdmin: PropTypes.bool,
+    isDirty: PropTypes.bool,
     isEditing: PropTypes.oneOfType([PropTypes.bool, PropTypes.object])
       .isRequired,
     isFullscreen: PropTypes.bool.isRequired,
@@ -151,6 +154,10 @@ class DashboardHeader extends Component {
     this.props.onEditingChange(dashboard);
   }
 
+  handleCancelWarningClose = () => {
+    this.setState({ showCancelWarning: false });
+  };
+
   handleToggleBookmark() {
     const { createBookmark, deleteBookmark, isBookmarked } = this.props;
 
@@ -206,10 +213,20 @@ class DashboardHeader extends Component {
     this.onDoneEditing();
   }
 
-  async onCancel() {
+  onRequestCancel = () => {
+    const { isDirty, isEditing } = this.props;
+
+    if (isDirty && isEditing) {
+      this.setState({ showCancelWarning: true });
+    } else {
+      this.onCancel();
+    }
+  };
+
+  onCancel = () => {
     this.onRevert();
     this.onDoneEditing();
-  }
+  };
 
   getEditWarning(dashboard) {
     if (dashboard.embedding_params) {
@@ -234,7 +251,7 @@ class DashboardHeader extends Component {
         data-metabase-event="Dashboard;Cancel Edits"
         key="cancel"
         className="Button Button--small mr1"
-        onClick={() => this.onCancel()}
+        onClick={this.onRequestCancel}
       >
         {t`Cancel`}
       </Button>,
@@ -502,31 +519,47 @@ class DashboardHeader extends Component {
       setSidebar,
       isHomepageDashboard,
     } = this.props;
+    const { showCancelWarning } = this.state;
     const hasLastEditInfo = dashboard["last-edit-info"] != null;
 
     return (
-      <DashboardHeaderComponent
-        headerClassName="wrapper"
-        objectType="dashboard"
-        analyticsContext="Dashboard"
-        location={this.props.location}
-        dashboard={dashboard}
-        isEditing={isEditing}
-        isBadgeVisible={!isEditing && !isFullscreen && isAdditionalInfoVisible}
-        isLastEditInfoVisible={hasLastEditInfo && isAdditionalInfoVisible}
-        isEditingInfo={isEditing}
-        isNavBarOpen={this.props.isNavBarOpen}
-        headerButtons={this.getHeaderButtons()}
-        editWarning={this.getEditWarning(dashboard)}
-        editingTitle={t`You're editing this dashboard.`.concat(
-          isHomepageDashboard
-            ? t` Remember that this dashboard is set as homepage.`
-            : "",
-        )}
-        editingButtons={this.getEditingButtons()}
-        setDashboardAttribute={setDashboardAttribute}
-        onLastEditInfoClick={() => setSidebar({ name: SIDEBAR_NAME.info })}
-      />
+      <>
+        <DashboardHeaderComponent
+          headerClassName="wrapper"
+          objectType="dashboard"
+          analyticsContext="Dashboard"
+          location={this.props.location}
+          dashboard={dashboard}
+          isEditing={isEditing}
+          isBadgeVisible={
+            !isEditing && !isFullscreen && isAdditionalInfoVisible
+          }
+          isLastEditInfoVisible={hasLastEditInfo && isAdditionalInfoVisible}
+          isEditingInfo={isEditing}
+          isNavBarOpen={this.props.isNavBarOpen}
+          headerButtons={this.getHeaderButtons()}
+          editWarning={this.getEditWarning(dashboard)}
+          editingTitle={t`You're editing this dashboard.`.concat(
+            isHomepageDashboard
+              ? t` Remember that this dashboard is set as homepage.`
+              : "",
+          )}
+          editingButtons={this.getEditingButtons()}
+          setDashboardAttribute={setDashboardAttribute}
+          onLastEditInfoClick={() => setSidebar({ name: SIDEBAR_NAME.info })}
+        />
+
+        <Modal isOpen={showCancelWarning}>
+          <ConfirmContent
+            title={t`Changes were not saved`}
+            message={t`Navigating away from here will cause you to lose any changes you have made.`}
+            confirmButtonText={t`Leave anyway`}
+            cancelButtonText={t`Cancel`}
+            onClose={this.handleCancelWarningClose}
+            onAction={this.onCancel}
+          />
+        </Modal>
+      </>
     );
   }
 }