Updated October 2026 for .NET 10 (LTS).

Your .NET app calls an HTTPS endpoint with a self-signed, expired or internal certificate, and the call fails with an SSL error. You can switch certificate checking off, but in most cases there is a safer fix that takes the same amount of time. This guide shows the safe options first, then how to bypass validation correctly for development only.

The error

System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure: RemoteCertificateNameMismatch, RemoteCertificateChainErrors

Warning: bypassing certificate validation means your app will talk to any server, including an attacker in the middle. Never ship a bypass to production.

Option 1: Trust the certificate instead (recommended)

  • Local ASP.NET Core APIs: trust the .NET development certificate once with dotnet dev-certs https --trust.
  • Internal company certificates: install your company’s root CA certificate on the machine or in the container. On Debian or Ubuntu, copy the .crt file to /usr/local/share/ca-certificates/ and run update-ca-certificates. On Windows, import it into Trusted Root Certification Authorities.
  • Name mismatch: call the host name that is on the certificate (for example api.internal.example.com), not an IP address.

With the certificate trusted, no code changes are needed and validation stays on.

Option 2: Trust one specific self-signed certificate (pinning)

If you cannot change the machine’s trust store, accept only the one certificate you expect, identified by its SHA-256 thumbprint. Every other invalid certificate is still rejected:

using System.Net.Security;
using System.Security.Cryptography;

const string ExpectedThumbprint = "3F2A9C...E41B"; // SHA-256, no colons

builder.Services.AddHttpClient("InventoryApi", client =>
        client.BaseAddress = new Uri("https://inventory.internal.example.com/"))
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new SocketsHttpHandler();
        handler.SslOptions.RemoteCertificateValidationCallback = (_, certificate, _, errors) =>
            errors == SslPolicyErrors.None ||
            (certificate is not null &&
             string.Equals(
                 certificate.GetCertHashString(HashAlgorithmName.SHA256),
                 ExpectedThumbprint,
                 StringComparison.OrdinalIgnoreCase));
        return handler;
    });

Get the thumbprint with openssl x509 -in server.crt -noout -fingerprint -sha256 and remove the colons. Remember to update it when the certificate is renewed.

Option 3: Bypass validation in Development only

For quick local testing against a throwaway server, you can accept any certificate. Use a named client from IHttpClientFactory so the bypass applies only to that client, and guard it with the environment:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpClient("AccountClient", client =>
        client.BaseAddress = new Uri("https://localhost:7043/"))
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new SocketsHttpHandler();

        if (builder.Environment.IsDevelopment())
        {
            // DEVELOPMENT ONLY: accepts every certificate
            handler.SslOptions.RemoteCertificateValidationCallback = (_, _, _, _) => true;
        }

        return handler;
    });

Then use the client as usual:

app.MapGet("/accounts/{id:int}", async (int id, IHttpClientFactory factory) =>
{
    var client = factory.CreateClient("AccountClient");
    var account = await client.GetFromJsonAsync<Account>($"api/accounts/{id}");
    return account is null ? Results.NotFound() : Results.Ok(account);
});

If you use HttpClientHandler instead, the equivalent is the built-in, clearly named validator:

new HttpClientHandler
{
    ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};

What not to do

  • Do not set ServicePointManager.ServerCertificateValidationCallback. It is a legacy .NET Framework API and does not affect HttpClient in modern .NET.
  • Do not create a new HttpClient per request to apply a bypass. Use IHttpClientFactory, as shown above. See HttpClient best practices.
  • Do not read the bypass from a config flag that could be switched on in production by mistake. The environment check is safer.

FAQ

Does this work in Linux containers?

Yes. SocketsHttpHandler is the default handler on Windows, Linux and macOS in modern .NET, and all three options above work in containers. For Option 1, add the CA certificate in your Dockerfile.

How do I send a client certificate instead?

That is mutual TLS, a different setup. See Client certificates with HttpClientFactory.

Related

Sources: Microsoft Learn: SocketsHttpHandler.SslOptions · Microsoft Learn: DangerousAcceptAnyServerCertificateValidator · Microsoft Learn: Enforce HTTPS and the development certificate

Leave a Reply

Your email address will not be published. Required fields are marked *