diff --git a/api/request_source/BUILD b/api/request_source/BUILD
index 9cf50e7ff..2473d6a2e 100644
--- a/api/request_source/BUILD
+++ b/api/request_source/BUILD
@@ -13,6 +13,7 @@ api_cc_py_proto_library(
         "@envoy_api//envoy/api/v2/auth:pkg",
         "@envoy_api//envoy/api/v2/cluster:pkg",
         "@envoy_api//envoy/api/v2/core:pkg",
+        "@envoy_api//envoy/config/core/v3:pkg",
     ],
 )
 
diff --git a/api/request_source/service.proto b/api/request_source/service.proto
index 9ab934558..e1acdb135 100644
--- a/api/request_source/service.proto
+++ b/api/request_source/service.proto
@@ -2,8 +2,9 @@ syntax = "proto3";
 
 package nighthawk.request_source;
 
-import "google/protobuf/wrappers.proto";
 import "envoy/api/v2/core/base.proto";
+import "envoy/config/core/v3/base.proto";
+import "google/protobuf/wrappers.proto";
 
 // Used to request a RequestStreamResponse.
 message RequestStreamRequest {
@@ -37,9 +38,18 @@ message RequestSpecifier {
   // Request content length. The client will transfer the number of bytes specified here for the
   // request body.
   google.protobuf.UInt32Value content_length = 4;
-  // Request header replacements. Any existing header(s) with the same name will be removed
-  // before setting.
-  envoy.api.v2.core.HeaderMap headers = 5;
+
+  oneof oneof_headers {
+    // Request header replacements. Any existing header(s) with the same name will be removed
+    // before setting.
+    //
+    // Envoy deprecated its v2 API, prefer to use v3_headers instead.
+    envoy.api.v2.core.HeaderMap headers = 5 [deprecated = true];
+
+    // Request header replacements. Any existing header(s) with the same name will be removed
+    // before setting.
+    envoy.config.core.v3.HeaderMap v3_headers = 6;
+  }
   // TODO(oschaaf): nice to have
   // google.protobuf.StringValue sni_hostname = 10;
 }
diff --git a/source/client/BUILD b/source/client/BUILD
index b89c90bd7..4fe97d170 100644
--- a/source/client/BUILD
+++ b/source/client/BUILD
@@ -156,6 +156,7 @@ envoy_cc_library(
         "//api/client:grpc_service_lib",
         "//api/request_source:grpc_request_source_service_lib",
         "@envoy//source/common/common:thread_lib_with_external_headers",
+        "@envoy_api//envoy/config/core/v3:pkg_cc_proto",
     ],
 )
 
diff --git a/source/client/service_impl.cc b/source/client/service_impl.cc
index 693955a79..fda3d69d7 100644
--- a/source/client/service_impl.cc
+++ b/source/client/service_impl.cc
@@ -2,6 +2,8 @@
 
 #include <grpc++/grpc++.h>
 
+#include "envoy/config/core/v3/base.pb.h"
+
 #include "common/request_source_impl.h"
 
 #include "client/client.h"
@@ -106,7 +108,7 @@ ::grpc::Status ServiceImpl::ExecutionStream(
 }
 
 namespace {
-void addHeader(envoy::api::v2::core::HeaderMap* map, absl::string_view key,
+void addHeader(envoy::config::core::v3::HeaderMap* map, absl::string_view key,
                absl::string_view value) {
   auto* request_header = map->add_headers();
   request_header->set_key(std::string(key));
@@ -144,7 +146,7 @@ ::grpc::Status RequestSourceServiceImpl::RequestStream(
       HeaderMapPtr headers = request->header();
       nighthawk::request_source::RequestStreamResponse response;
       auto* request_specifier = response.mutable_request_specifier();
-      auto* request_headers = request_specifier->mutable_headers();
+      auto* request_headers = request_specifier->mutable_v3_headers();
       headers->iterate([&request_headers](const Envoy::Http::HeaderEntry& header)
                            -> Envoy::Http::HeaderMap::Iterate {
         addHeader(request_headers, header.key().getStringView(), header.value().getStringView());
@@ -163,4 +165,4 @@ ::grpc::Status RequestSourceServiceImpl::RequestStream(
 }
 
 } // namespace Client
-} // namespace Nighthawk
\ No newline at end of file
+} // namespace Nighthawk
diff --git a/source/common/BUILD b/source/common/BUILD
index fd8cc3701..951c2a411 100644
--- a/source/common/BUILD
+++ b/source/common/BUILD
@@ -60,6 +60,8 @@ envoy_cc_library(
         "@envoy//source/common/grpc:typed_async_client_lib_with_external_headers",
         "@envoy//source/common/http:header_map_lib_with_external_headers",
         "@envoy//source/common/http:headers_lib_with_external_headers",
+        "@envoy_api//envoy/api/v2/core:pkg_cc_proto",
+        "@envoy_api//envoy/config/core/v3:pkg_cc_proto",
     ],
 )
 
diff --git a/source/common/request_stream_grpc_client_impl.cc b/source/common/request_stream_grpc_client_impl.cc
index da0095042..31db65a69 100644
--- a/source/common/request_stream_grpc_client_impl.cc
+++ b/source/common/request_stream_grpc_client_impl.cc
@@ -2,16 +2,22 @@
 
 #include <string>
 
+#include "envoy/api/v2/core/base.pb.h"
+#include "envoy/config/core/v3/base.pb.h"
 #include "envoy/stats/scope.h"
 
 #include "external/envoy/source/common/common/assert.h"
 #include "external/envoy/source/common/http/header_map_impl.h"
 #include "external/envoy/source/common/http/headers.h"
 
+#include "api/request_source/service.pb.h"
+
 #include "common/request_impl.h"
 
 namespace Nighthawk {
 
+using ::nighthawk::request_source::RequestSpecifier;
+
 const std::string RequestStreamGrpcClientImpl::METHOD_NAME =
     "nighthawk.request_source.NighthawkRequestSourceService.RequestStream";
 
@@ -75,15 +81,27 @@ RequestPtr ProtoRequestHelper::messageToRequest(
   RequestPtr request = std::make_unique<RequestImpl>(header);
 
   if (message.has_request_specifier()) {
-    const auto& request_specifier = message.request_specifier();
-    if (request_specifier.has_headers()) {
-      const auto& message_request_headers = request_specifier.headers();
-      for (const auto& message_header : message_request_headers.headers()) {
+    const RequestSpecifier& request_specifier = message.request_specifier();
+
+    if (request_specifier.has_v3_headers()) {
+      const envoy::config::core::v3::HeaderMap& message_request_headers =
+          request_specifier.v3_headers();
+      for (const envoy::config::core::v3::HeaderValue& message_header :
+           message_request_headers.headers()) {
+        Envoy::Http::LowerCaseString header_name(message_header.key());
+        header->remove(header_name);
+        header->addCopy(header_name, message_header.value());
+      }
+    } else if (request_specifier.has_headers()) {
+      const envoy::api::v2::core::HeaderMap& message_request_headers = request_specifier.headers();
+      for (const envoy::api::v2::core::HeaderValue& message_header :
+           message_request_headers.headers()) {
         Envoy::Http::LowerCaseString header_name(message_header.key());
         header->remove(header_name);
         header->addCopy(header_name, message_header.value());
       }
     }
+
     if (request_specifier.has_content_length()) {
       std::string s_content_length = absl::StrCat("", request_specifier.content_length().value());
       header->remove(Envoy::Http::Headers::get().ContentLength);
diff --git a/test/BUILD b/test/BUILD
index a56d270ef..c3ee1093a 100644
--- a/test/BUILD
+++ b/test/BUILD
@@ -326,8 +326,11 @@ envoy_cc_test(
     srcs = ["request_stream_grpc_client_test.cc"],
     repository = "@envoy",
     deps = [
+        "//api/request_source:grpc_request_source_service_lib",
         "//source/common:request_stream_grpc_client_lib",
         "@envoy//test/test_common:utility_lib",
+        "@envoy_api//envoy/api/v2/core:pkg_cc_proto",
+        "@envoy_api//envoy/config/core/v3:pkg_cc_proto",
     ],
 )
 
diff --git a/test/request_stream_grpc_client_test.cc b/test/request_stream_grpc_client_test.cc
index 91661ea52..b78b32f32 100644
--- a/test/request_stream_grpc_client_test.cc
+++ b/test/request_stream_grpc_client_test.cc
@@ -1,5 +1,10 @@
+#include "envoy/api/v2/core/base.pb.h"
+#include "envoy/config/core/v3/base.pb.h"
+
 #include "external/envoy/test/test_common/utility.h"
 
+#include "api/request_source/service.pb.h"
+
 #include "common/request_impl.h"
 #include "common/request_stream_grpc_client_impl.h"
 
@@ -8,6 +13,9 @@
 using namespace testing;
 
 namespace Nighthawk {
+namespace {
+
+using ::nighthawk::request_source::RequestSpecifier;
 
 // The grpc client itself is tested via the python based integration tests.
 // It is convenient to test message translation here.
@@ -18,7 +26,9 @@ class ProtoRequestHelperTest : public Test {
     // We test for equality. If we observe mismatch, we use EXPECT_EQ which is guaranteed
     // to fail -- but will provide much more helpful output.
     if (!Envoy::TestUtility::headerMapEqualIgnoreOrder(expected_header_, *request->header())) {
-      EXPECT_EQ(expected_header_, *request->header());
+      EXPECT_EQ(expected_header_, *request->header()) << "expected headers:\n"
+                                                      << expected_header_ << "\nactual headers:\n"
+                                                      << *request->header() << "\n";
     };
   }
 
@@ -42,14 +52,34 @@ TEST_F(ProtoRequestHelperTest, ExplicitFields) {
   translateExpectingEqual();
 }
 
-// Test the generic header api we offer in the proto api.
-TEST_F(ProtoRequestHelperTest, GenericHeaderFields) {
-  auto* request_specifier = response_.mutable_request_specifier();
-  auto* headers = request_specifier->mutable_headers();
-  auto* header_1 = headers->add_headers();
+// Test the generic header api we offer in the proto api using Envoy API v2
+// primitives.
+TEST_F(ProtoRequestHelperTest, GenericHeaderFieldsUsingDeprecatedEnvoyV2Api) {
+  RequestSpecifier* request_specifier = response_.mutable_request_specifier();
+  envoy::api::v2::core::HeaderMap* headers = request_specifier->mutable_headers();
+  envoy::api::v2::core::HeaderValue* header_1 = headers->add_headers();
+  header_1->set_key("header1");
+  header_1->set_value("value1");
+  envoy::api::v2::core::HeaderValue* header_2 = headers->add_headers();
+  header_2->set_key("header2");
+  header_2->set_value("value2");
+  // We re-add the same header, but do not expect that to show up in the translation because we
+  // always replace.
+  headers->add_headers()->MergeFrom(*header_2);
+  expected_header_ =
+      Envoy::Http::TestRequestHeaderMapImpl{{"header1", "value1"}, {"header2", "value2"}};
+  translateExpectingEqual();
+}
+
+// Test the generic header api we offer in the proto api using Envoy API v3
+// primitives.
+TEST_F(ProtoRequestHelperTest, GenericHeaderFieldsUsingEnvoyV3Api) {
+  RequestSpecifier* request_specifier = response_.mutable_request_specifier();
+  envoy::config::core::v3::HeaderMap* headers = request_specifier->mutable_v3_headers();
+  envoy::config::core::v3::HeaderValue* header_1 = headers->add_headers();
   header_1->set_key("header1");
   header_1->set_value("value1");
-  auto* header_2 = headers->add_headers();
+  envoy::config::core::v3::HeaderValue* header_2 = headers->add_headers();
   header_2->set_key("header2");
   header_2->set_value("value2");
   // We re-add the same header, but do not expect that to show up in the translation because we
@@ -74,4 +104,5 @@ TEST_F(ProtoRequestHelperTest, AmbiguousHost) {
   translateExpectingEqual();
 }
 
+} // namespace
 } // namespace Nighthawk