In my previous article/tutorial, I’ve explained how to setup your own DNS-over-HTTPS (DoH) server using Nginx, Certbot, and dns-over-https. In this article I’ll explain to you how to add Pi-Hole into the mix to block the unwanted advertising.


Pi-Hole is made of 2 components: a PHP web interface and a DNS server. Both are open-source. The web interface let you add blacklist, whitelist and configure the DNS server. The DNS server is the one doing the heavy lifting, responding to the queries according to its configuration generated by the interface.

The name comes from the lightness of the application, it can run without problem on a raspberry pi in your own network.


Installing Pi-Hole couldn’t be easier, you need to download their installer and run it.

Check the screenshots before running the command, or simply, be sure to disable lighttpd.

sudo apt install dialog
curl -sSL | bash

If you don’t want to use their installer, you try their docker image.


This section will mostly contain screenshot showing you what to do to configure your Pi-Hole for it to works with the currently installed dnscrypt-proxy.


First select your favorite DNS Server (I choose OpenDNS with ECS)

Choose which blacklist you want. You’ll be able to add your own in the UI later.

You can keep this as default. If your machine doesn’t have IPv6, Pi-Hole will detect it.

To make things easier, I advise you install the Web Admin interface.

We don’t want Lighttpd, since we already have Nginx. We’ll configure Nginx to serve the admin website.

Continue the installation until the last screen where it shows you the full config and admin password.

Before connecting to the UI, we’ll run a configuration command to set the password to what we want.

sudo pihole -a -p

This will ask you to set a new password.


Since we don’t want to use lighttpd as webserver, we’ll have to configure Nginx.


First we need to install PHP-fpm to take care of running the PHP website.

sudo apt install php7.2-fpm php7.2-zip


Put the content of this file into /etc/nginx/sites-available/pihole.

server {
        listen 80;
        listen [::]:80;

        root /var/www/html;
        autoindex off;

        index pihole/index.php index.php index.html index.htm;

        location / {
                expires max;
                try_files $uri $uri/ =404;

        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/run/php/php7.2-fpm.sock;

        location /*.js {
                index pihole/index.js;

        location /admin {
                root /var/www/html;
                index index.php index.html index.htm;

        location ~ /\.ht {
                deny all;

Don’t forget to change the server_name for your domain name.

Then do a symlink to the enabled folder. Ask nginx to check that to configuration works, and reload nginx.

sudo ln -s /etc/nginx/sites-available/pihole /etc/nginx/sites-enabled/pihole
sudo nginx -t
sudo systemctl reload nginx


As before, let’s make this domain an HTTPS one.

sudo certbot --nginx -d

And Voila, the full UI is HTTPS and accessible with Nginx.


There are 2 UI, a public one containing all the stats of the server and the Admin one letting you administrate your Pi-Hole.

Accessible at (Of course, the domain name need to be replaced by the one you are using).


This is the basic UI. Right now everybody can see this but only the Admin can add new blacklist/whitelist and configure the whole solution.


Once you click on Login, you’ll see something different.

You have more detailed statistics about the different requests done on your DNS Server.

As you can see, I’ve just spawn the instance on GCP and I’m already getting scanned for DNS Server. There is a section about firewalling later in this guide. Basically we’re going to block the public port 53, since we don’t need everybody to do request to our Pi-Hole, only doh-server.

Double check the settings to see if the installer did select the right DNS server.

This what it supposed to look like. If it’s not the case, you can correct it.


You can easily test your Pi-Hole server using dig.

dig @
; <<>> DiG 9.11.3-1ubuntu1.1-Ubuntu <<>> @
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18636
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
; EDNS: version: 0, flags:; udp: 4096
;                      IN      A
;; ANSWER SECTION:               2       IN      A
;; Query time: 0 msec
;; WHEN: Wed Oct 03 17:32:34 UTC 2018
;; MSG SIZE  rcvd: 53

This well check for the domain which is in one of the default blacklist.

As you can see the answer for it is which is the default answer for blocked domains.

Blocked Answer Type

By default, Pi-Hole will return for blocked domains. You can change this behaviour as documented on the official website.

Personally I prefer to return NXDOMAIN for blocked domains, making the DNS clients think the domain doesn’t exists.

If you want NXDOMAIN, do this command.

echo "BLOCKINGMODE=NXDOMAIN" | sudo tee /etc/pihole/pihole-FTL.conf
sudo systemctl restart pihole-FTL


Now that you have your Pi-Hole working, we need to reconfigure DoH-Server to use Pi-Hole as a source of DNS instead of dnscrypt-proxy.

# HTTP listen port
listen = [

# TLS certification file
# If left empty, plain-text HTTP will be used.
# You are recommended to leave empty and to use a server load balancer (e.g.
# Caddy, Nginx) and set up TLS there, because this program does not do OCSP
# Stapling, which is necessary for client bootstrapping in a network
# environment with completely no traditional DNS service.
cert = ""

# TLS private key file
key = ""

# HTTP path for resolve application
path = "/dns-query"

# Upstream DNS resolver
# If multiple servers are specified, a random one will be chosen each time.
upstream = [

# Upstream timeout
timeout = 60

# Number of tries if upstream DNS fails
tries = 10

# Only use TCP for DNS query
tcp_only = false

# Enable logging
verbose = false

Change the upstream servers to

Then restart the service.

sudo systemctl restart doh-server

There you go you now have a DoH server with Pi-Hole.


This is great, you have a fully working DoH server. But Pi-Hole like to listen on all interfaces. This is normal since it’s made to be installed on a Raspberry Pi in your own network with only a LAN interface.

But with a server on internet, that can be more than problematic. Your server could be used to DNS Flood other servers. We don’t want that.


We’re going to configure UFW to take care of the firewalling.


First we want all the HTTP and HTTPS traffic to be open on our machine. HTTP is used for by Certbot when validating that the domain belongs to you.

sudo ufw allow http
sudo ufw allow https


Obviously we want to be able to administrate the server.

sudo ufw allow ssh


And now everything else should be blocked on Inbound.

Final Touches

Enabling the firewall

sudo ufw enable

Checking that the rules are set correctly

sudo ufw status verbose

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To                         Action      From
--                         ------      ----
80/tcp                     ALLOW IN    Anywhere                  
443/tcp                    ALLOW IN    Anywhere                  
22/tcp                     ALLOW IN    Anywhere                  
80/tcp (v6)                ALLOW IN    Anywhere (v6)             
443/tcp (v6)               ALLOW IN    Anywhere (v6)             
22/tcp (v6)                ALLOW IN    Anywhere (v6)

Now your server is firewalled correctly. Only HTTP, HTTPS and SSH traffic is accepted.


You have now a DoH server that blocks advertisings. You can use the admin interface of the Pi-Hole to change the whitelist and the blacklists. There are a lot of tutorial online on how to add new blacklist, etc .

Original Post by :