HTTP localhost ONLY working on port definitions not literal address

1. Caddy version (2.4.3):

2. How I run Caddy:

caddy run --watch

a. System environment:

Ubuntu 20.04.2 LTS

b. Command:

caddy run --watch

c. Service/unit/compose file: NA

d. My complete Caddyfile or JSON config:

  1. None functioning Caddyfile

    • If I use this setting and access the localhost explicitly by http://localhost I get a blank page, no errors written in the log file nor shown on the by the browser when a web browser is used.
    • however if I do curl -v http://localhost the index.html gets served with identical output for both Caddyfile …
	auto_https off
http://localhost:80 {
	log {
		output file /etc/caddy/test/site/caddy.log

	root * /etc/caddy/test/site

  1. Functioning Caddyfile

    • If I use this setting and access the localhost explicitly by writing http://localhost on the web browser, the web page loads and the logger outputs in to the log file as expected.
	auto_https off

:80 {
	 log {
	        output file /etc/caddy/test/site/caddy.log

	root * /etc/caddy/test/site

3. The problem I’m having:

Basically I am trying to host local websites for dev/prototype purposes. I found myself not being able to access the site through the https, getting SSL certificate error in Firefox when I try to access my local server no matter what I do, even this won’t work:

respond "hello"

As a result I decided to turn off auto_https as it was not necessary for my needs, and followed the https guidance on the wiki here, it only works if I only specify the port of my choice otherwise I get an empty page…

  1. How do I access https://localhost without getting any error? I really tried everything on the forum and searched everywhere. I thought https/TLS was suppose to be all magical and automatic with caddy…
  2. Why is the website not loaded when I use http://localhost:80 vs :80 as the domain address

4. Error messages and/or full log output:

  • Firefox output

  • curl -v for BOTH configurations
* Trying
* Connected to localhost ( port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.68.0
> Accept: */*
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 23
< Content-Type: text/html; charset=utf-8
< Etag: "qygj0tn"
< Last-Modified: Thu, 26 Aug 2021 17:24:29 GMT
< Server: Caddy
< Date: Thu, 26 Aug 2021 22:43:16 GMT
<h1>hello world</h1>
* Connection #0 to host localhost left intact

5. What I already tried:

  • Making sense of `auto_https` and why disabling it still serves HTTPS instead of HTTP
  • all ports used by caddy are open on my machine, including the :80 and :443 (I used ufw to enable them)
  • I used sudo setcap 'cap_net_bind_service=+ep' /usr/bin/caddy to give caddy access to the privileged ports so that I could use https|:443 initially which it didn’t work (browsers giving error about the certificate…) but at least I could access the http port if I only use :80 as the domain.
  • I even allowed caddy to have read/write/execute access to all the files involved just in case if that was the issue (which I believe is very unlikely as the server works on other occasions)

6. Links to relevant resources:

The only reason this would be the case is if the browser wasn’t sending the localhost Host header in the request. I don’t see a reason why it wouldn’t.

If you made a request with an IP address instead of localhost, then it would not match. Site addresses are essentially sugar for the header matcher, plus wiring up per-hostname logs and certificate automation.

When you use :80, it essentially omits the header matcher altogether, and matches any request on port 80, regardless of the Host header.

Are you making the request from a different machine? Or are you doing this on a Linux desktop?

One of the aspects of TLS is trust. The error you’re seeing there is because the browser doesn’t trust the certificate Caddy served to it.

When Caddy tries to enable its local CA for issuing certificates, it will attempt to install the root CA certificate into your system and browser’s trust stores (note that Firefox in particular doesn’t use the system’s trust store, it has its own). This isn’t fool-proof though, because it requires sudo permissions to perform the installation, since the trust stores are usually in protected directories.

To manually run Caddy’s trust installation, you can run a command like this, where your_user is the user as which you’re actually running Caddy:

sudo HOME=~your_user caddy trust

What this does is run caddy trust as root, but overrides the HOME environment variable to the home of your_user (the ~ is a shortcut for getting the home of a particular user, if no username follows then it assumes your current user) so that Caddy can read from the location where the certs and keys actually are, but with elevated permissions so it can copy them where they need to go.

If you see warnings about libnss, then you should install those packages because nss is actually Firefox’s trust store service, and those libraries are needed so that Caddy can install the certificate for Firefox.

After that, you should good to go for local HTTPS.

It just requires that one-time setup step of trust, which depending on your environment and how your run Caddy, can be kinda complicated.


Thank you so much for your help, It actually demystified so many topics! As you probably realised I am a newbie to servers, and caddy seemed like a dream come true and I am just dipping my toes in this topic! :innocent:

Yes! that is correct, I have a spare laptop that I am using as my local server controlled by SSH, It had GNOME so I followed all your steps on the laptop and as a matter of fact both HTTP and HTTPS started working like a charm! :grin:

Of course the whole thing raised more questions for me in terms of understanding the fundamentals… :thinking:

  1. As the HTTPS started working locally, I decided to do the following so that the server works when I try to access using the local WLAN IP address:
:443, 80 {
	log {
		output file /etc/caddy/test/site/caddy.log

	root * /etc/caddy/test/site

Interestingly, I was able to access the the server through HTTP port, but HTTPS gave me the same error SSL_ERROR_INTERNAL.... on Firefox.

But when I changed the address in the config file to my WLAN address I was able to access the local server from other devices in the network on HTTP and HTTPS port… why is that? :thinking: they should sort of mean the same thing? {
	log {
		output file /etc/caddy/test/site/caddy.log

	root * /etc/caddy/test/site

This was not a smooth outcome though, I received this error, which is fine to be honest, as I can force safari or Firefox to forcefully trust the certificate but is there BETTER/CORRECT way to set up a local server on a local machine to be accessed via their WLAN or what I have done is the best that can be achieved with this set up, until of course I start serving the web app on a public server, with Let’s Encrypt and proper domain name?

I however checked my caddy log which had this error:

2021/08/30 20:24:34.420	ERROR	failed to install root certificate	{"error": "certificate cannot be installed in NSS security databases", "certificate_file": "storage:pki/authorities/local/root.crt"}

I decided to run the caddy run with sudo caddy run which sorted the error in the log (I am not sure how safe is this practice though, running caddy with sudo. Why is this even necessary for initiating the server?:man_shrugging:) I thought running sudo caddy trust was all that was needed only ever once as per the documentation. Regardless this did not solve the certificate issue when I was trying to access the local server, I got the same error as shown in the image above; even though the root certificate was successfully installed successfully this time. :grimacing:

  1. On a side note, I was reading about the valid addresses on caddy documentation, I came across this:
    Valid Addresses

This is something I have never come across :alien:… Is there a link to wiki or something to explain more about these kind of addresses?:flying_saucer:

So sorry for the long thread this has become, I seem to be an expert in over-complicating life! :see_no_evil:

Caddy only issues certificates for domains it knows about, as listed in the config. If you just use :443 then it doesn’t know about the domains ahead of time, so it can’t issue any certificates.

BUT, Caddy does have an innovative feature, On-Demand TLS. This is opt-in, because there are some caveats to it (avenues for abuse) if limits aren’t put in place. If you turn on On-Demand, then Caddy will issue certificates on the fly for domains in the requests, during the TLS handshake at the start of the connection. If you’re not publicly exposing Caddy, then it’s fine to turn this on without the ask options to limit which domains it allows. Read about it here:

In this case, it counts as an explicitly listed hostname, so Caddy issues a certificate for it at startup. See yours logs.

You can manually install the root certificate onto any device on your local network that needs it. There’s no automated way to do this, because Caddy has no access to those devices. You can find the root CA cert in Caddy’s storage at ~your_user/.local/share/caddy/pki/authorities/local/root.crt

Yeah this isn’t ideal because it will use the root user for certificate storage. But it’s fine if you’re just playing around, I guess.

Like I said earlier, running sudo caddy trust will run using the HOME of the root user, and not your user, so that’s why the command I gave you above overrides the HOME variable.

So that means you’ll have two CAs set up, one in your root user’s HOME, and one in your user’s HOME.

You can ignore the error about NSS when running as non-root, because running sudo HOME=~your_user caddy trust will have already installed it. Caddy tries to install it again anyways but you can ignore it.

That’s the IPv6 equivalent of (i.e. localhost), with port 2015.


Thanks a million for the help! :pray:

1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.