diff --git a/substrate/client/db/src/lib.rs b/substrate/client/db/src/lib.rs
index 8196a750557a8484ce72fc906b81a9301b1fe808..983459cfebe33ea35738b0d4db27bdb82659e7f0 100644
--- a/substrate/client/db/src/lib.rs
+++ b/substrate/client/db/src/lib.rs
@@ -49,6 +49,9 @@ use std::sync::Arc;
 use std::path::{Path, PathBuf};
 use std::io;
 use std::collections::{HashMap, HashSet};
+use parking_lot::{Mutex, RwLock};
+use linked_hash_map::LinkedHashMap;
+use log::{trace, debug, warn};
 
 use sc_client_api::{
 	UsageInfo, MemoryInfo, IoInfo, MemorySize,
@@ -63,7 +66,6 @@ use codec::{Decode, Encode};
 use hash_db::Prefix;
 use sp_trie::{MemoryDB, PrefixedMemoryDB, prefixed_key};
 use sp_database::Transaction;
-use parking_lot::RwLock;
 use sp_core::ChangesTrieConfiguration;
 use sp_core::offchain::storage::{OffchainOverlayedChange, OffchainOverlayedChanges};
 use sp_core::storage::{well_known_keys, ChildInfo};
@@ -83,7 +85,6 @@ use sc_state_db::StateDb;
 use sp_blockchain::{CachedHeaderMetadata, HeaderMetadata, HeaderMetadataCache};
 use crate::storage_cache::{CachingState, SyncingCachingState, SharedCache, new_shared_cache};
 use crate::stats::StateUsageStats;
-use log::{trace, debug, warn};
 
 // Re-export the Database trait so that one can pass an implementation of it.
 pub use sp_database::Database;
@@ -93,6 +94,7 @@ pub use sc_state_db::PruningMode;
 pub use bench::BenchmarkingState;
 
 const MIN_BLOCKS_TO_KEEP_CHANGES_TRIES_FOR: u32 = 32768;
+const CACHE_HEADERS: usize = 8;
 
 /// Default value for storage cache child ratio.
 const DEFAULT_CHILD_RATIO: (usize, usize) = (1, 10);
@@ -352,12 +354,24 @@ impl<'a> sc_state_db::MetaDb for StateMetaDb<'a> {
 	}
 }
 
+fn cache_header<Hash: std::cmp::Eq + std::hash::Hash, Header>(
+	cache: &mut LinkedHashMap<Hash, Option<Header>>,
+	hash: Hash,
+	header: Option<Header>,
+) {
+	cache.insert(hash, header);
+	while cache.len() > CACHE_HEADERS {
+		cache.pop_front();
+	}
+}
+
 /// Block database
 pub struct BlockchainDb<Block: BlockT> {
 	db: Arc<dyn Database<DbHash>>,
 	meta: Arc<RwLock<Meta<NumberFor<Block>, Block::Hash>>>,
 	leaves: RwLock<LeafSet<Block::Hash, NumberFor<Block>>>,
 	header_metadata_cache: Arc<HeaderMetadataCache<Block>>,
+	header_cache: Mutex<LinkedHashMap<Block::Hash, Option<Block::Header>>>,
 }
 
 impl<Block: BlockT> BlockchainDb<Block> {
@@ -369,6 +383,7 @@ impl<Block: BlockT> BlockchainDb<Block> {
 			leaves: RwLock::new(leaves),
 			meta: Arc::new(RwLock::new(meta)),
 			header_metadata_cache: Arc::new(HeaderMetadataCache::default()),
+			header_cache: Default::default(),
 		})
 	}
 
@@ -407,7 +422,20 @@ impl<Block: BlockT> BlockchainDb<Block> {
 
 impl<Block: BlockT> sc_client_api::blockchain::HeaderBackend<Block> for BlockchainDb<Block> {
 	fn header(&self, id: BlockId<Block>) -> ClientResult<Option<Block::Header>> {
-		utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id)
+		match &id {
+			BlockId::Hash(h) => {
+				let mut cache = self.header_cache.lock();
+				if let Some(result) = cache.get_refresh(h) {
+					return Ok(result.clone());
+				}
+				let header = utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id)?;
+				cache_header(&mut cache, h.clone(), header.clone());
+				Ok(header)
+			}
+			BlockId::Number(_) => {
+				utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id)
+			}
+		}
 	}
 
 	fn info(&self) -> sc_client_api::blockchain::Info<Block> {
@@ -424,12 +452,7 @@ impl<Block: BlockT> sc_client_api::blockchain::HeaderBackend<Block> for Blockcha
 
 	fn status(&self, id: BlockId<Block>) -> ClientResult<sc_client_api::blockchain::BlockStatus> {
 		let exists = match id {
-			BlockId::Hash(_) => read_db(
-				&*self.db,
-				columns::KEY_LOOKUP,
-				columns::HEADER,
-				id
-			)?.is_some(),
+			BlockId::Hash(_) => self.header(id)?.is_some(),
 			BlockId::Number(n) => n <= self.meta.read().best_number,
 		};
 		match exists {
@@ -1117,12 +1140,6 @@ impl<Block: BlockT> Backend<Block> {
 				hash,
 			)?;
 
-			let header_metadata = CachedHeaderMetadata::from(&pending_block.header);
-			self.blockchain.insert_header_metadata(
-				header_metadata.hash,
-				header_metadata,
-			);
-
 			transaction.set_from_vec(columns::HEADER, &lookup_key, pending_block.header.encode());
 			if let Some(body) = &pending_block.body {
 				transaction.set_from_vec(columns::BODY, &lookup_key, body.encode());
@@ -1271,7 +1288,7 @@ impl<Block: BlockT> Backend<Block> {
 
 			meta_updates.push((hash, number, pending_block.leaf_state.is_best(), finalized));
 
-			Some((number, hash, enacted, retracted, displaced_leaf, is_best, cache))
+			Some((pending_block.header, number, hash, enacted, retracted, displaced_leaf, is_best, cache))
 		} else {
 			None
 		};
@@ -1297,7 +1314,11 @@ impl<Block: BlockT> Backend<Block> {
 
 		self.storage.db.commit(transaction)?;
 
+		// Apply all in-memory state shanges.
+		// Code beyond this point can't fail.
+
 		if let Some((
+			header,
 			number,
 			hash,
 			enacted,
@@ -1306,6 +1327,12 @@ impl<Block: BlockT> Backend<Block> {
 			is_best,
 			mut cache,
 		)) = imported {
+			let header_metadata = CachedHeaderMetadata::from(&header);
+			self.blockchain.insert_header_metadata(
+				header_metadata.hash,
+				header_metadata,
+			);
+			cache_header(&mut self.blockchain.header_cache.lock(), hash, Some(header));
 			cache.sync_cache(
 				&enacted,
 				&retracted,
diff --git a/substrate/client/service/src/client/client.rs b/substrate/client/service/src/client/client.rs
index d423fdee39b6cbebe368b222001e79131459c297..e8d748011bc1770d8f28db5bd3f287b970d60259 100644
--- a/substrate/client/service/src/client/client.rs
+++ b/substrate/client/service/src/client/client.rs
@@ -1159,12 +1159,12 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
 
 	/// Prepare in-memory header that is used in execution environment.
 	fn prepare_environment_block(&self, parent: &BlockId<Block>) -> sp_blockchain::Result<Block::Header> {
-		let parent_header = self.backend.blockchain().expect_header(*parent)?;
+		let parent_hash = self.backend.blockchain().expect_block_hash_from_id(parent)?;
 		Ok(<<Block as BlockT>::Header as HeaderT>::new(
 			self.backend.blockchain().expect_block_number_from_id(parent)? + One::one(),
 			Default::default(),
 			Default::default(),
-			parent_header.hash(),
+			parent_hash,
 			Default::default(),
 		))
 	}
diff --git a/substrate/primitives/blockchain/src/backend.rs b/substrate/primitives/blockchain/src/backend.rs
index 1328dfb5752fc0544e33c6b9934965dd3158dded..326acd6b9bd43f69f1329b8df005ecd1f73bedc1 100644
--- a/substrate/primitives/blockchain/src/backend.rs
+++ b/substrate/primitives/blockchain/src/backend.rs
@@ -53,7 +53,7 @@ pub trait HeaderBackend<Block: BlockT>: Send + Sync {
 	/// Convert an arbitrary block ID into a block hash.
 	fn block_number_from_id(&self, id: &BlockId<Block>) -> Result<Option<NumberFor<Block>>> {
 		match *id {
-			BlockId::Hash(_) => Ok(self.header(*id)?.map(|h| h.number().clone())),
+			BlockId::Hash(h) => self.number(h),
 			BlockId::Number(n) => Ok(Some(n)),
 		}
 	}