Allowing Untrusted SSL Certificates with HttpClient
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
To allow untrusted SSL certificates with HttpClient in C#, set a ServerCertificateCustomValidationCallback on HttpClientHandler that returns true. The simplest version uses the built-in DangerousAcceptAnyServerCertificateValidator. This bypasses all certificate validation, which is acceptable during local development but must never reach production. In production, the correct fix is trusting the certificate in the operating system's certificate store or using a properly issued certificate.
The Error You Are Seeing
When HttpClient connects to a server with an untrusted certificate (self-signed, expired, wrong hostname, or issued by an unknown CA), it throws:
This is the TLS handshake failing because the server's certificate chain cannot be validated against the system's trusted root store.
Quick Bypass for Development
The fastest way to suppress the error is DangerousAcceptAnyServerCertificateValidator:
The name is intentionally alarming. This delegate returns true for every certificate regardless of errors, hostname mismatches, or expiration.
Custom Validation Logic
Instead of accepting everything, you can write a callback that accepts only specific certificates. This gives you a middle ground between full bypass and full validation.
The callback receives four parameters:
| Parameter | Type | Description |
request | HttpRequestMessage | The outgoing HTTP request |
cert | X509Certificate2 | The server's certificate |
chain | X509Chain | The full certificate chain |
errors | SslPolicyErrors | Flags indicating what validation failed |
The SslPolicyErrors enum has three relevant values:
| Value | Meaning |
None | Certificate is fully valid |
RemoteCertificateChainErrors | Chain validation failed (self-signed, unknown CA, expired) |
RemoteCertificateNameMismatch | Certificate hostname does not match the request URL |
Using IHttpClientFactory in ASP.NET Core
In ASP.NET Core applications, you should not create HttpClient instances directly. Use IHttpClientFactory to manage handler lifetimes and configure SSL behavior per named or typed client:
Then inject and use the factory:
This approach keeps the SSL bypass scoped to a single named client rather than affecting all HTTP traffic in the application.
Environment-Conditional Bypass
The bypass should only activate in development. Use the hosting environment or a configuration flag to gate it:
In Option 2, the BypassSslValidation key would be set in appsettings.Development.json but not in appsettings.Production.json.
The Correct Fix: Trust the Certificate
Bypassing validation is a development shortcut. The production fix is adding the certificate to the trusted store so that HttpClient validates it normally:
After trusting the certificate, HttpClient accepts it without any custom callback. This is the approach that should be used in staging and production environments.
.NET Framework (Legacy Approach)
In .NET Framework (not .NET Core/.NET 5+), SSL validation is controlled globally through ServicePointManager:
This is even more dangerous than the per-handler approach because it disables validation for every HttpClient, WebClient, and HttpWebRequest in the process, including those created by third-party libraries. In .NET Core and .NET 5+, always use the per-handler ServerCertificateCustomValidationCallback instead.
Comparison: Bypass Approaches
| Approach | Scope | .NET Version | Risk Level |
HttpClientHandler callback | Single client | .NET Core+ | Moderate (scoped) |
IHttpClientFactory handler config | Named/typed client | .NET Core+ | Moderate (scoped) |
ServicePointManager callback | Entire process | .NET Framework | High (global) |
| Trust certificate in OS store | System-wide | Any | Low (proper fix) |
dotnet dev-certs https --trust | Dev machine | .NET Core+ | Low (dev only) |
Common Pitfalls
- Shipping the bypass to production: This is the single most important thing to get right. Disabling SSL validation in production exposes every request to man-in-the-middle attacks. An attacker on the network can intercept, read, and modify all traffic. Gate the bypass behind
IsDevelopment()or a configuration flag that is never set in production. - Using
ServicePointManagerin .NET Core: It does not work in .NET Core. The per-handler callback is the only option, which is actually safer because it is scoped to a single client. - Forgetting that
IHttpClientFactoryreuses handlers: TheConfigurePrimaryHttpMessageHandlercallback runs when a new handler is created, and handlers are pooled for two minutes by default. Changing configuration at runtime does not immediately affect existing pooled handlers. - Certificate pinning without rotation plans: If you pin a specific thumbprint in your custom callback, deploying a new certificate requires a code change and redeployment. Plan for certificate rotation by pinning the public key (SPKI) or the issuer instead.
- Docker containers missing CA certificates: Containers built from minimal base images often lack the host's trusted certificates. Copy the CA certificate into the image during the Docker build (
COPY ca.crt /usr/local/share/ca-certificates/ && RUN update-ca-certificates) rather than bypassing validation in the application code. - Singleton
HttpClientwith development handler: If you registerHttpClientas a singleton with SSL bypass and it survives into a production deployment through misconfiguration, every request in the application is unprotected for the process lifetime.
Summary
- Use
ServerCertificateCustomValidationCallbackonHttpClientHandlerto bypass SSL validation during development. DangerousAcceptAnyServerCertificateValidatoraccepts all certificates. Use it only for local testing.- Write custom validation to accept certificates by thumbprint or domain for more controlled bypass.
- In ASP.NET Core, configure SSL bypass through
IHttpClientFactorywith named clients. - Gate the bypass behind
IsDevelopment()or a configuration flag that is absent in production. - The proper production fix is trusting the certificate in the OS store or using
dotnet dev-certs https --trust. - Never deploy SSL bypass to production. It completely negates the protection HTTPS provides.
Related reading
- Amazon API Gateway in front of ELB and ECS Cluster
- Amazon API gateway timeout
- Amazon AWS Route 53 Hosted Zone does not work
- Amazon ec2 not working when accessing through public IP
- Am I trying to connect to a TLS-enabled daemon without TLS?
- Amazon Cognito A client attempted to write unauthorized attribute
- Amazon ELB for EC2 instances in private subnet in VPC
- Amazon ELB in VPC

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.