diff --git a/api/src/main/java/org/eclipse/microprofile/rest/client/RestClientBuilder.java b/api/src/main/java/org/eclipse/microprofile/rest/client/RestClientBuilder.java
index 7279c06..a28590f 100644
--- a/api/src/main/java/org/eclipse/microprofile/rest/client/RestClientBuilder.java
+++ b/api/src/main/java/org/eclipse/microprofile/rest/client/RestClientBuilder.java
@@ -269,6 +269,20 @@ default RestClientBuilder baseUri(String uri) {
      */
     RestClientBuilder queryParamStyle(QueryParamStyle style);
 
+    /**
+     * Add an arbitrary header.
+     *
+     * @param name
+     *            - the name of the header
+     * @param name
+     *            - the value of the HTTP header to add to the request.
+     * @return the current builder with the header added to the request.
+     * @throws NullPointerException
+     *             if the value is null.
+     * @since 4.0
+     */
+    RestClientBuilder header(String name, Object value);
+
     /**
      * Based on the configured RestClientBuilder, creates a new instance of the given REST interface to invoke API calls
      * against.
diff --git a/api/src/test/java/org/eclipse/microprofile/rest/client/BuilderImpl1.java b/api/src/test/java/org/eclipse/microprofile/rest/client/BuilderImpl1.java
index 1a38d65..daac7ad 100644
--- a/api/src/test/java/org/eclipse/microprofile/rest/client/BuilderImpl1.java
+++ b/api/src/test/java/org/eclipse/microprofile/rest/client/BuilderImpl1.java
@@ -90,6 +90,11 @@ public RestClientBuilder queryParamStyle(QueryParamStyle style) {
         throw new IllegalStateException("not implemented");
     }
 
+    @Override
+    public RestClientBuilder header(String name, Object value) {
+        throw new IllegalStateException("not implemented");
+    }
+
     @Override
     public <T> T build(Class<T> clazz) {
         throw new IllegalStateException("not implemented");
diff --git a/api/src/test/java/org/eclipse/microprofile/rest/client/BuilderImpl2.java b/api/src/test/java/org/eclipse/microprofile/rest/client/BuilderImpl2.java
index 64051ba..83f9322 100644
--- a/api/src/test/java/org/eclipse/microprofile/rest/client/BuilderImpl2.java
+++ b/api/src/test/java/org/eclipse/microprofile/rest/client/BuilderImpl2.java
@@ -90,6 +90,11 @@ public RestClientBuilder queryParamStyle(QueryParamStyle style) {
         throw new IllegalStateException("not implemented");
     }
 
+    @Override
+    public RestClientBuilder header(String name, Object value) {
+        throw new IllegalStateException("not implemented");
+    }
+
     @Override
     public <T> T build(Class<T> clazz) {
         throw new IllegalStateException("not implemented");
diff --git a/spec/src/main/asciidoc/clientexamples.asciidoc b/spec/src/main/asciidoc/clientexamples.asciidoc
index 2b88a5c..2e100a1 100644
--- a/spec/src/main/asciidoc/clientexamples.asciidoc
+++ b/spec/src/main/asciidoc/clientexamples.asciidoc
@@ -149,6 +149,19 @@ implementation must invoke the `DefaultClientHeadersFactoryImpl`. This default f
 
 `org.eclipse.microprofile.rest.client.propagateHeaders`
 
+You can also configure headers on a per instance basis using the `RestClientBuilder.header(String name, Object value)` method. Headers added via this method will be merged with the headers added via `@ClientHeaderParam` annotations, `@HeaderParam` annotations, and `ClientHeadersFactory` implementations.
+**Note: The method will throw a `NullPointerException` if the value is `null`.**
+
+Example:
+
+[source, java]
+----
+RedirectClient client = RestClientBuilder.newBuilder()
+                                         .baseUri(someUri)
+                                         .header("Some-Header", headerValueObj)
+                                         .build(SomeClient.class);
+----
+
 === Following Redirect Responses
 
 By default, a Rest Client instance will not automatically follow redirect responses. Redirect responses are typically responses with status codes in the 300 range and include `Location` header that indicates the URL of the redirected resource.
diff --git a/tck/src/main/java/org/eclipse/microprofile/rest/client/tck/ClientBuilderHeaderTest.java b/tck/src/main/java/org/eclipse/microprofile/rest/client/tck/ClientBuilderHeaderTest.java
new file mode 100644
index 0000000..ed94902
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/rest/client/tck/ClientBuilderHeaderTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2024 Contributors to the Eclipse Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.eclipse.microprofile.rest.client.tck;
+
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.eclipse.microprofile.rest.client.RestClientBuilder;
+import org.eclipse.microprofile.rest.client.tck.interfaces.ClientBuilderHeaderClient;
+import org.eclipse.microprofile.rest.client.tck.interfaces.ClientBuilderHeaderMethodClient;
+import org.eclipse.microprofile.rest.client.tck.providers.ReturnWithAllDuplicateClientHeadersFilter;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.testng.Arquillian;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonString;
+import jakarta.json.JsonValue;
+
+public class ClientBuilderHeaderTest extends Arquillian {
+    @Deployment
+    public static Archive<?> createDeployment() {
+        return ShrinkWrap.create(WebArchive.class, ClientBuilderHeaderTest.class.getSimpleName() + ".war")
+                .addClasses(
+                        ClientBuilderHeaderMethodClient.class,
+                        ReturnWithAllDuplicateClientHeadersFilter.class);
+    }
+
+    @Test
+    public void testHeaderBuilderMethod() {
+
+        RestClientBuilder builder = RestClientBuilder.newBuilder().baseUri("http://localhost:8080/");
+        builder.register(ReturnWithAllDuplicateClientHeadersFilter.class);
+        builder.header("InterfaceAndBuilderHeader", "builder");
+        ClientBuilderHeaderMethodClient client = builder.build(ClientBuilderHeaderMethodClient.class);
+
+        checkHeaders(client.getAllHeaders("headerparam"), "method");
+    }
+
+    @Test
+    public void testHeaderBuilderInterface() {
+
+        RestClientBuilder builder = RestClientBuilder.newBuilder().baseUri("http://localhost:8080/");
+        builder.register(ReturnWithAllDuplicateClientHeadersFilter.class);
+        builder.header("InterfaceAndBuilderHeader", "builder");
+        ClientBuilderHeaderClient client = builder.build(ClientBuilderHeaderClient.class);
+
+        checkHeaders(client.getAllHeaders("headerparam"), "interface");
+    }
+
+    @Test
+    public void testHeaderBuilderMethodNullValue() {
+
+        RestClientBuilder builder = RestClientBuilder.newBuilder().baseUri("http://localhost:8080/");
+        try {
+            builder.header("BuilderHeader", null);
+        } catch (NullPointerException npe) {
+            return;
+        }
+        fail("header(\"builderHeader\", null) should have thrown a NullPointerException");
+    }
+
+    private static void checkHeaders(final JsonObject headers, final String clientHeaderParamName) {
+        final List<String> clientRequestHeaders = headerValues(headers, "InterfaceAndBuilderHeader");
+
+        assertTrue(clientRequestHeaders.contains("builder"),
+                "Header InterfaceAndBuilderHeader did not container \"builder\": " + clientRequestHeaders);
+        assertTrue(clientRequestHeaders.contains(clientHeaderParamName),
+                "Header InterfaceAndBuilderHeader did not container \"" + clientHeaderParamName + "\": "
+                        + clientRequestHeaders);
+
+        final List<String> headerParamHeaders = headerValues(headers, "HeaderParam");
+        assertTrue(headerParamHeaders.contains("headerparam"),
+                "Header HeaderParam did not container \"headerparam\": " + headerParamHeaders);
+    }
+
+    private static List<String> headerValues(final JsonObject headers, final String headerName) {
+        final JsonArray headerValues = headers.getJsonArray(headerName);
+        Assert.assertNotNull(headerValues,
+                String.format("Expected header '%s' to be present in %s", headerName, headers));
+        return headerValues.stream().map(
+                v -> (v.getValueType() == JsonValue.ValueType.STRING ? ((JsonString) v).getString() : v.toString()))
+                .collect(Collectors.toList());
+    }
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/rest/client/tck/interfaces/ClientBuilderHeaderClient.java b/tck/src/main/java/org/eclipse/microprofile/rest/client/tck/interfaces/ClientBuilderHeaderClient.java
new file mode 100644
index 0000000..da92804
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/rest/client/tck/interfaces/ClientBuilderHeaderClient.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2024 Contributors to the Eclipse Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.eclipse.microprofile.rest.client.tck.interfaces;
+
+import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;
+
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.HeaderParam;
+import jakarta.ws.rs.Path;
+
+@ClientHeaderParam(name = "InterfaceAndBuilderHeader", value = "interface")
+@Path("/")
+public interface ClientBuilderHeaderClient {
+
+    @GET
+    JsonObject getAllHeaders(@HeaderParam("HeaderParam") String param);
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/rest/client/tck/interfaces/ClientBuilderHeaderMethodClient.java b/tck/src/main/java/org/eclipse/microprofile/rest/client/tck/interfaces/ClientBuilderHeaderMethodClient.java
new file mode 100644
index 0000000..00795f5
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/rest/client/tck/interfaces/ClientBuilderHeaderMethodClient.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2024 Contributors to the Eclipse Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.eclipse.microprofile.rest.client.tck.interfaces;
+
+import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;
+
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.HeaderParam;
+import jakarta.ws.rs.Path;
+
+@Path("/")
+public interface ClientBuilderHeaderMethodClient {
+
+    @GET
+    @ClientHeaderParam(name = "InterfaceAndBuilderHeader", value = "method")
+    JsonObject getAllHeaders(@HeaderParam("HeaderParam") String param);
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/rest/client/tck/providers/ReturnWithAllDuplicateClientHeadersFilter.java b/tck/src/main/java/org/eclipse/microprofile/rest/client/tck/providers/ReturnWithAllDuplicateClientHeadersFilter.java
new file mode 100644
index 0000000..94d5a83
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/rest/client/tck/providers/ReturnWithAllDuplicateClientHeadersFilter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018, 2021 Contributors to the Eclipse Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.eclipse.microprofile.rest.client.tck.providers;
+
+import java.io.IOException;
+import java.util.List;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.Response;
+
+public class ReturnWithAllDuplicateClientHeadersFilter implements ClientRequestFilter {
+
+    @Override
+    public void filter(ClientRequestContext clientRequestContext) throws IOException {
+        JsonObjectBuilder allClientHeaders = Json.createObjectBuilder();
+        MultivaluedMap<String, Object> clientHeaders = clientRequestContext.getHeaders();
+        for (String headerName : clientHeaders.keySet()) {
+            List<Object> header = clientHeaders.get(headerName);
+            final JsonArrayBuilder headerValues = Json.createArrayBuilder();
+            header.forEach(h -> headerValues.add(h.toString()));
+            allClientHeaders.add(headerName, headerValues);
+        }
+        clientRequestContext.abortWith(Response.ok(allClientHeaders.build()).build());
+    }
+
+}