diff --git a/src/avalanche/avalanche.h b/src/avalanche/avalanche.h
--- a/src/avalanche/avalanche.h
+++ b/src/avalanche/avalanche.h
@@ -24,6 +24,16 @@
  */
 static constexpr bool AVALANCHE_DEFAULT_PEER_DISCOVERY_ENABLED = false;
 
+/**
+ * Is proof replacement enabled by default.
+ */
+static constexpr bool AVALANCHE_DEFAULT_PROOF_REPLACEMENT_ENABLED = false;
+
+/**
+ * Proof replacement cooldown time default value in seconds.
+ */
+static constexpr size_t AVALANCHE_DEFAULT_PROOF_REPLACEMENT_COOLDOWN = 60 * 60;
+
 /**
  * Avalanche default cooldown in milliseconds.
  */
diff --git a/src/avalanche/peermanager.h b/src/avalanche/peermanager.h
--- a/src/avalanche/peermanager.h
+++ b/src/avalanche/peermanager.h
@@ -256,6 +256,7 @@
     bool isOrphan(const ProofId &proofid) const;
 
 private:
+    void moveToOrphan(const ProofRef &proof);
     bool createPeer(const ProofRef &proof);
     bool addOrUpdateNode(const PeerSet::iterator &it, NodeId nodeid);
     bool addNodeToPeer(const PeerSet::iterator &it);
diff --git a/src/avalanche/peermanager.cpp b/src/avalanche/peermanager.cpp
--- a/src/avalanche/peermanager.cpp
+++ b/src/avalanche/peermanager.cpp
@@ -4,6 +4,7 @@
 
 #include <avalanche/peermanager.h>
 
+#include <avalanche/avalanche.h>
 #include <avalanche/delegation.h>
 #include <avalanche/validation.h>
 #include <random.h>
@@ -251,10 +252,32 @@
     return orphanProofPool.getProof(proofid) != nullptr;
 }
 
+void PeerManager::moveToOrphan(const ProofRef &proof) {
+    auto &peersView = peers.get<by_proofid>();
+    auto it = peersView.find(proof->getId());
+    if (it != peersView.end()) {
+        removePeer(it->peerid);
+    }
+
+    orphanProofPool.addProofIfPreferred(proof);
+}
+
 bool PeerManager::createPeer(const ProofRef &proof) {
     assert(proof);
 
-    switch (validProofPool.addProofIfNoConflict(proof)) {
+    ProofPool::ConflictingProofSet conflictingProofs;
+    // Set a cooldown time to limit the proof replacement rate
+    std::chrono::seconds cooldown{
+        gArgs.GetArg("-avalancheproofreplacementcooldown",
+                     AVALANCHE_DEFAULT_PROOF_REPLACEMENT_COOLDOWN)};
+    auto added =
+        gArgs.GetBoolArg("-enableavalancheproofreplacement",
+                         AVALANCHE_DEFAULT_PROOF_REPLACEMENT_ENABLED)
+            ? validProofPool.addProofIfPreferred(proof, conflictingProofs,
+                                                 cooldown)
+            : validProofPool.addProofIfNoConflict(proof, conflictingProofs);
+
+    switch (added) {
         case ProofPool::AddProofStatus::REJECTED:
             // The proof has conflicts, orphan the proof so it can be pulled
             // back if the conflicting ones are invalidated.
@@ -269,6 +292,12 @@
             // No default case, so the compiler can warn about missing cases
     }
 
+    // If we have overridden other proofs due to conflict, remove the peers and
+    // attempt to orphan them
+    for (const ProofRef &conflictingProof : conflictingProofs) {
+        moveToOrphan(conflictingProof);
+    }
+
     // New peer means new peerid!
     const PeerId peerid = nextPeerId++;
 
diff --git a/src/avalanche/test/peermanager_tests.cpp b/src/avalanche/test/peermanager_tests.cpp
--- a/src/avalanche/test/peermanager_tests.cpp
+++ b/src/avalanche/test/peermanager_tests.cpp
@@ -15,6 +15,8 @@
 
 #include <boost/test/unit_test.hpp>
 
+#include <tinyformat.h>
+
 using namespace avalanche;
 
 namespace avalanche {
@@ -859,8 +861,6 @@
 }
 
 BOOST_AUTO_TEST_CASE(conflicting_proof_selection) {
-    avalanche::PeerManager pm;
-
     const CKey key = CKey::MakeCompressedKey();
 
     const Amount amount(10 * COIN);
@@ -892,11 +892,23 @@
 
     auto proof_base = buildProofWithSequence(10);
 
+    gArgs.ForceSetArg("-enableavalancheproofreplacement", "1");
+    gArgs.ForceSetArg("avalancheproofreplacementcooldown", "0");
+
     ConflictingProofComparator comparator;
     auto checkPreferred = [&](const ProofRef &candidate,
                               const ProofRef &reference, bool expectAccepted) {
         BOOST_CHECK_EQUAL(comparator(candidate, reference), expectAccepted);
         BOOST_CHECK_EQUAL(comparator(reference, candidate), !expectAccepted);
+
+        avalanche::PeerManager pm;
+        BOOST_CHECK(pm.registerProof(reference));
+        BOOST_CHECK(pm.isBoundToPeer(reference->getId()));
+
+        BOOST_CHECK_EQUAL(pm.registerProof(candidate), expectAccepted);
+        BOOST_CHECK_EQUAL(pm.isBoundToPeer(candidate->getId()), expectAccepted);
+        BOOST_CHECK_EQUAL(pm.isBoundToPeer(reference->getId()),
+                          !expectAccepted);
     };
 
     // Same master key, lower sequence number
@@ -940,6 +952,115 @@
         checkPreferred(proofSimilar, proof_multiUtxo,
                        proofSimilar->getId() < proof_multiUtxo->getId());
     }
+
+    gArgs.ClearForcedArg("-enableavalancheproofreplacement");
+    gArgs.ClearForcedArg("-avalancheproofreplacementcooldown");
+}
+
+BOOST_AUTO_TEST_CASE(conflicting_proofs_cooldown) {
+    avalanche::PeerManager pm;
+
+    int64_t cooldown = 100;
+
+    gArgs.ForceSetArg("-enableavalancheproofreplacement", "1");
+    gArgs.ForceSetArg("avalancheproofreplacementcooldown",
+                      strprintf("%d", cooldown));
+
+    int64_t mockTime = 100;
+    SetMockTime(mockTime);
+
+    const CKey key = CKey::MakeCompressedKey();
+
+    const Amount amount(10 * COIN);
+    const uint32_t height = 100;
+    const bool is_coinbase = false;
+
+    CScript script = GetScriptForDestination(PKHash(key.GetPubKey()));
+
+    auto addCoin = [&](const Amount &amount) {
+        LOCK(cs_main);
+        const COutPoint outpoint(TxId(GetRandHash()), 0);
+        CCoinsViewCache &coins = ::ChainstateActive().CoinsTip();
+        coins.AddCoin(outpoint,
+                      Coin(CTxOut(amount, script), height, is_coinbase), false);
+        return outpoint;
+    };
+
+    auto conflictingOutpoint = addCoin(amount);
+
+    auto buildProofWithSequence = [&](uint64_t sequence) {
+        ProofBuilder pb(sequence, GetRandInt(std::numeric_limits<int>::max()),
+                        key);
+        BOOST_CHECK(
+            pb.addUTXO(conflictingOutpoint, amount, height, is_coinbase, key));
+
+        return pb.build();
+    };
+
+    auto proof_seq10 = buildProofWithSequence(10);
+    auto proof_seq20 = buildProofWithSequence(20);
+    auto proof_seq30 = buildProofWithSequence(30);
+
+    // No conflict means no cooldown
+    BOOST_CHECK(pm.registerProof(proof_seq10));
+    BOOST_CHECK(pm.isBoundToPeer(proof_seq10->getId()));
+    BOOST_CHECK(!pm.isOrphan(proof_seq10->getId()));
+
+    // Cooldown not elapsed, the proof is orphaned and no replacement occurs
+    mockTime += cooldown - 1;
+    SetMockTime(mockTime);
+    BOOST_CHECK(!pm.registerProof(proof_seq30));
+
+    BOOST_CHECK(pm.isBoundToPeer(proof_seq10->getId()));
+    BOOST_CHECK(!pm.isBoundToPeer(proof_seq30->getId()));
+
+    BOOST_CHECK(!pm.isOrphan(proof_seq10->getId()));
+    BOOST_CHECK(pm.isOrphan(proof_seq30->getId()));
+
+    // Cooldown elapsed, replacement can occur. Since there is already a better
+    // orphan candidate, proof_seq10 is evicted.
+    mockTime += 1;
+    SetMockTime(mockTime);
+    BOOST_CHECK(pm.registerProof(proof_seq20));
+
+    BOOST_CHECK(!pm.isBoundToPeer(proof_seq10->getId()));
+    BOOST_CHECK(pm.isBoundToPeer(proof_seq20->getId()));
+    BOOST_CHECK(!pm.isBoundToPeer(proof_seq30->getId()));
+
+    BOOST_CHECK(!pm.isOrphan(proof_seq10->getId()));
+    BOOST_CHECK(!pm.isOrphan(proof_seq20->getId()));
+    BOOST_CHECK(pm.isOrphan(proof_seq30->getId()));
+
+    // Mine a new block will NOT bring back proof_seq30 from orphan to a peer,
+    // because it is the best candidate but the cooldown is not elapsed since
+    // registration of proof_seq20
+    pm.updatedBlockTip();
+
+    BOOST_CHECK(!pm.isBoundToPeer(proof_seq10->getId()));
+    BOOST_CHECK(pm.isBoundToPeer(proof_seq20->getId()));
+    BOOST_CHECK(!pm.isBoundToPeer(proof_seq30->getId()));
+
+    BOOST_CHECK(!pm.isOrphan(proof_seq10->getId()));
+    BOOST_CHECK(!pm.isOrphan(proof_seq20->getId()));
+    BOOST_CHECK(pm.isOrphan(proof_seq30->getId()));
+
+    // Wait for the cooldown and mine a block again. Now proof_seq30 will be
+    // promoted to peer and proof_seq20 will be orphaned.
+    mockTime += cooldown;
+    SetMockTime(mockTime);
+
+    pm.updatedBlockTip();
+
+    BOOST_CHECK(!pm.isBoundToPeer(proof_seq10->getId()));
+    BOOST_CHECK(!pm.isBoundToPeer(proof_seq20->getId()));
+    BOOST_CHECK(pm.isBoundToPeer(proof_seq30->getId()));
+
+    BOOST_CHECK(!pm.isOrphan(proof_seq10->getId()));
+    BOOST_CHECK(pm.isOrphan(proof_seq20->getId()));
+    BOOST_CHECK(!pm.isOrphan(proof_seq30->getId()));
+
+    gArgs.ClearForcedArg("-enableavalancheproofreplacement");
+    gArgs.ClearForcedArg("-avalancheproofreplacementcooldown");
 }
 
 BOOST_AUTO_TEST_CASE(conflicting_orphans) {
diff --git a/src/init.cpp b/src/init.cpp
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1270,6 +1270,15 @@
                    strprintf("Enable avalanche peer discovery (default: %u)",
                              AVALANCHE_DEFAULT_PEER_DISCOVERY_ENABLED),
                    ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
+    argsman.AddArg("-enableavalancheproofreplacement",
+                   strprintf("Enable avalanche proof replacement (default: %u)",
+                             AVALANCHE_DEFAULT_PROOF_REPLACEMENT_ENABLED),
+                   ArgsManager::ALLOW_BOOL, OptionsCategory::AVALANCHE);
+    argsman.AddArg("-avalancheproofreplacementcooldown",
+                   strprintf("Mandatory cooldown before a proof replacement "
+                             "can occur in seconds (default: %u)",
+                             AVALANCHE_DEFAULT_PROOF_REPLACEMENT_COOLDOWN),
+                   ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
     argsman.AddArg(
         "-avacooldown",
         strprintf("Mandatory cooldown between two avapoll (default: %u)",