diff --git a/reviewboard/diffviewer/opcode_generator.py b/reviewboard/diffviewer/opcode_generator.py
index a71a3f5f7ae1ae55240070a7efd6a96838ee7a20..c0ea8395055403d0d88103539c9eff6ca7baed8b 100644
--- a/reviewboard/diffviewer/opcode_generator.py
+++ b/reviewboard/diffviewer/opcode_generator.py
@@ -315,6 +315,8 @@ class DiffOpcodeGenerator(object):
         r_move_ranges = {}  # key -> (start, end, group)
         move_key = None
 
+        is_replace = (itag == 'replace')
+
         # Loop through every location from ij1 through ij2 - 1 until we've
         # reached the end.
         while i_move_cur < ij2:
@@ -361,13 +363,17 @@ class DiffOpcodeGenerator(object):
                             r_move_range.add_group(rgroup, rgroup_index)
                             updated_range = True
                     else:
-                        # We don't have any move ranges yet, or we're done
-                        # with the existing range, so it's time to build one
-                        # based on any removed lines we find that match the
-                        # inserted line.
-                        r_move_ranges[move_key] = \
-                            MoveRange(ri, ri, [(rgroup, rgroup_index)])
-                        updated_range = True
+                        # Check that this isn't a replace line that's just
+                        # "replacing" itself (which would happen if it's just
+                        # changing whitespace).
+                        if not is_replace or i_move_cur - ij1 != ri - ii1:
+                            # We don't have any move ranges yet, or we're done
+                            # with the existing range, so it's time to build one
+                            # based on any removed lines we find that match the
+                            # inserted line.
+                            r_move_ranges[move_key] = \
+                                MoveRange(ri, ri, [(rgroup, rgroup_index)])
+                            updated_range = True
 
                 if not updated_range and r_move_ranges:
                     # We didn't find a move range that this line is a part
diff --git a/reviewboard/diffviewer/tests.py b/reviewboard/diffviewer/tests.py
index 0d5643ae1dcc5ba168b9d6bce1591b75a679711b..a472abca32f0504cbf969b22373ceeb1787e83d4 100644
--- a/reviewboard/diffviewer/tests.py
+++ b/reviewboard/diffviewer/tests.py
@@ -330,6 +330,27 @@ class DiffParserTest(TestCase):
             ]
         )
 
+    def test_move_detection_with_whitespace_replace_lines(self):
+        """Testing diff viewer move detection with whitespace-only
+        changes on replace lines
+        """
+        self._test_move_detection(
+            [
+                'this is line 1, and it is sufficiently long',
+                '-------------------------------------------',
+                '-------------------------------------------',
+                'this is line 2, and it is sufficiently long  ',
+            ],
+            [
+                '  this is line 1, and it is sufficiently long',
+                '-------------------------------------------',
+                '-------------------------------------------',
+                'this is line 2, and it is sufficiently long',
+            ],
+            [],
+            []
+        )
+
     def test_move_detection_with_last_line_in_range(self):
         """Testing diff viewer move detection with last line in a range"""
         # The move detection rewrite in 2.0 introduced an off-by-one where
