How not to serve https to certain client IPs?

1. Output of caddy version: 2.5.2

2. How I run Caddy:

Container. Image: Works just fine (“caddy run…”).

a. System environment:

Podman on Debian.

b. Command:

Paste command here.

c. Service/unit/compose file:

Paste full file contents here.
Make sure backticks stay on their own lines,
and the post looks nice in the preview pane. -->

d. My complete Caddy config:

Slightly anonymized:

s1.dom.invalid {
tls ssl@dom.invalid {
dns cloudflare “token”

@localNetworks {
route @localNetworks {
    reverse_proxy *
respond "404 Not found." 404


Paste your config here, replacing this text.
Use `caddy fmt` to make it readable.
DO NOT REDACT anything except credentials.
Make sure the backticks stay on their own lines.

3. The problem I’m having:

I run a bunch of sites on Caddy. For some of them, I’d like to keep the https and certificate un-exposed - unless requested from certain IPs.

How to do this?
I’m getting the sneaking notion it’s not supposed to be done.

4. Error messages and/or full log output:

Paste logs/commands/output here.

5. What I already tried:

Mostly just staring at the documentation to no avail.
I’m not sure what to look for.

It seems that once a request to a configured sitename hits the Caddy the certificate is exchanged… which is perfectly fine for most of the sites on Caddy - but not all.
For specific sites I just want only clients from the local network to even see the certificate.
I have other sites on the same Caddy where certificates are for all to see and even the existence of a given hostname in Caddy’s config is OK.

Again, everything works for now… except for the small thing about “https only for select clients”.

So how to avoid interacting with a https request with the correct hostname but from the “wrong” client IP?

6. Links to relevant resources:

Not sure why you prefer HTTP access from other network except local, but give this a try
ADD “remote_ip” to your config

s1.dom.invalid {
    tls ssl@dom.invalid {
        dns cloudflare “token”

also HTTP access for other network

http://s1.dom.invalid {
    reverse_proxy *
1 Like

Arh… But off course :wink:
I’ll try that, thanks.

It is not because I want to serve the site via http instead.
Rather, I want to keep that site hidden in all ways except for the intended.
If the site is defined in the configuration, I get a proper certificate for that site the way my config looks like now (which leaks its existence).
Basically, I want to block or dismantle the request before certificate exchange takes place so the existence of the site isn’t leaked.

1 Like

In my opinion, exposing HTTP is WORSE.
I rather just have HTTPS. And thanks to Caddy is all possible.

1 Like

Sticking remote_ip in there at the top does not parse.
Your idea, however, gave me a new view.

From the documentation on remote_ip:

@denied not remote_ip private_ranges
abort @denied

This gives an “error” but not the same error as a truly unconfigured hostname.

I think that perhaps it would be better to run another Caddy container to handle “internal sites” so that configured sites will not leak (and no; this is not security by obscurity - it’s more a question of keeping things tight). This is done through forwarding rules to :80 and :443 in the firewall based on source IP.

My desire is still to get a request for an “internal site” answered the exact same way as a non-configured name (not in Caddyfile), i.e. the same error.

1 Like

This is not about exposing http. It is about not telegraphing the existence of a certain hostname/sitename to an external client.

Okay, you might have your reason you don’t want the world to know your have a website call by getting SSL certificate from public trusted certificate authority. But that doesn’t mean you have to settle for less security by using HTTP. There’re few ways you can get around it.

If you setup secret.local or top.secret.local , it will use locally-trusted CA to produce certificates for the site. secret.local or top.secret.local will not known to external world. But your browser will prompt insecure https connection. {
    tls internal
} {
    tls internal

Option 2: wildcard certificate
You can create an empty shell frontpage with error 404 for outside world, but still keep your LAN HTTPS happy for other services, without publish your subdomain ( only the main domain ). Caddy will only obtain 1 time SSL certificate


*, {
	@app1 host
	handle @app1 {

	@app2 host
	handle @app2 {
		reverse_proxy {
			transport http {

	@app3 host
	handle @app3 {
		reverse_proxy homer:8080

    root * /usr/local/www/empty-folder
    respone 404

Option 3 : Disable WAN 443 port
Disable all port 443 forwarding from WAN side. I believe you are still able to get SSL certificate with just port 80 open. In this case, only internal LAN will be able to access site through HTTPS

Or if you really paranoid, you can combine all 3 option into one :slight_smile:

That’s not possible with Caddy itself, because for Caddy to handle the request, it must have solved the TLS handshake.

Security through obscurity is not a useful pattern. Security through obscurity - Wikipedia

You can use the abort directive to close the connection as soon as possible when a certain request matcher matches, but people will still know something exists there because the TLS handshake succeeded before Caddy could cancel the connection.

What you can do instead is use firewall software to block connections before they even reach Caddy.

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