diff --git a/Gemfile b/Gemfile
index aaa095dfcd71cca5f0923f768237b0fbf9b161c5..9d4eef8ae43ed445d5bc6d2ec1ea71da5b28cf4e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -149,6 +149,7 @@ gem 'aws-sdk-core', '~> 3'
 gem 'aws-sdk-cloudformation', '~> 1'
 gem 'aws-sdk-s3', '~> 1'
 gem 'faraday_middleware-aws-sigv4', '~>0.3.0'
+gem 'typhoeus', '~> 1.4.0' # Used with Elasticsearch to support http keep-alive connections
 
 # Markdown and HTML processing
 gem 'html-pipeline', '~> 2.13.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index 45c916bbf767f3d1ff0e43349057d0956e34526d..8b811cafe68cfe80c9ed52bef01b2a18ea710b2b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -333,6 +333,8 @@ GEM
     escape_utils (1.2.1)
     et-orbi (1.2.1)
       tzinfo
+    ethon (0.15.0)
+      ffi (>= 1.15.0)
     eventmachine (1.2.7)
     excon (0.71.1)
     execjs (2.8.1)
@@ -1311,6 +1313,8 @@ GEM
       tty-screen (~> 0.8)
       wisper (~> 2.0)
     tty-screen (0.8.1)
+    typhoeus (1.4.0)
+      ethon (>= 0.9.0)
     tzinfo (2.0.4)
       concurrent-ruby (~> 1.0)
     u2f (0.2.1)
@@ -1655,6 +1659,7 @@ DEPENDENCIES
   timecop (~> 0.9.1)
   toml-rb (~> 2.0)
   truncato (~> 0.7.11)
+  typhoeus (~> 1.4.0)
   u2f (~> 0.2.1)
   undercover (~> 0.4.4)
   unf (~> 0.1.4)
diff --git a/config/feature_flags/development/use_typhoeus_elasticsearch_adapter.yml b/config/feature_flags/development/use_typhoeus_elasticsearch_adapter.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ac762395cd2fc91c232038dd0cba82760019b48d
--- /dev/null
+++ b/config/feature_flags/development/use_typhoeus_elasticsearch_adapter.yml
@@ -0,0 +1,8 @@
+---
+name: use_typhoeus_elasticsearch_adapter
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76879
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/348607
+milestone: '14.7'
+type: development
+group: group::global search
+default_enabled: false
diff --git a/ee/lib/gem_extensions/elasticsearch/model/client.rb b/ee/lib/gem_extensions/elasticsearch/model/client.rb
index cdf0fa4d63a37cd9e62e74ce071d7abe94db1c13..d9854e24e4bf7cb415db27dcad77aff448a2d341 100644
--- a/ee/lib/gem_extensions/elasticsearch/model/client.rb
+++ b/ee/lib/gem_extensions/elasticsearch/model/client.rb
@@ -15,16 +15,19 @@ module Client
 
         cattr_accessor :cached_client
         cattr_accessor :cached_config
+        cattr_accessor :cached_adapter
 
         def client(_client = nil)
           store = ::GemExtensions::Elasticsearch::Model::Client
 
           store::CLIENT_MUTEX.synchronize do
-            config = Gitlab::CurrentSettings.elasticsearch_config
+            config = ::Gitlab::CurrentSettings.elasticsearch_config
+            adapter = ::Gitlab::Elastic::Client.adapter
 
-            if store.cached_client.nil? || config != store.cached_config
+            if store.cached_client.nil? || config != store.cached_config || adapter != store.cached_adapter
               store.cached_client = ::Gitlab::Elastic::Client.build(config)
               store.cached_config = config
+              store.cached_adapter = adapter
             end
           end
 
diff --git a/ee/lib/gitlab/elastic/client.rb b/ee/lib/gitlab/elastic/client.rb
index 00175bcd219e25136486f87d1a904adb8c5702bb..d2292882095e9218da0f4f4d358039693304cb74 100644
--- a/ee/lib/gitlab/elastic/client.rb
+++ b/ee/lib/gitlab/elastic/client.rb
@@ -14,6 +14,7 @@ module Client
       # and configures itself based on those parameters
       def self.build(config)
         base_config = {
+          adapter: self.adapter,
           urls: config[:url],
           transport_options: {
             request: {
@@ -37,6 +38,10 @@ def self.build(config)
         end
       end
 
+      def self.adapter
+        ::Feature.enabled?(:use_typhoeus_elasticsearch_adapter) ? :typhoeus : :net_http
+      end
+
       def self.resolve_aws_credentials(config)
         # Resolve credentials in order
         # 1.  Static config
diff --git a/ee/spec/lib/gitlab/elastic/client_spec.rb b/ee/spec/lib/gitlab/elastic/client_spec.rb
index f5a0d6006591d2b43af8826b2aa5b675dab517e8..1472efc8625d151836112c32fcf48f751e163b2c 100644
--- a/ee/spec/lib/gitlab/elastic/client_spec.rb
+++ b/ee/spec/lib/gitlab/elastic/client_spec.rb
@@ -23,6 +23,37 @@
         expect(options).to include(open_timeout: described_class::OPEN_TIMEOUT, timeout: nil)
       end
 
+      context 'with typhoeus adapter for keep-alive connections' do
+        it 'sets typhoeus as the adapter' do
+          options = client.transport.options
+
+          expect(options).to include(adapter: :typhoeus)
+        end
+
+        context 'when use_typhoeus_elasticsearch_adapter FeatureFlag is disabled' do
+          before do
+            stub_feature_flags(use_typhoeus_elasticsearch_adapter: false)
+          end
+
+          it 'uses the net/http adapter' do
+            options = client.transport.options
+            expect(options).to include(adapter: :net_http)
+          end
+        end
+
+        context 'cached client when FeatureFlag changes' do
+          it 'successfully changes adapter from net/http to typhoeus' do
+            stub_feature_flags(use_typhoeus_elasticsearch_adapter: false)
+            adapter = Issue.__elasticsearch__.client.transport.connections.first.connection.builder.adapter
+            expect(adapter).to eq(::Faraday::Adapter::NetHttp)
+
+            stub_feature_flags(use_typhoeus_elasticsearch_adapter: true)
+            adapter = Issue.__elasticsearch__.client.transport.connections.first.connection.builder.adapter
+            expect(adapter).to eq(::Faraday::Adapter::Typhoeus)
+          end
+        end
+      end
+
       context 'with client_request_timeout in config' do
         let(:params) { { url: 'http://dummy-elastic:9200', client_request_timeout: 30 } }