diff --git a/substrate/client/network/src/protocol.rs b/substrate/client/network/src/protocol.rs
index 6af5e128549766685d4ed1099af79b2087fa820f..e0e2e63cad0063200b29cc8fff772fd30687082a 100644
--- a/substrate/client/network/src/protocol.rs
+++ b/substrate/client/network/src/protocol.rs
@@ -72,14 +72,23 @@ const TICK_TIMEOUT: time::Duration = time::Duration::from_millis(1100);
 /// Interval at which we propagate transactions;
 const PROPAGATE_TIMEOUT: time::Duration = time::Duration::from_millis(2900);
 
-/// Maximim number of known block hashes to keep for a peer.
+/// Maximum number of known block hashes to keep for a peer.
 const MAX_KNOWN_BLOCKS: usize = 1024; // ~32kb per peer + LruHashSet overhead
-/// Maximim number of known transaction hashes to keep for a peer.
+/// Maximum number of known transaction hashes to keep for a peer.
 ///
 /// This should be approx. 2 blocks full of transactions for the network to function properly.
 const MAX_KNOWN_TRANSACTIONS: usize = 10240; // ~300kb per peer + overhead.
 
-/// Maximim number of transaction validation request we keep at any moment.
+/// Maximum allowed size for a block announce.
+const MAX_BLOCK_ANNOUNCE_SIZE: u64 = 1024 * 1024;
+/// Maximum allowed size for a transactions notification.
+const MAX_TRANSACTIONS_SIZE: u64 = 16 * 1024 * 1024;
+
+/// Maximum size used for notifications in the block announce and transaction protocols.
+// Must be equal to `max(MAX_BLOCK_ANNOUNCE_SIZE, MAX_TRANSACTIONS_SIZE)`.
+pub(crate) const BLOCK_ANNOUNCES_TRANSACTIONS_SUBSTREAM_SIZE: u64 = 16 * 1024 * 1024;
+
+/// Maximum number of transaction validation request we keep at any moment.
 const MAX_PENDING_TRANSACTIONS: usize = 8192;
 
 /// Current protocol version.
@@ -483,8 +492,8 @@ impl<B: BlockT, H: ExHashT> Protocol<B, H> {
 				versions,
 				build_status_message::<B>(&config, best_number, best_hash, genesis_hash),
 				peerset,
-				iter::once((block_announces_protocol, block_announces_handshake, 1024 * 1024))
-					.chain(iter::once((transactions_protocol, vec![], 1024 * 1024)))
+				iter::once((block_announces_protocol, block_announces_handshake, MAX_BLOCK_ANNOUNCE_SIZE))
+					.chain(iter::once((transactions_protocol, vec![], MAX_TRANSACTIONS_SIZE)))
 					.chain(network_config.extra_sets.iter().map(|s| (
 						s.notifications_protocol.clone(),
 						handshake_message.clone(),
diff --git a/substrate/client/network/src/service.rs b/substrate/client/network/src/service.rs
index 09acef62e7784b939e717b1babdef149ea531531..20968c12788938e347b5fc058ab19dcbd2f1b988 100644
--- a/substrate/client/network/src/service.rs
+++ b/substrate/client/network/src/service.rs
@@ -82,6 +82,7 @@ use sp_runtime::traits::{Block as BlockT, NumberFor};
 use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender};
 use std::{
 	borrow::Cow,
+	cmp,
 	collections::{HashMap, HashSet},
 	convert::TryFrom as _,
 	fs,
@@ -310,8 +311,13 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
 						.map(|cfg| usize::try_from(cfg.max_notification_size).unwrap_or(usize::max_value()));
 
 					// A "default" max is added to cover all the other protocols: ping, identify,
-					// kademlia.
-					let default_max = 1024 * 1024;
+					// kademlia, block announces, and transactions.
+					let default_max = cmp::max(
+						1024 * 1024,
+						usize::try_from(protocol::BLOCK_ANNOUNCES_TRANSACTIONS_SUBSTREAM_SIZE)
+							.unwrap_or(usize::max_value())
+					);
+
 					iter::once(default_max)
 						.chain(requests_max).chain(responses_max).chain(notifs_max)
 						.max().expect("iterator known to always yield at least one element; qed")