HTTP HEAD
HttpClient
.NET 4.5
C#
web requests

HTTP HEAD request with HttpClient in .NET 4.5 and C

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

An HTTP HEAD request is the right tool when you want metadata about a resource without downloading the body. In .NET 4.5, HttpClient can send HEAD requests just fine, but you do it through HttpRequestMessage and SendAsync because there is no built-in HeadAsync helper.

Build the Request Explicitly

The key idea is simple: create a request object, set the method to HEAD, and send it with HttpClient.

csharp
1using System;
2using System.Net.Http;
3using System.Threading.Tasks;
4
5public class HeadProbe
6{
7    public static async Task RunAsync()
8    {
9        using (var client = new HttpClient())
10        using (var request = new HttpRequestMessage(HttpMethod.Head, "https://example.com/file.zip"))
11        using (var response = await client.SendAsync(request))
12        {
13            Console.WriteLine((int)response.StatusCode);
14            Console.WriteLine(response.ReasonPhrase);
15        }
16    }
17}

That is all a HEAD request really is in HttpClient. The server receives the same target URL and most of the same headers as a GET, but it should return only response headers.

Use HEAD When Headers Are the Real Goal

A common mistake is sending GET and then ignoring the body. If you only need metadata such as Content-Length, Last-Modified, or ETag, HEAD communicates the intent better and can avoid unnecessary transfer work.

csharp
1using System;
2using System.Net.Http;
3using System.Threading.Tasks;
4
5public class MetadataExample
6{
7    public static async Task RunAsync()
8    {
9        using (var client = new HttpClient())
10        using (var request = new HttpRequestMessage(HttpMethod.Head, "https://example.com/archive.tar.gz"))
11        using (var response = await client.SendAsync(
12            request,
13            HttpCompletionOption.ResponseHeadersRead))
14        {
15            Console.WriteLine("Status: " + response.StatusCode);
16            Console.WriteLine("Length: " + (response.Content.Headers.ContentLength?.ToString() ?? "unknown"));
17            Console.WriteLine("Last-Modified: " + (response.Content.Headers.LastModified?.ToString() ?? "missing"));
18            Console.WriteLine("ETag: " + (response.Headers.ETag?.Tag ?? "missing"));
19        }
20    }
21}

ResponseHeadersRead is a good fit here because headers are the only interesting part of the response.

Wrap It in a Reusable Helper

In application code, the caller usually wants an answer such as "does this file exist" or "what is the remote size." A small helper keeps the rest of the code cleaner.

csharp
1using System.Net;
2using System.Net.Http;
3using System.Threading.Tasks;
4
5public static class RemoteFileInfo
6{
7    public static async Task<long?> TryGetContentLengthAsync(HttpClient client, string url)
8    {
9        using (var request = new HttpRequestMessage(HttpMethod.Head, url))
10        using (var response = await client.SendAsync(
11            request,
12            HttpCompletionOption.ResponseHeadersRead))
13        {
14            if (response.StatusCode == HttpStatusCode.NotFound)
15            {
16                return null;
17            }
18
19            response.EnsureSuccessStatusCode();
20            return response.Content.Headers.ContentLength;
21        }
22    }
23}

This keeps HEAD logic, status handling, and header parsing in one place. It also makes testing easier because the code that consumes the helper no longer needs to understand the entire HTTP exchange.

Be Ready for Servers That Mishandle HEAD

Although HEAD is part of HTTP, some servers do not implement it properly. A few return 405 Method Not Allowed, and others respond without the headers you expected. If you know you must talk to such an endpoint, a deliberate fallback to GET may be necessary.

csharp
1using System.Net;
2using System.Net.Http;
3using System.Threading.Tasks;
4
5public static class HeadFallback
6{
7    public static async Task<HttpResponseMessage> SendHeadOrGetAsync(HttpClient client, string url)
8    {
9        var headRequest = new HttpRequestMessage(HttpMethod.Head, url);
10        var headResponse = await client.SendAsync(headRequest, HttpCompletionOption.ResponseHeadersRead);
11
12        if (headResponse.StatusCode == HttpStatusCode.MethodNotAllowed)
13        {
14            headResponse.Dispose();
15            var getRequest = new HttpRequestMessage(HttpMethod.Get, url);
16            return await client.SendAsync(getRequest, HttpCompletionOption.ResponseHeadersRead);
17        }
18
19        return headResponse;
20    }
21}

That fallback should be intentional, not automatic cargo cult. A GET may begin a large body transfer immediately, so you are changing both performance and semantics.

Reuse HttpClient Correctly

Even in .NET 4.5, you should avoid constructing a fresh HttpClient for every single probe inside a long-running process. Reusing the client usually gives better connection behavior and fewer resource surprises. It is also wise to set an explicit timeout that matches the job.

csharp
1var client = new HttpClient
2{
3    Timeout = TimeSpan.FromSeconds(10)
4};

For a service that performs many metadata checks, one long-lived client per logical service boundary is a sensible default.

Common Pitfalls

  • Trying to call a nonexistent HeadAsync method instead of using HttpRequestMessage.
  • Sending GET when the real requirement is only headers.
  • Assuming every upstream server handles HEAD correctly.
  • Creating a new HttpClient for every probe in a loop.
  • Checking only the status code and forgetting to inspect whether required headers are actually present.

Summary

  • In .NET 4.5, send HEAD by creating an HttpRequestMessage and using SendAsync.
  • Use HEAD when you need metadata but not the response body.
  • Read values such as Content-Length, Last-Modified, and ETag from the response headers.
  • Add a fallback only if you know the target server mishandles HEAD.
  • Reuse HttpClient and set explicit timeouts in real applications.

Course illustration
Course illustration

All Rights Reserved.