Making HEAD Requests with HttpClient in C
A HEAD request retrieves metadata about a resource without downloading its content body. This makes it ideal for checking resource availability, content type, or file size without the overhead of a full GET request.
Basic HEAD Request
Use HttpClient.SendAsync()
with an HttpRequestMessage
configured for HEAD requests:
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Head, "https://example.com");
try
{
using var response = await client.SendAsync(request);
Console.WriteLine($"Status: {response.StatusCode}");
Console.WriteLine($"Content-Type: {response.Content.Headers.ContentType}");
Console.WriteLine($"Content-Length: {response.Content.Headers.ContentLength}");
Console.WriteLine($"Last-Modified: {response.Content.Headers.LastModified}");
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Request failed: {ex.Message}");
}
}
}
Extracting Useful Information
HEAD requests are commonly used to check specific resource properties:
public static async Task<bool> CheckResourceExists(string url)
{
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Head, url);
try
{
using var response = await client.SendAsync(request);
return response.IsSuccessStatusCode;
}
catch
{
return false;
}
}
public static async Task<long?> GetContentLength(string url)
{
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Head, url);
try
{
using var response = await client.SendAsync(request);
return response.Content.Headers.ContentLength;
}
catch
{
return null;
}
}
Advanced Example with Headers
For production code, include custom headers and proper error handling:
public class HttpHeadService
{
private readonly HttpClient _httpClient;
public HttpHeadService()
{
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");
}
public async Task<HeadRequestResult> GetResourceInfo(string url)
{
using var request = new HttpRequestMessage(HttpMethod.Head, url);
try
{
using var response = await _httpClient.SendAsync(request);
return new HeadRequestResult
{
IsSuccess = response.IsSuccessStatusCode,
StatusCode = response.StatusCode,
ContentType = response.Content.Headers.ContentType?.MediaType,
ContentLength = response.Content.Headers.ContentLength,
LastModified = response.Content.Headers.LastModified?.DateTime,
Server = response.Headers.Server?.ToString()
};
}
catch (HttpRequestException ex)
{
return new HeadRequestResult
{
IsSuccess = false,
ErrorMessage = ex.Message
};
}
}
public void Dispose() => _httpClient?.Dispose();
}
public class HeadRequestResult
{
public bool IsSuccess { get; set; }
public HttpStatusCode StatusCode { get; set; }
public string ContentType { get; set; }
public long? ContentLength { get; set; }
public DateTime? LastModified { get; set; }
public string Server { get; set; }
public string ErrorMessage { get; set; }
}
Best Practices
- Reuse HttpClient: Create one instance per application lifetime to avoid socket exhaustion
- Use HTTPS: Always use secure URLs when possible
- Handle exceptions: Wrap requests in try-catch blocks for network failures
- Set timeouts: Configure appropriate timeout values for your use case
- Check status codes: Always verify
IsSuccessStatusCode
before processing headers
Common Use Cases
- File size checking: Get content length before downloading large files
- Cache validation: Check Last-Modified headers for conditional requests
- Link verification: Validate URLs without downloading content
- API health checks: Test endpoint availability efficiently