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
.crtfile to/usr/local/share/ca-certificates/and runupdate-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 affectHttpClientin modern .NET. - Do not create a new
HttpClientper request to apply a bypass. UseIHttpClientFactory, 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
- Failed to determine the https port for redirect
- HttpClient resiliency with Polly
- HTTPS vs HTTP: benefits and differences
- Free tool: generate C# classes for API responses
Sources: Microsoft Learn: SocketsHttpHandler.SslOptions · Microsoft Learn: DangerousAcceptAnyServerCertificateValidator · Microsoft Learn: Enforce HTTPS and the development certificate
