diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb
index c75f314e51895ea1506875c683b2bfc6ec1651c4..8cc8c39ac74065a7d87d820ca05a3285f6a816ad 100644
--- a/config/initializers/session_store.rb
+++ b/config/initializers/session_store.rb
@@ -36,7 +36,10 @@
 Rails.application.configure do
   config.session_store(
     Gitlab::Sessions::RedisStore, # Using the cookie_store would enable session replay attacks
-    redis_server: Gitlab::Redis::Sessions.params.merge(namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE),
+    redis_server: Gitlab::Redis::Sessions.params.merge(
+      namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE,
+      serializer: Gitlab::Sessions::RedisStoreSerializer
+    ),
     key: cookie_key,
     secure: Gitlab.config.gitlab.https,
     httponly: true,
diff --git a/lib/gitlab/sessions/redis_store_serializer.rb b/lib/gitlab/sessions/redis_store_serializer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8d0cba26d9248e70175957067694a7cb09c23fbb
--- /dev/null
+++ b/lib/gitlab/sessions/redis_store_serializer.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Gitlab
+  module Sessions
+    module RedisStoreSerializer
+      def self.load(val)
+        deserialized = Marshal.load(val) # rubocop:disable Security/MarshalLoad -- We're loading session data similar to Redis::Store::Serialization#get from redis-store gem
+
+        return deserialized if deserialized.is_a?(Hash)
+
+        # Session data can be an instance of ActiveSupport::Cache::Entry
+        # when we're using session store based on ActionDispatch::Session::CacheStore
+        # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/176108
+        deserialized&.value
+      rescue StandardError => e
+        Gitlab::ErrorTracking.track_and_raise_exception(e)
+      end
+
+      def self.dump(val)
+        Marshal.dump(val)
+      end
+    end
+  end
+end
diff --git a/spec/initializers/session_store_spec.rb b/spec/initializers/session_store_spec.rb
index 8b8ac0eea71ec83dfeef99d94fd4c039b3b97500..917f7c535f55a109572ae4b293071a392cdd34fd 100644
--- a/spec/initializers/session_store_spec.rb
+++ b/spec/initializers/session_store_spec.rb
@@ -18,7 +18,10 @@
       expect(subject).to receive(:session_store).with(
         Gitlab::Sessions::RedisStore,
         a_hash_including(
-          redis_server: Gitlab::Redis::Sessions.params.merge(namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE)
+          redis_server: Gitlab::Redis::Sessions.params.merge(
+            namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE,
+            serializer: Gitlab::Sessions::RedisStoreSerializer
+          )
         )
       )
 
diff --git a/spec/lib/gitlab/sessions/redis_store_serializer_spec.rb b/spec/lib/gitlab/sessions/redis_store_serializer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..430fe069e0f11a0a6050c6d6ba1ebf326201504d
--- /dev/null
+++ b/spec/lib/gitlab/sessions/redis_store_serializer_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Sessions::RedisStoreSerializer, feature_category: :system_access do
+  let(:hash) { { a: 1, b: 2 } }
+  let(:serialized) { Marshal.dump(val) }
+
+  describe '.load' do
+    shared_examples 'unmarshal' do
+      it 'returns original value' do
+        expect(load).to eq(expected)
+      end
+    end
+
+    subject(:load) { described_class.load(serialized) }
+
+    context 'with hash value' do
+      let(:val) { hash }
+      let(:expected) { hash }
+
+      it_behaves_like 'unmarshal'
+    end
+
+    context 'with ActiveSupport::Cache::Entry value' do
+      let(:val) { ActiveSupport::Cache::Entry.new(hash) }
+      let(:expected) { hash }
+
+      it_behaves_like 'unmarshal'
+    end
+
+    context 'with nil value' do
+      let(:val) { nil }
+      let(:expected) { nil }
+
+      it_behaves_like 'unmarshal'
+    end
+
+    context 'with unrecognized type' do
+      let(:val) { %w[a b c] }
+
+      it 'tracks and raises an exception' do
+        expect(Gitlab::ErrorTracking).to receive(:track_and_raise_exception).with(instance_of(NoMethodError))
+
+        load
+      end
+    end
+  end
+
+  describe '.dump' do
+    subject(:dump) { described_class.dump(hash) }
+
+    it 'calls Marshal.dump' do
+      expect(Marshal).to receive(:dump).with(hash)
+
+      dump
+    end
+
+    it 'returns marshalled object' do
+      expect(dump).to eq(Marshal.dump(hash))
+    end
+  end
+end