Every ASP.NET Core template serves HTTPS on localhost, and it works on a Windows PC because a certificate you never created is trusted by a store you never opened. This part reads that certificate on the host without changing anything, serves a page over it, then moves to places where it does not exist: a throwaway WSL distribution with a fresh SDK, where the certificate is created, refused by curl, exported and trusted the Ubuntu way, and a container, where the image switches certificate generation off. The outputs are quoted, and the host's certificate stores were compared before and after: unchanged.

Steps in this part
  1. dotnet dev-certs https --check --trust on Windows, then Get-ChildItem Cert:\CurrentUser\My and Cert:\CurrentUser\Root filtered to the ASP.NET Core certificates
  2. dotnet new web, run the https profile, curl.exe -sS -D - -o NUL https://localhost:15960/ without -k
  3. In a throwaway WSL distribution (wsl --install Ubuntu-24.04 --name ds-p6 --no-launch), install the SDK with dotnet-install.sh and run the same app over HTTPS
  4. Move the certificate out of ~/.dotnet/corefx/cryptography/x509stores/my and start the app: the "Unable to configure HTTPS endpoint" error
  5. dotnet dev-certs https, then curl with and without -k
  6. Export with -ep ~/.aspnet/https/aspnetcore.crt --format PEM, copy to /usr/local/share/ca-certificates, sudo update-ca-certificates, curl again
  7. From Windows, curl the WSL app on localhost; then the same app in the sdk:10.0 container

What Windows already has

2 trusted certificates were found: 2 certificates

That is dotnet dev-certs https --check --trust on this PC. The certificate store told the longer story: six ASP.NET Core development certificates in the personal store, four of them expired, and the same six in the trusted root store next to an IIS Express certificate. Each is self-signed with CN=localhost, each is valid for exactly one year, and nothing removes the old ones. The documentation's two sentences fit what the store showed: "Installing the .NET SDK installs the ASP.NET Core HTTPS development certificate to the local user certificate store. The certificate is installed, but it isn't trusted." Trusting it, the one-time --trust, is what put the copies in the root store, and the newest valid one is the one Kestrel served: a web app from the template listened on https://localhost:15960, and curl.exe from Windows got HTTP/1.1 200 OK without -k. One trap for the curious: Windows curl uses Schannel and prints no certificate details with -v; the served certificate was read with a short .NET SslStream call instead.

A fresh SDK in WSL, and the first surprise

In the throwaway distribution the SDK came from the documented script, dotnet-install.sh --channel 10.0, which installed 10.0.402 into the home folder. The plan was to show an SDK with no certificate, and the very first SDK command spoiled it:

Installed an ASP.NET Core HTTPS development certificate.
To trust the certificate, run 'dotnet dev-certs https --trust'

The SDK's first-run step creates the certificate on Linux too, so the app started over HTTPS at once, with Kestrel's warning that the certificate "is not trusted". To see the real no-certificate state the .pfx was moved out of the SDK's store folder, and then:

No valid certificate found.
System.InvalidOperationException: Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found or is out of date.
To generate a developer certificate run 'dotnet dev-certs https'. To trust the certificate (Windows and macOS only) run 'dotnet dev-certs https --trust'.

That is the error in the Fixes post, and the fix it names worked here too: dotnet dev-certs https printed "The HTTPS developer certificate was generated successfully." and the app listened again. Note the message's own claim that trust is for Windows and macOS only; the current documentation says Ubuntu and Fedora are supported, and the next section does it by hand.

Trusting it on Ubuntu, by hand

With a new, untrusted certificate, curl inside the distribution refused:

curl: (60) SSL certificate problem: self-signed certificate

curl -k got the page. The documented route for Ubuntu is to export the certificate as PEM and hand it to the system store: dotnet dev-certs https -ep ~/.aspnet/https/aspnetcore.crt --format PEM, sudo cp into /usr/local/share/ca-certificates/, and sudo update-ca-certificates, which answered 1 added, 0 removed; done. After that, curl -sS https://localhost:15962/ printed Hello World! with no flag. Two things to know. The export is written with mode 600 and sudo cp keeps it, so run sudo chmod 644 on the copy, or a lookup by directory fails for the normal user while curl, which reads the bundle file, still works. And the tool's own check never agreed:

The following certificates were found, but none of them is trusted: 1 certificate

dotnet dev-certs https --check --trust kept printing that, with exit code 7, after the system store trusted the certificate, and Kestrel's warning changed from "is not trusted" to "is only trusted by some clients". The tool looks for its own trust marker, not the system bundle; take the curl result as the truth for curl, and the tool's as the truth for the tool. The documented alternative, --trust on Linux with SSL_CERT_DIR, was deliberately not run: the .NET 10 documentation says that inside WSL it "also trusts the certificate on the Windows host", which this run had promised not to touch.

Across the boundary, and in a container

With the WSL app still running, Windows reached it on localhost, and refused the certificate:

curl: (60) schannel: SEC_E_UNTRUSTED_ROOT (0x80090325) - The certificate chain was issued by an authority that is not trusted.

Localhost forwarding works; trust does not cross. Windows trusts its own certificates and the distribution its own, and the documentation puts it the same way: development certificates "are stored and trusted separately for each user." The container is stricter still. The sdk:10.0 image sets DOTNET_GENERATE_ASPNET_CERTIFICATE=false, so a web app run inside it with an HTTPS URL fails with the same exception, and dotnet dev-certs https first makes it listen on https://[::]:15963, in Production, with a certificate that is new on every container start and trusted by nobody, not even curl in the same container. The documentation's warning is explicit: "Don't create a development certificate in an environment planned for redistribution, such as a container image or virtual machine." That is why the series' Part 7 image speaks plain HTTP on 8080 and leaves TLS to a reverse proxy, and why the documentation adds that behind a proxy you "use forwarded headers middleware before calling HTTPS redirection middleware."

The short version: on Windows run dotnet dev-certs https --trust once a year and delete the expired copies when the store gets long; in WSL export and trust the distribution's own certificate; in containers do not do HTTPS at all.

Frequently asked

Why does my ASP.NET Core app say the developer certificate could not be found?
There is no valid ASP.NET Core HTTPS development certificate where the SDK looks: a fresh Linux install before any SDK command, a container image that sets DOTNET_GENERATE_ASPNET_CERTIFICATE=false, or an expired one. Run dotnet dev-certs https to create one; on Windows add --trust so browsers and curl accept it. Each certificate is valid for one year.
How do I trust the ASP.NET Core development certificate in WSL?
Trust is per user and per system, so the Windows certificate does not help inside WSL. Create one in the distribution with dotnet dev-certs https, export it with -ep and --format PEM, copy it to /usr/local/share/ca-certificates, chmod 644 it, and run sudo update-ca-certificates; curl then accepts it. The tool's own --check --trust still reports it untrusted, because it looks for its own marker.
Should a Docker container use the ASP.NET Core developer certificate?
No. The certificate made inside a container is new on every start and trusted by nothing, and the documentation warns against creating one in an image. Run the app on plain HTTP inside the container and terminate TLS at a reverse proxy or load balancer with a real certificate, with forwarded headers middleware in the app.

That closes the series. The next step for the PC you have just set up is the Docker for .NET Developers on Windows series, which starts where Part 4 left off.