diff --git a/blockchain/v2/reactor_test.go b/blockchain/v2/reactor_test.go
index d3231a4ae..02c991cc6 100644
--- a/blockchain/v2/reactor_test.go
+++ b/blockchain/v2/reactor_test.go
@@ -43,6 +43,7 @@ func (mp mockPeer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: mp.RemoteIP()
 
 func (mp mockPeer) IsOutbound() bool   { return true }
 func (mp mockPeer) IsPersistent() bool { return true }
+func (mp mockPeer) HasIPChanged() bool { return false }
 func (mp mockPeer) CloseConn() error   { return nil }
 
 func (mp mockPeer) NodeInfo() p2p.NodeInfo {
diff --git a/p2p/mock/peer.go b/p2p/mock/peer.go
index 31ce85623..0ceea3c53 100644
--- a/p2p/mock/peer.go
+++ b/p2p/mock/peer.go
@@ -57,6 +57,7 @@ func (mp *Peer) Status() conn.ConnectionStatus { return conn.ConnectionStatus{}
 func (mp *Peer) ID() p2p.ID                    { return mp.id }
 func (mp *Peer) IsOutbound() bool              { return mp.Outbound }
 func (mp *Peer) IsPersistent() bool            { return mp.Persistent }
+func (mp *Peer) HasIPChanged() bool            { return false }
 func (mp *Peer) Get(key string) interface{} {
 	if value, ok := mp.kv[key]; ok {
 		return value
diff --git a/p2p/mocks/peer.go b/p2p/mocks/peer.go
index 92f0106e1..808612715 100644
--- a/p2p/mocks/peer.go
+++ b/p2p/mocks/peer.go
@@ -81,6 +81,20 @@ func (_m *Peer) ID() p2p.ID {
 	return r0
 }
 
+// HasIPChanged provides a mock function with given fields:
+func (_m *Peer) HasIPChanged() bool {
+	ret := _m.Called()
+
+	var r0 bool
+	if rf, ok := ret.Get(0).(func() bool); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Get(0).(bool)
+	}
+
+	return r0
+}
+
 // IsOutbound provides a mock function with given fields:
 func (_m *Peer) IsOutbound() bool {
 	ret := _m.Called()
diff --git a/p2p/mocks/peer_envelope_sender.go b/p2p/mocks/peer_envelope_sender.go
index 89f231104..56f2d4a06 100644
--- a/p2p/mocks/peer_envelope_sender.go
+++ b/p2p/mocks/peer_envelope_sender.go
@@ -109,6 +109,20 @@ func (_m *PeerEnvelopeSender) IsPersistent() bool {
 	return r0
 }
 
+// HasIPChanged provides a mock function for given fields:
+func (_m *PeerEnvelopeSender) HasIPChanged() bool {
+	ret := _m.Called()
+
+	var r0 bool
+	if rf, ok := ret.Get(0).(func() bool); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Get(0).(bool)
+	}
+
+	return r0
+}
+
 // IsRunning provides a mock function with given fields:
 func (_m *PeerEnvelopeSender) IsRunning() bool {
 	ret := _m.Called()
diff --git a/p2p/peer.go b/p2p/peer.go
index f43cff9d5..bb04cd36c 100644
--- a/p2p/peer.go
+++ b/p2p/peer.go
@@ -32,6 +32,8 @@ type Peer interface {
 	IsOutbound() bool   // did we dial the peer
 	IsPersistent() bool // do we redial this peer when we disconnect
 
+	HasIPChanged() bool // has the peer's IP changed
+
 	CloseConn() error // close original connection
 
 	NodeInfo() NodeInfo // peer's info
@@ -300,6 +302,18 @@ func (p *peer) IsPersistent() bool {
 	return p.peerConn.persistent
 }
 
+// HasIPChanged returns true and the new IP if the peer's IP has changed.
+func (p *peer) HasIPChanged() bool {
+	oldIP := p.ip
+	if oldIP == nil {
+		return false
+	}
+	// Reset the IP so we can get the new one
+	p.ip = nil
+	newIP := p.RemoteIP()
+	return !oldIP.Equal(newIP)
+}
+
 // NodeInfo returns a copy of the peer's NodeInfo.
 func (p *peer) NodeInfo() NodeInfo {
 	return p.nodeInfo
diff --git a/p2p/peer_set_test.go b/p2p/peer_set_test.go
index 6501dd77a..ca92c65cb 100644
--- a/p2p/peer_set_test.go
+++ b/p2p/peer_set_test.go
@@ -27,6 +27,7 @@ func (mp *mockPeer) NodeInfo() NodeInfo              { return DefaultNodeInfo{}
 func (mp *mockPeer) Status() ConnectionStatus        { return ConnectionStatus{} }
 func (mp *mockPeer) ID() ID                          { return mp.id }
 func (mp *mockPeer) IsOutbound() bool                { return false }
+func (mp *mockPeer) HasIPChanged() bool              { return false }
 func (mp *mockPeer) IsPersistent() bool              { return true }
 func (mp *mockPeer) Get(s string) interface{}        { return s }
 func (mp *mockPeer) Set(string, interface{})         {}
diff --git a/p2p/switch.go b/p2p/switch.go
index af0607e03..8bdaa9617 100644
--- a/p2p/switch.go
+++ b/p2p/switch.go
@@ -381,20 +381,37 @@ func (sw *Switch) StopPeerForError(peer Peer, reason interface{}) {
 	sw.stopAndRemovePeer(peer, reason)
 
 	if peer.IsPersistent() {
-		var addr *NetAddress
-		if peer.IsOutbound() { // socket address for outbound peers
-			addr = peer.SocketAddr()
-		} else { // self-reported address for inbound peers
-			var err error
-			addr, err = peer.NodeInfo().NetAddress()
-			if err != nil {
-				sw.Logger.Error("Wanted to reconnect to inbound peer, but self-reported address is wrong",
-					"peer", peer, "err", err)
-				return
-			}
+		addr, err := sw.getPeerAddress(peer)
+		if err != nil {
+			sw.Logger.Error("Failed to get address for persistent peer", "peer", peer, "err", err)
+			return
 		}
 		go sw.reconnectToPeer(addr)
 	}
+
+	if peer.HasIPChanged() {
+		addr, err := sw.getPeerAddress(peer)
+		if err != nil {
+			sw.Logger.Error("Failed to get address for peer with changed IP", "peer", peer, "err", err)
+		}
+		go sw.reconnectToPeer(addr)
+	}
+}
+
+// getPeerAddress returns the appropriate NetAddress for a given peer,
+// handling both outbound and inbound peers.
+func (sw *Switch) getPeerAddress(peer Peer) (*NetAddress, error) {
+	if peer.IsOutbound() {
+		return peer.SocketAddr(), nil
+	}
+	// For inbound peers, get the self-reported address
+	addr, err := peer.NodeInfo().NetAddress()
+	if err != nil {
+		sw.Logger.Error("Failed to get address for inbound peer",
+			"peer", peer, "err", err)
+		return nil, err
+	}
+	return addr, nil
 }
 
 // StopPeerGracefully disconnects from a peer gracefully.
diff --git a/test/fuzz/p2p/pex/reactor_receive.go b/test/fuzz/p2p/pex/reactor_receive.go
index be9c6bba0..7cf94c71f 100644
--- a/test/fuzz/p2p/pex/reactor_receive.go
+++ b/test/fuzz/p2p/pex/reactor_receive.go
@@ -74,8 +74,10 @@ func (fp *fuzzPeer) RemoteIP() net.IP { return net.IPv4(0, 0, 0, 0) }
 func (fp *fuzzPeer) RemoteAddr() net.Addr {
 	return &net.TCPAddr{IP: fp.RemoteIP(), Port: 98991, Zone: ""}
 }
-func (fp *fuzzPeer) IsOutbound() bool                    { return false }
-func (fp *fuzzPeer) IsPersistent() bool                  { return false }
+func (fp *fuzzPeer) IsOutbound() bool   { return false }
+func (fp *fuzzPeer) IsPersistent() bool { return false }
+func (fp *fuzzPeer) HasIPChanged() bool { return false }
+
 func (fp *fuzzPeer) CloseConn() error                    { return nil }
 func (fp *fuzzPeer) NodeInfo() p2p.NodeInfo              { return defaultNodeInfo }
 func (fp *fuzzPeer) Status() p2p.ConnectionStatus        { var cs p2p.ConnectionStatus; return cs }