Can PiHole act as a DoH or DoT server?

I just set up Pi-hole on my NAS and it seems to be working well. However, my iPhone is complaining:

This network is blocking encrypted DNS traffic.

The names of websites and other servers your device accesses on this network may be monitored and recorded by other devices on this network.

I understand that this is because Pi-hole only provides standard DNS service over port 53, and shouldn't be a problem assuming my home network isn't teaming with hackers. However, I would like to know if there's a way to allow Pi-hole to act as a DNS over HTTPS or TLS server. I have a self-signed certificate that the devices on my network trust (I like to play sysadmin) that could be presumably used to issue a certificate for use with DNS responses. Is this something supported by Pi-hole/its underlying components, or has nobody bothered because it's pointless? Some browsers seem to prefer encrypted DNS and, I've read, may try to bypass Pi-hole because of this. This is a feature AdGuard claims to have on their Github readme.

When I search for this online, I can only find references to enabling upstream DNS over HTTPS/TLS using unbound or cloudflared, which I've tried and does work (though I'm staying with unbound's recursive queries instead), but that's different.

Not easily. You would need to install a front end proxy to receive the encrypted DNS queries and then forward them to Pi-hole in a format Pi-hole understands.

You might take a look at this project: doh-proxy 0.0.9 . I have used it to provide DoH for Firefox for quite a while now.

1 Like

I think I've figured it out. I've gotten DoH requests to work using the Pi-hole's mDNS name and IP address, and my iPhone no longer has the security warning. I'll make a tutorial here on what I did so others can do the same if they want.

  1. Install this DoH proxy. I put it in /usr/local/bin and made sure it was owned by root (sudo chown root:root). The DNScrypt people also have a proxy for the DNScrypt protocol, but I didn't bother because as far as I know none of my devices support it.

  2. Make a new user to run the proxy as so it doesn't have to run as root.
    sudo useradd -s /usr/sbin/nologin -r -M doh-proxy

  3. Make a systemd service to keep it running. As root, put the following in /etc/systemd/system/doh-proxy.service

Description=DNS over HTTPS server proxy

ExecStart=/usr/local/bin/doh-proxy -u

  1. Start the systemd service
sudo systemctl enable doh-proxy
sudo systemctl start doh-proxy
sudo systemctl status doh-proxy

This will start doh-proxy listening on localhost port 3000. It will convert these requests to standard DNS queries which it will then go to Pi-hole to resolve using the usual port 53.

  1. Issue a certificate to be used with DoH. This tutorial is good. Make sure to add your Pi-hole's static IP address to the .ext file when creating the certificate (the stuff under alt_names). Transfer the certificates to your Pi-hole machine somehow (scp is good). Set your devices to trust this certificate (and keep the certificate key safe!). Any devices that don't trust it shouldn't be able to use DoH (that's the whole point).
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

DNS.1 = pihole.local
IP.1 =
  1. Prepare the certificate files for lighttpd. Concatenate the key and certificates into one file using cat pihole.crt pihole.key > pihole.pem. It's a good idea to make all these files owned and only readable by root: sudo chown root:root pihole.*, sudo chmod 600 pihole.*

  2. We now need to modify Pi-hole's web serve (lighttpd) to use HTTPS and to forward requests to the standard /dns-request URL to doh-proxy. Pi-hole allows you to do this by editing /etc/lighttpd/external.conf. Make sure to enter the location you decided to put your .pem file.

# Load necessary lighttpd modules
server.modules += ("mod_openssl", "mod_proxy")

# Forward DoH DNS queries to doh-proxy
proxy.server = ("/dns-query" => ( (
  "host" => "",
  "port" => 3000
) ) )

$HTTP["remoteip"] != "" {
  # Ensure the Pi-hole Block Page knows that this is not a blocked domain
  setenv.add-environment = ("fqdn" => "true")

  # Enable the SSL engine when using port 443
  $SERVER["socket"] == ":443" {
    ssl.engine = "enable"
    ssl.pemfile = "/path/to/your/pihole.pem"
    ssl.honor-cipher-order = "enable"
    ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"

  # Redirect HTTP to HTTPS
  $HTTP["scheme"] == "http" {
    $HTTP["host"] =~ ".*" {
      url.redirect = (".*" => "https://%0$0")
  1. Restart lighttpd.
    sudo systemctl restart lighttpd

  2. Check that it's working (use Pi-hole's IP or hostname). This tool is very useful for testing all the different DNS protocols.
    dnslookup https://pihole.local/dns-query

  3. (optional) lighttpd will sometimes log the URLs of the queries, which includes the base64 representation of the cleartext DNS query in the URL's parameters. If you don't want this, the best way seems to be to symlink the log file to /dev/null so that the logs are never saved. Of course, you then won't have any logs of access to the admin panel, if you care.
    sudo ln -sf /dev/null /var/log/lighttpd/access.log

Now, DoH queries will take the following path.
[/dns-query, HTTPS port 443] -> lighttpd -> [HTTP port 3000] -> doh-proxy -> [DNS port 53] -> Pi-hole -> [cache or DNS port 53]

You can follow this tutorial to add another proxy to Pi-hole's outgoing requests so that they are either over HTTPS as well, or to do recursive lookups. Using unbound for this would get quite the chain of stuff going on.
[/dns-query, HTTPS port 443] -> lighttpd -> [HTTP port 3000] -> doh-proxy -> [DNS port 53] -> Pi-hole -> [cache or DNS port 5335] -> unbound -> [DNS port 53]

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.