C# – How to add request headers when using HttpClient

There are two ways add request headers when using HttpClient:

  • Add headers for all requests using HttpClient.DefaultRequestHeaders.
  • Add headers per request using HttpRequestMessage.Headers.

In this article, I’ll show examples of both ways to add request headers.

Add an unchanging header for all requests

Let’s say you’re adding an API Key header. It needs to be included in all requests and the value won’t change.

To add this request header, you can use HttpClient.DefaultRequestHeaders when you’re initializing the HttpClient instance, like this:

public class RandomNumberService
{
	private readonly HttpClient HttpClient;
	private const string key = "123";

	public RandomNumberService()
	{
		HttpClient = new HttpClient();
		HttpClient.DefaultRequestHeaders.Add("ApiKey", key);
	}
	public async Task<string> GetRandomNumber()
	{
		var response = await HttpClient.GetAsync(GetRandomNumberUrl);
		response.EnsureSuccessStatusCode();
		return await response.Content.ReadAsStringAsync();
	}

}
Code language: C# (cs)

Here’s what the request looks like in Fiddler:

GET https://localhost:12345/RandomNumber HTTP/1.1
Host: localhost:12345
ApiKey: 123
Code language: plaintext (plaintext)

It includes the ApiKey header in all requests. This only had to be configured once.

Add a header per request

To add a header per request, use HttpRequestMessage.Headers + HttpClient.SendAsync(), like this:

public class RandomNumberService
{
	private readonly HttpClient HttpClient;
	private const string randomNumberUrl = "https://localhost:12345/RandomNumber";
	public RandomNumberService()
	{
		HttpClient = new HttpClient();
	}
	public async Task<string> GetRandomNumber(string Token)
	{
		using (var request = new HttpRequestMessage(HttpMethod.Get, randomNumberUrl))
		{
			request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Token);
			var response = await HttpClient.SendAsync(request);

			response.EnsureSuccessStatusCode();

			return await response.Content.ReadAsStringAsync();
		}
	}
}
Code language: C# (cs)

First, it’s best practice to use a single HttpClient instance for multiple requests. Since you’re using a single instance, don’t use HttpClient.DefaultRequestHeaders for headers that need to be applied per request. It’s not thread-safe. This is why you have to use HttpRequestMessage.Headers instead.

Second, you have to use HttpClient.SendAsync() to send the request because there are no overloads of GetAsync() / PostAsync() that take an HttpRequestMessage parameter.

Here’s an example of what multiple requests look like in Fiddler:

GET https://localhost:12345/RandomNumber HTTP/1.1
Host: localhost:12345
Authorization: Bearer 11

GET https://localhost:12345/RandomNumber HTTP/1.1
Host: localhost:12345
Authorization: Bearer 12

Code language: plaintext (plaintext)

Notice a unique authorization header was added to each request.

GetWithHeadersAsync() extension method for per request headers

HttpClient.GetAsync() / PostAsync() are convenience methods. It would be nice if there were overloads of these that accepted a list of per request headers, but there aren’t.

If you don’t want to have HttpRequestMessage + SendAsync() all over the place, you can abstract that logic away by using extension methods. Here’s an example:

public static class HttpClientExtensions
{
	public static async Task<HttpResponseMessage> GetWithHeadersAsync(this HttpClient httpClient, string requestUri, Dictionary<string, string> headers)
	{
		using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri))
		{
			foreach(var header in headers)
			{
				request.Headers.Add(header.Key, header.Value);
			}

			return await httpClient.SendAsync(request);
		}
	}

}
Code language: C# (cs)

You can use the extension method in a similar way that you’re used to using GetAsync():

public async Task<string> GetRandomNumber(string Token)
{
	var response = await HttpClient.GetWithHeadersAsync(randomNumberUrl, new Dictionary<string, string>()
	{
		["Authorization"] = $"Bearer {Token}"
	});

	response.EnsureSuccessStatusCode();

	return await response.Content.ReadAsStringAsync();
}
Code language: C# (cs)

This is just one extension method. You can use this as starting point. I wouldn’t bother adding extension methods for all possible overloads of GetAsync() or PostAsync().

Use HttpRequestHeaders properties for adding common headers

You can add any header using .Add(key, value). For common headers, such as Authorization, you can also add the header through properties in HttpRequestHeaders. This can help you avoid mistakes and improves readability.

For example, you can add the Authorization header in these two functionally equivalent ways:

//Option 1
request.Headers.Add("Authorization", $"Bearer {Token}");

//Option 2 - Using the common header property
request.Headers.Authorization = new AuthenticationHeaderValue(scheme: "Bearer", parameter: Token);
Code language: C# (cs)

11 thoughts on “C# – How to add request headers when using HttpClient”

  1. Good examples but what are the allowed values for the scheme parameter for AuthenticationHeaderValue ?

    Is this just the “username” and set by whoever coded up the API?

    Reply
    • There are a few standard HTTP auth schemes , such as Basic and Bearer, but AuthenticationHeaderValue doesn’t validate what you pass in. It just formats it properly for you. You’ll want to set the auth headers according to what the web API you’re integrating with requires.

      Reply
  2. I am using DefaultRequestHeaders.Add to add ClientId and ClientSecret to hit one of the mulesoft endpoints but I was asked to add a GUID in every request header to successfully hit the endpoint. I am generating the GUID in code but in order to add it to headers along with existing ClientId and ClientSecret, how can I do that?

    Reply
    • Hi Sunny,

      Keep using HttpClient.DefaultRequestHeaders.Add for the two headers that are the same for all requests – ClientId and ClientSecret.

      For the other header, where you are generating a unique value for each request, you’ll have to build an HttpRequestMessage, use HttpRequestMessage.Headers.Add(), and send it with HttpClient.SendAsync(). Check the “Add a header per request” section in the article for a code example.

      Reply
  3. Hi,

    Is it safe to use “PostAsJsonAsync” if we have single HttpClient instance?
    Like HttpClient.PostAsJsonAsync(uri, value) — Is this Thread safe?

    Thanks

    Reply
  4. I don’t get how `HttpRequestMessage.Headers` is supposed to be more more thread-safe than `HttpClient.DefaultRequestHeaders`. Both are the same collection type so where should that thread-safety be coming from?
    And how is it that thread-safety is such a pervasive concern in any scenario? I can imagine a number of use cases for `HttpClient` where thread safety doesn’t matter a bit. Like single-threaded applications for example.

    Reply
    • If you’re using HttpClient in a single-threaded context, where you’re only ever sending one request, then thread-safety isn’t a concern. Go ahead and use HttpClient.DefaultRequestHeaders to set the headers and don’t worry about it.

      If you’re using HttpClient in a multi-threaded context, you’ll be using a single instance to send concurrent requests from multiple threads. It was designed for this (as opposed to using a new instance for every request). This is where you have to be careful with how you set headers that are different for each request (per-request headers). For headers that will be the same for all requests, you can set them in HttpClient.DefaultRequestHeaders without a problem. This sets the header for all requests. For per-request headers, you want to set them specifically for that request in HttpRequestMessage.Headers. If you set this per-request header in DefaultRequestHeaders, it changes it for ALL concurrent requests in all threads, which means you’ll end up sending the wrong values, hence why it’s thread-unsafe to use HttpClient.DefaultRequestHeaders for per-request headers in a multi-threaded context.

      In short, if you’re not sending concurrent requests, don’t worry about it.

      Reply

Leave a Comment