diff --git a/doc/api/readline.md b/doc/api/readline.md
index 10164bc7887984..59f39e5ae5189d 100644
--- a/doc/api/readline.md
+++ b/doc/api/readline.md
@@ -370,6 +370,9 @@ changes:
     `crlfDelay` milliseconds, both `\r` and `\n` will be treated as separate
     end-of-line input. Default to `100` milliseconds.
     `crlfDelay` will be coerced to `[100, 2000]` range.
+  * `deDupeHistory` {boolean} If `true`, when a new input line added to the
+    history list duplicates an older one, this removes the older line from the
+    list. Defaults to `false`.
 
 The `readline.createInterface()` method creates a new `readline.Interface`
 instance.
diff --git a/lib/readline.js b/lib/readline.js
index a571657a726d2a..cea5c5e6d60910 100644
--- a/lib/readline.js
+++ b/lib/readline.js
@@ -39,6 +39,7 @@ function Interface(input, output, completer, terminal) {
 
   EventEmitter.call(this);
   var historySize;
+  var deDupeHistory = false;
   let crlfDelay;
   let prompt = '> ';
 
@@ -48,6 +49,7 @@ function Interface(input, output, completer, terminal) {
     completer = input.completer;
     terminal = input.terminal;
     historySize = input.historySize;
+    deDupeHistory = input.deDupeHistory;
     if (input.prompt !== undefined) {
       prompt = input.prompt;
     }
@@ -80,6 +82,7 @@ function Interface(input, output, completer, terminal) {
   this.output = output;
   this.input = input;
   this.historySize = historySize;
+  this.deDupeHistory = !!deDupeHistory;
   this.crlfDelay = Math.max(kMincrlfDelay,
                             Math.min(kMaxcrlfDelay, crlfDelay >>> 0));
 
@@ -257,6 +260,12 @@ Interface.prototype._addHistory = function() {
   if (this.line.trim().length === 0) return this.line;
 
   if (this.history.length === 0 || this.history[0] !== this.line) {
+    if (this.deDupeHistory) {
+      // Remove older history line if identical to new one
+      const dupIndex = this.history.indexOf(this.line);
+      if (dupIndex !== -1) this.history.splice(dupIndex, 1);
+    }
+
     this.history.unshift(this.line);
 
     // Only store so many
diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js
index 53c132dc30ec25..09ace7824ec525 100644
--- a/test/parallel/test-readline-interface.js
+++ b/test/parallel/test-readline-interface.js
@@ -305,6 +305,67 @@ function isWarned(emitter) {
     return false;
   });
 
+  // duplicate lines are removed from history when `options.deDupeHistory`
+  // is `true`
+  fi = new FakeInput();
+  rli = new readline.Interface({
+    input: fi,
+    output: fi,
+    terminal: true,
+    deDupeHistory: true
+  });
+  expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat'];
+  callCount = 0;
+  rli.on('line', function(line) {
+    assert.strictEqual(line, expectedLines[callCount]);
+    callCount++;
+  });
+  fi.emit('data', expectedLines.join('\n') + '\n');
+  assert.strictEqual(callCount, expectedLines.length);
+  fi.emit('keypress', '.', { name: 'up' }); // 'bat'
+  assert.strictEqual(rli.line, expectedLines[--callCount]);
+  fi.emit('keypress', '.', { name: 'up' }); // 'bar'
+  assert.notStrictEqual(rli.line, expectedLines[--callCount]);
+  assert.strictEqual(rli.line, expectedLines[--callCount]);
+  fi.emit('keypress', '.', { name: 'up' }); // 'baz'
+  assert.strictEqual(rli.line, expectedLines[--callCount]);
+  fi.emit('keypress', '.', { name: 'up' }); // 'foo'
+  assert.notStrictEqual(rli.line, expectedLines[--callCount]);
+  assert.strictEqual(rli.line, expectedLines[--callCount]);
+  assert.strictEqual(callCount, 0);
+  rli.close();
+
+  // duplicate lines are not removed from history when `options.deDupeHistory`
+  // is `false`
+  fi = new FakeInput();
+  rli = new readline.Interface({
+    input: fi,
+    output: fi,
+    terminal: true,
+    deDupeHistory: false
+  });
+  expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat'];
+  callCount = 0;
+  rli.on('line', function(line) {
+    assert.strictEqual(line, expectedLines[callCount]);
+    callCount++;
+  });
+  fi.emit('data', expectedLines.join('\n') + '\n');
+  assert.strictEqual(callCount, expectedLines.length);
+  fi.emit('keypress', '.', { name: 'up' }); // 'bat'
+  assert.strictEqual(rli.line, expectedLines[--callCount]);
+  fi.emit('keypress', '.', { name: 'up' }); // 'bar'
+  assert.notStrictEqual(rli.line, expectedLines[--callCount]);
+  assert.strictEqual(rli.line, expectedLines[--callCount]);
+  fi.emit('keypress', '.', { name: 'up' }); // 'baz'
+  assert.strictEqual(rli.line, expectedLines[--callCount]);
+  fi.emit('keypress', '.', { name: 'up' }); // 'bar'
+  assert.strictEqual(rli.line, expectedLines[--callCount]);
+  fi.emit('keypress', '.', { name: 'up' }); // 'foo'
+  assert.strictEqual(rli.line, expectedLines[--callCount]);
+  assert.strictEqual(callCount, 0);
+  rli.close();
+
   // sending a multi-byte utf8 char over multiple writes
   const buf = Buffer.from('☮', 'utf8');
   fi = new FakeInput();