Edit

Share via


Make HTTP requests with the HttpClient class

In this article, you learn how to make HTTP requests and handle responses with the HttpClient class.

Important

All of the example HTTP requests in this article target one of the following URLs:

HTTP endpoints commonly return JavaScript Object Notation (JSON) data, but not always. For convenience, the optional System.Net.Http.Json NuGet package provides several extension methods for HttpClient and HttpContent objects that perform automatic serialization and deserialization by using the 📦 System.Text.Json NuGet package. The examples in this article call attention to places where these extensions are available.

Tip

All source code referenced in this article is available in the GitHub: .NET Docs repository.

Create an HttpClient object

Most of the examples in this article reuse the same HttpClient instance, so you can configure the instance once and use it for the remaining examples. To create an HttpClient object, use the HttpClient class constructor. For more information, see Guidelines for using HttpClient.

C#
// HttpClient lifecycle management best practices:
// https://learn.microsoft.com/dotnet/fundamentals/networking/http/httpclient-guidelines#recommended-use
private static HttpClient sharedClient = new()
{
    BaseAddress = new Uri("https://jsonplaceholder.typicode.com"),
};

The code completes the following tasks:

  • Instantiate a new HttpClient instance as a static variable. According to the guidelines, the recommended approach is to reuse HttpClient instances during the application lifecycle.
  • Set the HttpClient.BaseAddress property to "https://jsonplaceholder.typicode.com".

This HttpClient instance uses the base address to make subsequent requests. To apply other configurations, consider the following APIs:

Tip

Alternatively, you can create HttpClient instances by using a factory-pattern approach that allows you to configure any number of clients and consume them as dependency injection services. For more information, see HTTP client factory with .NET.

Make an HTTP request

To make an HTTP request, you call any of the following API methods:

A USER SPECIFIED request indicates that the SendAsync method accepts any valid HttpMethod object.

Warning

Making HTTP requests is considered network I/O-bound work. A synchronous HttpClient.Send method exists, but the recommendation is to use the asynchronous APIs instead, unless you have good reason not to.

Note

While targeting Android devices (such as with .NET MAUI development), you must add the android:usesCleartextTraffic="true" definition to the <application></application> section in the AndroidManifest.xml file. This setting enables clear-text traffic, such as HTTP requests, which is otherwise disabled by default due to Android security policies. Consider the following example XML settings:

XML
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <application android:usesCleartextTraffic="true"></application>
  <!-- omitted for brevity -->
</manifest>

For more information, see Enable clear-text network traffic for the localhost domain.

Understand HTTP content

The HttpContent type is used to represent an HTTP entity body and corresponding content headers. For HTTP methods (or request methods) that require a body (POST, PUT, PATCH), you use the HttpContent class to specify the body of the request. Most examples show how to prepare the StringContent subclass with a JSON payload, but other subclasses exist for different content (MIME) types.

The HttpContent class is also used to represent the response body of the HttpResponseMessage class, which is accessible on the HttpResponseMessage.Content property.

Use an HTTP GET request

A GET request shouldn't send a body. This request is used (as the method name indicates) to retrieve (or get) data from a resource. To make an HTTP GET request given an HttpClient instance and a Uri object, use the HttpClient.GetAsync method:

C#
static async Task GetAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.GetAsync("todos/3");
    
    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();
    
    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output:
    //   GET https://jsonplaceholder.typicode.com/todos/3 HTTP/1.1
    //   {
    //     "userId": 1,
    //     "id": 3,
    //     "title": "fugiat veniam minus",
    //     "completed": false
    //   }
}

The code completes the following tasks:

  • Make a GET request to the "https://jsonplaceholder.typicode.com/todos/3" endpoint.
  • Ensure the response is successful.
  • Write the request details to the console.
  • Read the response body as a string.
  • Write the JSON response body to the console.

The WriteRequestToConsole method is a custom extension that isn't part of the framework. If you're curious about the implementation, consider the following C# code:

C#
static class HttpResponseMessageExtensions
{
    internal static void WriteRequestToConsole(this HttpResponseMessage response)
    {
        if (response is null)
        {
            return;
        }

        var request = response.RequestMessage;
        Console.Write($"{request?.Method} ");
        Console.Write($"{request?.RequestUri} ");
        Console.WriteLine($"HTTP/{request?.Version}");        
    }
}

This functionality is used to write the request details to the console in the following form:

<HTTP Request Method> <Request URI> <HTTP/Version>

As an example, the GET request to the "https://jsonplaceholder.typicode.com/todos/3" endpoint outputs the following message:

Output
GET https://jsonplaceholder.typicode.com/todos/3 HTTP/1.1

Create the HTTP GET request from JSON

The https://jsonplaceholder.typicode.com/todos endpoint returns a JSON array of Todo objects. Their JSON structure resembles the following form:

JSON
[
  {
    "userId": 1,
    "id": 1,
    "title": "example title",
    "completed": false
  },
  {
    "userId": 1,
    "id": 2,
    "title": "another example title",
    "completed": true
  },
]

The C# Todo object is defined as follows:

C#
public record class Todo(
    int? UserId = null,
    int? Id = null,
    string? Title = null,
    bool? Completed = null);

It's a record class type, with optional Id, Title, Completed, and UserId properties. For more information on the record type, see Introduction to record types in C#. To automatically deserialize GET requests into a strongly typed C# object, use the GetFromJsonAsync extension method that's part of the 📦 System.Net.Http.Json NuGet package.

C#
static async Task GetFromJsonAsync(HttpClient httpClient)
{
    var todos = await httpClient.GetFromJsonAsync<List<Todo>>(
        "todos?userId=1&completed=false");

    Console.WriteLine("GET https://jsonplaceholder.typicode.com/todos?userId=1&completed=false HTTP/1.1");
    todos?.ForEach(Console.WriteLine);
    Console.WriteLine();

    // Expected output:
    //   GET https://jsonplaceholder.typicode.com/todos?userId=1&completed=false HTTP/1.1
    //   Todo { UserId = 1, Id = 1, Title = delectus aut autem, Completed = False }
    //   Todo { UserId = 1, Id = 2, Title = quis ut nam facilis et officia qui, Completed = False }
    //   Todo { UserId = 1, Id = 3, Title = fugiat veniam minus, Completed = False }
    //   Todo { UserId = 1, Id = 5, Title = laboriosam mollitia et enim quasi adipisci quia provident illum, Completed = False }
    //   Todo { UserId = 1, Id = 6, Title = qui ullam ratione quibusdam voluptatem quia omnis, Completed = False }
    //   Todo { UserId = 1, Id = 7, Title = illo expedita consequatur quia in, Completed = False }
    //   Todo { UserId = 1, Id = 9, Title = molestiae perspiciatis ipsa, Completed = False }
    //   Todo { UserId = 1, Id = 13, Title = et doloremque nulla, Completed = False }
    //   Todo { UserId = 1, Id = 18, Title = dolorum est consequatur ea mollitia in culpa, Completed = False }
}

The code completes the following tasks:

  • Make a GET request to "https://jsonplaceholder.typicode.com/todos?userId=1&completed=false".

    The query string represents the filtering criteria for the request. When the command succeeds, the response is automatically deserialized into a List<Todo> object.

  • Write the request details to the console, along with each Todo object.

Use an HTTP POST request

A POST request sends data to the server for processing. The Content-Type header of the request signifies what MIME type the body is sending. To make an HTTP POST request given an HttpClient instance and a Uri object, use the HttpClient.PostAsync method:

C#
static async Task PostAsync(HttpClient httpClient)
{
    using StringContent jsonContent = new(
        JsonSerializer.Serialize(new
        {
            userId = 77,
            id = 1,
            title = "write code sample",
            completed = false
        }),
        Encoding.UTF8,
        "application/json");

    using HttpResponseMessage response = await httpClient.PostAsync(
        "todos",
        jsonContent);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();
    
    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output:
    //   POST https://jsonplaceholder.typicode.com/todos HTTP/1.1
    //   {
    //     "userId": 77,
    //     "id": 201,
    //     "title": "write code sample",
    //     "completed": false
    //   }
}

The code completes the following tasks:

  • Prepare a StringContent instance with the JSON body of the request (MIME type of "application/json").
  • Make a POST request to the "https://jsonplaceholder.typicode.com/todos" endpoint.
  • Ensure the response is successful and write the request details to the console.
  • Write the response body as a string to the console.

Create the HTTP POST request as JSON

To automatically serialize POST request arguments and deserialize responses into strongly typed C# objects, use the PostAsJsonAsync extension method that's part of the System.Net.Http.Json NuGet package.

C#
static async Task PostAsJsonAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.PostAsJsonAsync(
        "todos", 
        new Todo(UserId: 9, Id: 99, Title: "Show extensions", Completed: false));

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var todo = await response.Content.ReadFromJsonAsync<Todo>();
    Console.WriteLine($"{todo}\n");

    // Expected output:
    //   POST https://jsonplaceholder.typicode.com/todos HTTP/1.1
    //   Todo { UserId = 9, Id = 201, Title = Show extensions, Completed = False }
}

The code completes the following tasks:

  • Serialize the Todo instance as JSON and make a POST request to the "https://jsonplaceholder.typicode.com/todos" endpoint.
  • Ensure the response is successful and write the request details to the console.
  • Deserialize the response body into a Todo instance and write the Todo object to the console.

Use an HTTP PUT request

The PUT request method either replaces an existing resource or creates a new one by using the request body payload. To make an HTTP PUT request given an HttpClient instance and a Uri object, use the HttpClient.PutAsync method:

C#
static async Task PutAsync(HttpClient httpClient)
{
    using StringContent jsonContent = new(
        JsonSerializer.Serialize(new 
        {
            userId = 1,
            id = 1,
            title = "foo bar",
            completed = false
        }),
        Encoding.UTF8,
        "application/json");

    using HttpResponseMessage response = await httpClient.PutAsync(
        "todos/1",
        jsonContent);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();
    
    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output:
    //   PUT https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
    //   {
    //     "userId": 1,
    //     "id": 1,
    //     "title": "foo bar",
    //     "completed": false
    //   }
}

The code completes the following tasks:

  • Prepare a StringContent instance with the JSON body of the request (MIME type of "application/json").
  • Make a PUT request to the "https://jsonplaceholder.typicode.com/todos/1" endpoint.
  • Ensure the response is successful and write the request details with the JSON response body to the console.

Create the HTTP PUT request as JSON

To automatically serialize PUT request arguments and deserialize responses into strongly typed C# objects, use the PutAsJsonAsync extension method that's part of the System.Net.Http.Json NuGet package.

C#
static async Task PutAsJsonAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.PutAsJsonAsync(
        "todos/5",
        new Todo(Title: "partially update todo", Completed: true));

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var todo = await response.Content.ReadFromJsonAsync<Todo>();
    Console.WriteLine($"{todo}\n");

    // Expected output:
    //   PUT https://jsonplaceholder.typicode.com/todos/5 HTTP/1.1
    //   Todo { UserId = , Id = 5, Title = partially update todo, Completed = True }
}

The code completes the following tasks:

  • Serialize the Todo instance as JSON and make a PUT request to the "https://jsonplaceholder.typicode.com/todos/5" endpoint.
  • Ensure the response is successful and write the request details to the console.
  • Deserialize the response body into a Todo instance and write the Todo objects to the console.

Use an HTTP PATCH request

The PATCH request is a partial update to an existing resource. This request doesn't create a new resource and it isn't intended to replace an existing resource. Instead, this method only partially updates a resource. To make an HTTP PATCH request given an HttpClient instance and a Uri object, use the HttpClient.PatchAsync method:

C#
static async Task PatchAsync(HttpClient httpClient)
{
    using StringContent jsonContent = new(
        JsonSerializer.Serialize(new
        {
            completed = true
        }),
        Encoding.UTF8,
        "application/json");

    using HttpResponseMessage response = await httpClient.PatchAsync(
        "todos/1",
        jsonContent);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output
    //   PATCH https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
    //   {
    //     "userId": 1,
    //     "id": 1,
    //     "title": "delectus aut autem",
    //     "completed": true
    //   }
}

The code completes the following tasks:

  • Prepare a StringContent instance with the JSON body of the request (MIME type of "application/json").
  • Make a PATCH request to the "https://jsonplaceholder.typicode.com/todos/1" endpoint.
  • Ensure the response is successful and write the request details with the JSON response body to the console.

No extension methods exist for PATCH requests in the System.Net.Http.Json NuGet package.

Use an HTTP DELETE request

A DELETE request removes an existing resource and the request is idempotent, but not safe. Multiple DELETE requests to the same resources yield the same result, but the request affects the state of the resource. To make an HTTP DELETE request given an HttpClient instance and a Uri object, use the HttpClient.DeleteAsync method:

C#
static async Task DeleteAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.DeleteAsync("todos/1");
    
    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output
    //   DELETE https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
    //   {}
}

The code completes the following tasks:

  • Make a DELETE request to the "https://jsonplaceholder.typicode.com/todos/1" endpoint.
  • Ensure the response is successful and write the request details to the console.

Tip

The response to a DELETE request (just like a PUT request) might or might not include a body.

Explore the HTTP HEAD request

The HEAD request is similar to a GET request. Instead of returning the resource, this request returns only the headers associated with the resource. A response to the HEAD request doesn't return a body. To make an HTTP HEAD request given an HttpClient instance and a Uri object, use the HttpClient.SendAsync method with the HttpMethod type set to HttpMethod.Head:

C#
static async Task HeadAsync(HttpClient httpClient)
{
    using HttpRequestMessage request = new(
        HttpMethod.Head, 
        "https://www.example.com");

    using HttpResponseMessage response = await httpClient.SendAsync(request);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    foreach (var header in response.Headers)
    {
        Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
    }
    Console.WriteLine();

    // Expected output:
    //   HEAD https://www.example.com/ HTTP/1.1
    //   Accept-Ranges: bytes
    //   Age: 550374
    //   Cache-Control: max-age=604800
    //   Date: Wed, 10 Aug 2022 17:24:55 GMT
    //   ETag: "3147526947"
    //   Server: ECS, (cha / 80E2)
    //   X-Cache: HIT
}

The code completes the following tasks:

  • Make a HEAD request to the "https://www.example.com/" endpoint.
  • Ensure the response is successful and write the request details to the console.
  • Iterate over all of the response headers and write each header to the console.

Explore the HTTP OPTIONS request

The OPTIONS request is used to identify which HTTP methods a server or endpoint supports. To make an HTTP OPTIONS request given an HttpClient instance and a Uri object, use the HttpClient.SendAsync method with the HttpMethod type set to HttpMethod.Options:

C#
static async Task OptionsAsync(HttpClient httpClient)
{
    using HttpRequestMessage request = new(
        HttpMethod.Options, 
        "https://www.example.com");

    using HttpResponseMessage response = await httpClient.SendAsync(request);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    foreach (var header in response.Content.Headers)
    {
        Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
    }
    Console.WriteLine();

    // Expected output
    //   OPTIONS https://www.example.com/ HTTP/1.1
    //   Allow: OPTIONS, GET, HEAD, POST
    //   Content-Type: text/html; charset=utf-8
    //   Expires: Wed, 17 Aug 2022 17:28:42 GMT
    //   Content-Length: 0
}

The code completes the following tasks:

  • Send an OPTIONS HTTP request to the "https://www.example.com/" endpoint.
  • Ensure the response is successful and write the request details to the console.
  • Iterate over all of the response content headers and write each header to the console.

Explore the HTTP TRACE request

The TRACE request can be useful for debugging as it provides application-level loop-back of the request message. To make an HTTP TRACE request, create an HttpRequestMessage by using the HttpMethod.Trace type:

C#
using HttpRequestMessage request = new(
    HttpMethod.Trace, 
    "{ValidRequestUri}");

Caution

Not all HTTP servers support the TRACE HTTP method. This method can expose a security vulnerability if used unwisely. For more information, see Open Web Application Security Project (OWASP): Cross Site Tracing.

Handle an HTTP response

When you handle an HTTP response, you interact with the HttpResponseMessage type. Several members are used to evaluate the validity of a response. The HTTP status code is available in the HttpResponseMessage.StatusCode property.

Suppose you send a request given a client instance:

C#
using HttpResponseMessage response = await httpClient.SendAsync(request);

To ensure the response is OK (HTTP status code 200), you can evaluate the value as shown in the following example:

C#
if (response is { StatusCode: HttpStatusCode.OK })
{
    // Omitted for brevity...
}

There are other HTTP status codes that represent a successful response, such as CREATED (HTTP status code 201), ACCEPTED (HTTP status code 202), NO CONTENT (HTTP status code 204), and RESET CONTENT (HTTP status code 205). You can use the HttpResponseMessage.IsSuccessStatusCode property to evaluate these codes as well, which ensures that the response status code is within the range 200-299:

C#
if (response.IsSuccessStatusCode)
{
    // Omitted for brevity...
}

If you need to have the framework throw the HttpRequestException error, you can call the HttpResponseMessage.EnsureSuccessStatusCode() method:

C#
response.EnsureSuccessStatusCode();

This code throws an HttpRequestException error if the response status code isn't within the 200-299 range.

Explore HTTP valid content responses

With a valid response, you can access the response body by using the Content property. The body is available as an HttpContent instance, which you can use to access the body as a stream, byte array, or string.

The following code uses the responseStream object to read the response body:

C#
await using Stream responseStream =
    await response.Content.ReadAsStreamAsync();

You can use different objects to read the response body. Use the responseByteArray object to read the response body:

C#
byte[] responseByteArray = await response.Content.ReadAsByteArrayAsync();

Use the responseString object to read the response body:

C#
string responseString = await response.Content.ReadAsStringAsync();

When you know an HTTP endpoint returns JSON, you can deserialize the response body into any valid C# object by using the System.Net.Http.Json NuGet package:

C#
T? result = await response.Content.ReadFromJsonAsync<T>();

In this code, the result value is the response body deserialized as the type T.

Use HTTP error handling

When an HTTP request fails, the system throws the HttpRequestException object. Catching the exception alone might not be sufficient. There are other potential exceptions thrown that you might want to consider handling. For example, the calling code might use a cancellation token that was canceled before the request completed. In this scenario, you can catch the TaskCanceledException error:

C#
using var cts = new CancellationTokenSource();
try
{
    // Assuming:
    //   httpClient.Timeout = TimeSpan.FromSeconds(10)

    using var response = await httpClient.GetAsync(
        "http://localhost:5001/sleepFor?seconds=100", cts.Token);
}
catch (OperationCanceledException ex) when (cts.IsCancellationRequested)
{
    // When the token has been canceled, it is not a timeout.
    Console.WriteLine($"Canceled: {ex.Message}");
}

Likewise, when you make an HTTP request, if the server doesn't respond before the HttpClient.Timeout value is exceeded, the same exception is thrown. In this scenario, you can distinguish that the time-out occurred by evaluating the Exception.InnerException property when catching the TaskCanceledException error:

C#
try
{
    // Assuming:
    //   httpClient.Timeout = TimeSpan.FromSeconds(10)

    using var response = await httpClient.GetAsync(
        "http://localhost:5001/sleepFor?seconds=100");
}
catch (OperationCanceledException ex) when (ex.InnerException is TimeoutException tex)
{
    Console.WriteLine($"Timed out: {ex.Message}, {tex.Message}");
}

In the code, when the inner exception is an TimeoutException type, then the time-out occurred and the cancellation token doesn't cancel the request.

To evaluate the HTTP status code when you catch the HttpRequestException object, you can evaluate the HttpRequestException.StatusCode property:

C#
try
{
    // Assuming:
    //   httpClient.Timeout = TimeSpan.FromSeconds(10)

    using var response = await httpClient.GetAsync(
        "http://localhost:5001/doesNotExist");

    response.EnsureSuccessStatusCode();
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
    // Handle 404
    Console.WriteLine($"Not found: {ex.Message}");
}

In the code, the EnsureSuccessStatusCode() method is called to throw an exception if the response isn't successful. The HttpRequestException.StatusCode property is then evaluated to determine if the response was a 404 (HTTP status code 404). There are several helper methods on the HttpClient object that implicitly call the EnsureSuccessStatusCode method on your behalf.

For HTTP error handing, consider the following APIs:

Tip

All HttpClient methods used to make HTTP requests that don't return an HttpResponseMessage type implicitly call the EnsureSuccessStatusCode method on your behalf.

When you call these methods, you can handle the HttpRequestException object and evaluate the HttpRequestException.StatusCode property to determine the HTTP status code of the response:

C#
try
{
    // These extension methods will throw HttpRequestException
    // with StatusCode set when the HTTP request status code isn't 2xx:
    //
    //   GetByteArrayAsync
    //   GetStreamAsync
    //   GetStringAsync

    using var stream = await httpClient.GetStreamAsync(
        "https://localhost:5001/doesNotExists");
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
    // Handle 404
    Console.WriteLine($"Not found: {ex.Message}");
}

There might be scenarios where you need to throw the HttpRequestException object in your code. The HttpRequestException() constructor is public and you can use it to throw an exception with a custom message:

C#
try
{
    using var response = await httpClient.GetAsync(
        "https://localhost:5001/doesNotExists");

    // Throw for anything higher than 400.
    if (response is { StatusCode: >= HttpStatusCode.BadRequest })
    {
        throw new HttpRequestException(
            "Something went wrong", inner: null, response.StatusCode);
    }
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
    Console.WriteLine($"Not found: {ex.Message}");
}

Configure an HTTP proxy

An HTTP proxy can be configured in one of two ways. A default is specified on the HttpClient.DefaultProxy property. Alternatively, you can specify a proxy on the HttpClientHandler.Proxy property.

Use a global default proxy

The HttpClient.DefaultProxy property is a static property that determines the default proxy that all HttpClient instances use, if no proxy is set explicitly in the HttpClientHandler object passed through its constructor.

The default instance returned by this property initializes according to a different set of rules depending on your platform:

  • Windows: Read proxy configuration from environment variables, or if variables aren't defined, read from user proxy settings.
  • macOS: Read proxy configuration from environment variables, or if variables aren't defined, read from system proxy settings.
  • Linux: Read proxy configuration from environment variables, or if variables aren't defined, initialize a nonconfigured instance to bypass all addresses.

The DefaultProxy property initialization on Windows and Unix-based platforms uses the following environment variables:

  • HTTP_PROXY: The proxy server used on HTTP requests.
  • HTTPS_PROXY: The proxy server used on HTTPS requests.
  • ALL_PROXY: The proxy server used on HTTP and/or HTTPS requests when the HTTP_PROXY and/or HTTPS_PROXY variables aren't defined.
  • NO_PROXY: A comma-separated list of hostnames to exclude from proxying. Asterisks aren't supported for wildcards. Use a leading period (.) when you want to match a subdomain. Examples: NO_PROXY=.example.com (with leading period) matches www.example.com, but doesn't match example.com. NO_PROXY=example.com (without leading period) doesn't match www.example.com. This behavior might be revisited in the future to match other ecosystems better.

On systems where environment variables are case-sensitive, the variable names can be all lowercase or all uppercase. The lowercase names are checked first.

The proxy server can be a hostname or IP address, optionally followed by a colon and port number, or it can be an http URL, optionally including a username and password for proxy authentication. The URL must start with http, not https, and can't include any text after the hostname, IP, or port.

Configure the proxy per client

The HttpClientHandler.Proxy property identifies the WebProxy object to use to process requests to internet resources. To specify that no proxy should be used, set the Proxy property to the proxy instance returned by the GlobalProxySelection.GetEmptyWebProxy() method.

The local computer or application configuration file might specify that a default proxy is used. If the Proxy property is specified, then the proxy settings from the Proxy property override the local computer or application config file and the handler uses the proxy settings specified. If no proxy is specified in a config file and the Proxy property is unspecified, the handler uses the proxy settings inherited from the local computer. If there are no proxy settings, the request is sent directly to the server.

The HttpClientHandler class parses a proxy bypass list with wildcard characters inherited from local computer settings. For example, the HttpClientHandler class parses a bypass list of "nt*" from browsers as a regular expression of "nt.*". Therefore, a URL of http://nt.com bypasses the proxy by using the HttpClientHandler class.

The HttpClientHandler class supports local proxy bypass. The class considers a destination to be local if any of the following conditions are met:

  • The destination contains a flat name (no periods (.) in the URL).
  • The destination contains a loopback address (Loopback or IPv6Loopback) or the destination contains an IPAddress property assigned to the local computer.
  • The domain suffix of the destination matches the local computer's domain suffix, as defined in the DomainName property.

For more information about configuring a proxy, see the following APIs:

Next steps