Enabling HTTPS for your Pi-hole Web Interface

I followed this article Adding HTTP certificates to Pi-Hole with LEGO
I hope it will be helpful to others here, too

1 Like

A post was split to a new topic: External.conf stopped working

I followed the instruction but the lighttpd service cannot restart after I had created the external.conf as described above.

In the scrept I replaced the red fqdn with my domain name.
As there is no combined.pem in /etc/lighttpd I copied the file from /etc/letsencrypt/live/pihole.example.com/

Is there anything else there has to be done before or after creating external.conf

I had achieved everything until creating privkey.pem, cert.pem and combined.pem in
/etc/letsencrypt/live/pihole.example.com/

Hi, I have been running Pi-hole on a Debian 10 mini-PC for the last couple of years and had SSL enabled for the last few months. A recent OS update caused Pi-hole to stop working and no matter what I did I could not get it back up and running. I bit the bullet and installed Debian 12 onto the PC as a clean install and then re-installed Pi-hole, and all was working again. Fortunately, I had a backup from my Pi-hole configuration via transporter from when I did the last update, and nothing had changed so it was running again quite quickly.

When I tried to enable SSL I hit problem. No matter what I did my old configuration would not work. I found that lighttpd on Debian 12 is now 1.4.69 and there have been some chanages to what is included and how it is configured. It is no longer possible to use external.conf in the /etc/lighttpd directory to load the SSL configuration. The configuration must be loaded from /etc/lighttpd/conf-enabled, however, to ensure that my configuration does not get overwritten at a later date I still created my configuration in /etc/lighttpd and then created a link to it in /etc/lighttpd/conf-enabled.

I have a local SSL configuration, not using LetsEncrypt so use a combined.pem file.
The configuration I ended up with is based on info found here and at:

Here are the steps I followed and my config which may help someone facing the same issue.

Install lighttpd's openssl module.

sudo apt-get install lighttpd-mod-openssl

Create a directory for the combined certificate.

mkdir /etc/cert

Upload combined.pem to /ect/cert.
Set permissions on the certificate file and directory

sudo chown root /etc/cert/combined.pem
sudo chown :root /etc/cert/combined.pem
sudo chown www-data -R /etc/cert

Create the lighttpd SSL configuration file. Named so that it is loaded in the correct order by lighttpd

sudo touch /etc/lighttpd/10-ssl.conf

Edit 10-ssl.conf and add the following:

# turn on ssl #
server.modules += (
  "mod_openssl"
)

$HTTP["host"] == "<your-server-fqdn>" {
    # Ensure the Pi-hole Block Page knows that this is not a blocked domain
    setenv.add-environment = ("fqdn" => "true")
	
	$SERVER["socket"] == "0.0.0.0:443" {
		ssl.engine = "enable"
		ssl.disable-client-renegotiation = "enable"
		ssl.pemfile = "/etc/cert/combined.pem"

		# ECDH/ECDHE ciphers curve strength 
		ssl.ec-curve = "secp384r1"

		ssl.use-compression = "disable"

		# Environment flag for HTTPS enabled
		setenv.add-environment = (
			"HTTPS" => "on"
		)
		
		ssl.use-sslv2 = "disable"
		ssl.use-sslv3 = "disable"
		ssl.honor-cipher-order = "enable"
		ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"

		# HSTS(15768000 seconds = 6 months)
		setenv.add-response-header = (
			"Strict-Transport-Security" => "max-age=15768000;"
		)
	}
	
	# Redirect all HTTP traffic to HTTPS
    $HTTP["scheme"] == "http" {
        $HTTP["host"] =~ ".*" {
            url.redirect = (".*" => "https://%0$0")
        }
    }
    # Auto redirect to admin panel when hitting base URL
    url.redirect = ("^/$" => "/admin" )
}

Set permissions on the SSL configuration file (may not be needed but want to be sure)

sudo chown 644 /etc/lighttpd/10-ssl.conf
sudo chown root /etc/lighttpd/10-ssl.conf
sudo chown :root /etc/lighttpd/10-ssl.conf

Create a link to the configuration file in /etc/lighttpd/conf-enabled and restart the lighttpd service.

sudo ln -s /etc/lighttpd/10-ssl.conf /etc/lighttpd/conf-enabled/10-ssl.conf
sudo service lighttpd restart
3 Likes

if using docker how we should do this? Asking because whenever the container is restarted all this configuration will have to be re-done, isn't it?

Hello Dan. I know this post, along with your reply, is pretty dated, but I was hoping to get some clarification. At what point in the OP's directions should I deviate and follow your safer installation instructions? There's a lot of comments/corrections in the comments and I have gotten lost and can't get this to work. I have started over and hope to get some clarification. Thanks.

The docker approach would be to use a reverse proxy container in front to handle the TLS termination.

1 Like

This entire process would be so much easier if pi-hole just allowed you to connect to the web interface with a port number alongside the IP of the device right out of the box, instead of needing to start poking around in config files. I say that because if that were possible, you could just use Nginx Proxy Manager which is the standard for reverse proxy and attach a letsencrypt SSL cert to your FQDN.

AdGuard Home does this right out of the box, and I feel pi-hole should as well. Why am I forced to use /admin/ instead of using a port number behind the IP address of the device still puzzles me.

That makes no sense, sorry. You would be running a Docker instance for NPM with a domain name configured and LE SSL installed, but somehow it wouldn't be too easy because a Pi-hole configuration file needed editing, alongside all the other configuration files that also need editing?

In my experience Pi-hole's installation and operation works fine out of the box for use with what you might call a typical home or hobbyist setup.

For use in a more advanced setting, such as non-typical networking or clustering, one would expect there to be a level of configuration file editing and admin involved for all aspects of that setup.

The /admin redirect went away with the dropping of the block page. I think (not sure) that Pi-hole v6 will introduce new customisation options, but the current v5 behaviour is a result of the interplay between the various components, as well as the way that the web server is sometimes used by people for other purposes outside of Pi-hole.

All I know is this for certain. When I install AdGuard Home on my Raspberry Pi device using the dietpi OS, I am able to connect to my AdGuard homes web interface using the Pi device IP address followed by the port number and I can also use the IP with /admin/. With Pihole I am forced to use /admin/ only.

This is not a problem for you, but it is for me. How am I supposed to get Nginx Proxy Manager to redirect my FQDN if it requires a port with the IP and doesn't accept /admin/. I need this so I can use my SSL cert in NPM to apply to the pihole redirect.

I have been asking for help to figure this out for the past 2–3 days and I still don't have a clear answer, nor did it seem like the team member who has been speaking to me has any experience with this. I don't understand, what's so difficult to understand about this?
Very simple. How do I access pihole web interface with a port number behind the IP? If it can't be done then please pass this along to the developers because it very much should be possible and would be great if it was considered for version 6. Self-hosted services should always have the ability to access web interfaces with a port number. Too many users have NPM these days to ignore that.

As it stands right now I can use the 'local DNS records' option but this is HTTP only so I can't use my SSL cert from NPM.

Am I making any sense? I mentioned this in a self-hosting discord and Subreddit, and everyone there understands what I am saying.

See what I mean? I have to use a port number in NPM. It has to be the port to the actual web interface.

The answer has been out there since 2016.

There's also discussions about how to do this dating back to 2021 and earlier.

1 Like

jfb helped me figure out my unusual scenario in a different thread. Thanks though!

The lighttpd configuration originally suggested in 2017 can be simplified considerably. It's also no longer necessary to concatenate the cert chain with the private key, as lighttpd now knows how to read them as separate files, which certbot and others provide directly. I ended up with:

server.modules += ( "mod_openssl", "mod_redirect" )

# Set my fully-qualified domain name in a variable for convenience
var.fqdn = "pihole.example.com"

$SERVER["socket"] == ":443" {
    ssl.engine = "enable" 
    # Public cert and intermediate cert chain
    ssl.pemfile = "/etc/letsencrypt/live/" + fqdn + "/fullchain.pem"
    ssl.privkey = "/etc/letsencrypt/live/" + fqdn + "/privkey.pem" 
    # Require TLS 1.3
    ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.3")
}

$HTTP["host"] == fqdn {
    # Set redirect code for any redirects we do
    url.redirect-code = 308
    # Redirect all http to https
    $HTTP["scheme"] == "http" {
        url.redirect = ("" => "https://" + fqdn + "${url.path}${qsa}")
    # Redirect root to admin
    } else $HTTP["url"] == "/" {
        url.redirect = ("" => "/admin/")
    } 
}

Just as an additional remark, I stumbled across this guide but wasn't able to get ssl working. I noticed with the more recent versions there are more steps necessary. I have put them together here:

  • The openssl module is not part of the default installation anymore, you need to manually install it:
    sudo apt-get reinstall lighttpd-mod-openssl
  • Add an ssl folder in your lighttpd folder:
sudo mkdir etc/lighttpd/ssl
  • Copy your fullchain.pem and privkey.pem to your ssl folder (its not necessary to create a combined .pem anymore)
  • Change the owner of the files to be read by lighttpd
sudo chown www-data -R etc/lighttpd/ssl
  • Create your external.conf with an editor of your choice directly in the folder conf-enabled:
  • sudo nano /etc/lighttpd/conf-enabled/external.conf
  • I used the config of ravron , thanks for that
server.modules += ( "mod_openssl" )

var.fqdn = "ENTER_YOUR_DOMAIN_HERE"

$SERVER["socket"] == ":443" {
    ssl.engine = "enable"
    # Public cert and intermediate cert chain
    ssl.pemfile = "/etc/lighttpd/ssl/fullchain.pem"
    ssl.privkey = "/etc/lighttpd/ssl/privkey.pem"
    ssl.ca-file = "/etc/lighttpd/ssl/fullchain.pem"
    # Require TLS 1.3
    ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.3")
}

$HTTP["host"] == fqdn {
    # Set redirect code for any redirects we do
    url.redirect-code = 308
    # Redirect all http to https
    $HTTP["scheme"] == "http" {
        url.redirect = ("" => "https://" + fqdn + "${url.path}${qsa}")
    # Redirect root to admin
    } else $HTTP["url"] == "/" {
        url.redirect = ("" => "/admin/")
    }
}

In my case, the 10-ssl.conf file in the conf-enabled folder was causing issues with my config, so i removed it:

sudo rm etc/lighttpd/conf-enabled/10-ssl.conf

Finally, test your config with

sudo /usr/sbin/lighttpd -tt -f /etc/lighttpd/lighttpd.conf

If no error occured, the output will be empty.
Then you can restart your service:

sudo service lighttpd restart
1 Like

Thank you, that worked for me after a fresh Pi-Hole install.

Hi together,

thanks WaLLy3K for the initial guide and ravron/moPi for the update!

I have the following external.conf

server.modules += ( "mod_openssl" )

$SERVER["socket"] == ":8443" {
  $HTTP["scheme"] == "http" {
    url.redirect = ( "^/(.*)" => "https://%0$0" )
  }

  $HTTP["url"] == "/" {
    url.redirect = ( "^/$" => "/admin/" )
  }

  ssl.engine = "enable"
  ssl.pemfile = "/etc/lighttpd/server.crt"
  ssl.privkey = "/etc/lighttpd/server.key" 
  ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.3")
}

So far everything is working great, I can access pi-hole at https://[server-ip]:8443. I have no experience with lighttpd but I was trying to forward any http request on 8443 to https. unfortunately this seems not to work :frowning:

In nginx I would do:

server {
    listen       8443 ssl;
    listen  [::]:8443 ssl;
    server_name  localhost;
    
    ssl_certificate /etc/nginx/certs/server.crt;
    ssl_certificate_key /etc/nginx/certs/server.key;
    
    error_page 497  301 =307 https://$host:$server_port$request_uri;
    
    location / {
        root   /usr/share/nginx/mypage;
        index  index.html index.htm;
    }
}

Is this possible with lighttpd? If so, how?

Thanks in advance! :slight_smile:

I found the lightttpd documentation to be quite useful.

For http to https redirect: here is their site's link: