diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h
index 1148458616..af98be4d1b 100644
--- a/Marlin/Configuration.h
+++ b/Marlin/Configuration.h
@@ -719,6 +719,9 @@
  */
 //#define ENDSTOP_NOISE_THRESHOLD 2
 
+// Check for stuck or disconnected endstops during homing moves.
+//#define DETECT_BROKEN_ENDSTOP
+
 //=============================================================================
 //============================== Movement Settings ============================
 //=============================================================================
diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp
index 9cf283ccba..a9a0a7e9bf 100644
--- a/Marlin/src/module/endstops.cpp
+++ b/Marlin/src/module/endstops.cpp
@@ -861,10 +861,6 @@ void Endstops::update() {
 
 #if ENABLED(SPI_ENDSTOPS)
 
-  #define X_STOP (X_HOME_DIR < 0 ? X_MIN : X_MAX)
-  #define Y_STOP (Y_HOME_DIR < 0 ? Y_MIN : Y_MAX)
-  #define Z_STOP (Z_HOME_DIR < 0 ? Z_MIN : Z_MAX)
-
   bool Endstops::tmc_spi_homing_check() {
     bool hit = false;
     #if X_SPI_SENSORLESS
@@ -875,7 +871,7 @@ void Endstops::update() {
           || stepperZ.test_stall_status()
         #endif
       )) {
-        SBI(live_state, X_STOP);
+        SBI(live_state, X_ENDSTOP);
         hit = true;
       }
     #endif
@@ -887,7 +883,7 @@ void Endstops::update() {
           || stepperZ.test_stall_status()
         #endif
       )) {
-        SBI(live_state, Y_STOP);
+        SBI(live_state, Y_ENDSTOP);
         hit = true;
       }
     #endif
@@ -899,7 +895,7 @@ void Endstops::update() {
           || stepperY.test_stall_status()
         #endif
       )) {
-        SBI(live_state, Z_STOP);
+        SBI(live_state, Z_ENDSTOP);
         hit = true;
       }
     #endif
@@ -907,9 +903,9 @@ void Endstops::update() {
   }
 
   void Endstops::clear_endstop_state() {
-    TERN_(X_SPI_SENSORLESS, CBI(live_state, X_STOP));
-    TERN_(Y_SPI_SENSORLESS, CBI(live_state, Y_STOP));
-    TERN_(Z_SPI_SENSORLESS, CBI(live_state, Z_STOP));
+    TERN_(X_SPI_SENSORLESS, CBI(live_state, X_ENDSTOP));
+    TERN_(Y_SPI_SENSORLESS, CBI(live_state, Y_ENDSTOP));
+    TERN_(Z_SPI_SENSORLESS, CBI(live_state, Z_ENDSTOP));
   }
 
 #endif // SPI_ENDSTOPS
diff --git a/Marlin/src/module/endstops.h b/Marlin/src/module/endstops.h
index 0121492bd6..a5ae8d2f92 100644
--- a/Marlin/src/module/endstops.h
+++ b/Marlin/src/module/endstops.h
@@ -38,6 +38,10 @@ enum EndstopEnum : char {
   Z4_MIN, Z4_MAX
 };
 
+#define X_ENDSTOP (X_HOME_DIR < 0 ? X_MIN : X_MAX)
+#define Y_ENDSTOP (Y_HOME_DIR < 0 ? Y_MIN : Y_MAX)
+#define Z_ENDSTOP (Z_HOME_DIR < 0 ? TERN(HOMING_Z_WITH_PROBE, Z_MIN, Z_MIN_PROBE) : Z_MAX)
+
 class Endstops {
   public:
     #if HAS_EXTRA_ENDSTOPS
diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp
index 5b7845e45b..539ceac787 100644
--- a/Marlin/src/module/motion.cpp
+++ b/Marlin/src/module/motion.cpp
@@ -1606,6 +1606,21 @@ void homeaxis(const AxisEnum axis) {
       #endif
     );
 
+    #if ENABLED(DETECT_BROKEN_ENDSTOP)
+      // Check for a broken endstop
+      EndstopEnum es;
+      switch (axis) {
+        default:
+        case X_AXIS: es = X_ENDSTOP; break;
+        case Y_AXIS: es = Y_ENDSTOP; break;
+        case Z_AXIS: es = Z_ENDSTOP; break;
+      }
+      if (TEST(endstops.state(), es)) {
+        SERIAL_ECHO_MSG("Bad ", axis_codes[axis], " Endstop?");
+        kill(GET_TEXT(MSG_KILL_HOMING_FAILED));
+      }
+    #endif
+
     // Slow move towards endstop until triggered
     if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Home 2 Slow:");
 
diff --git a/buildroot/tests/mega2560-tests b/buildroot/tests/mega2560-tests
index 1c299ad307..3a969c9cf7 100755
--- a/buildroot/tests/mega2560-tests
+++ b/buildroot/tests/mega2560-tests
@@ -71,7 +71,7 @@ opt_set NUM_SERVOS 1
 opt_enable ZONESTAR_LCD Z_PROBE_SERVO_NR Z_SERVO_ANGLES DEACTIVATE_SERVOS_AFTER_MOVE BOOT_MARLIN_LOGO_ANIMATED \
            AUTO_BED_LEVELING_3POINT DEBUG_LEVELING_FEATURE EEPROM_SETTINGS EEPROM_CHITCHAT M114_DETAIL \
            NO_VOLUMETRICS EXTENDED_CAPABILITIES_REPORT AUTO_REPORT_TEMPERATURES AUTOTEMP G38_PROBE_TARGET JOYSTICK \
-           PRUSA_MMU2 MMU2_MENUS PRUSA_MMU2_S_MODE DIRECT_STEPPING \
+           PRUSA_MMU2 MMU2_MENUS PRUSA_MMU2_S_MODE DIRECT_STEPPING DETECT_BROKEN_ENDSTOP \
            FILAMENT_RUNOUT_SENSOR NOZZLE_PARK_FEATURE ADVANCED_PAUSE_FEATURE Z_SAFE_HOMING
 exec_test $1 $2 "RAMPS | ZONESTAR + Chinese | MMU2 | Servo | 3-Point + Debug | G38 ..."