Raspberry Pi Uncategorized

Pi-hole: A Raspberry Pi Ad-Blocker with DNS Caching (Ultra-fast)

This version of the Pi-hole is outdated

Click the image below to read about the new version.


Inspired by the AdTrap, I wanted to make a low-cost, roll-your-own alternative that would neutralize ads before they reach your device.  The Raspberry Pi fit this need.  Unfortunately, the Pi only has 100BaseT (but there are alternatives), which isn’t ideal, but it still ran very fast for me.

The Raspberry Pi runs as a DNS server and redirects queries for advertisements to a local Web server, which will display 1×1 transparent image instead of the ad.

How It Works

Block online ads by using your Raspberry Pi to manipulate advertising URLs.

Network map of Raspberry Pi ad-blocking pi-hole

Another benefit of this is that your other network devices can use it instead of a browser plugin ad-blocker.  This way, your computer doesn’t have to do the processing of blocking the ads, which frees up resources for other applications.

Requirements For This Walkthrough


  1. Local network
  2. Mac or PC
  3. Raspberry Pi running Raspbian “wheezy” running a lighttpd Web server
  4. HDMI Cable (*optional)
  5. Keyboard (*optional)
  6. Mouse (*optional)
  7. Monitor with HDMI input (*optional)
  8. *If the Raspberry Pi is set up as a headless machine, you will not need a monitor, keyboard, or mouse–just another computer, which would be used to access it remotely over the network via SSH.  This is the recommended method.


I have copies of all the files on Github, but in the walkthrough, I show how to make each one.  You can download them if you want, but it is not required as the files will be made in the walkthrough.

  1. Pi-hole


Automated Setup

Instructions are available. These instructions will install a newer version of the Pi-hole, which is different than what is described in this article.

Alternative Methods

In my research, there were a few different ways to do this.  I wanted a way that would be the least disruptive to normal network activity while still providing a cool feature.  Below are some of these methods:

  • Pixelserv (too slow)
  • Apache (too heavy-duty for the Pi)
  • Access point with DNS, DHCP, and Apache (inconvenient, and bloated)

I decided to go with the following for this setup:

  • lighttpd Web server (lighter weight than Apache–better suited for the Pi)
  • dnsmasq  running only DNS (lighter-weight and faster because caching will be enabled)
  • serving requests over eth0  and not wlan0  (faster and more reliable.  It can also be hooked up directly to a router)

Conceptual Overview

  1. Set up a lighttpd Web Server where ads will be redirected to
  2. Install the DNS server software and utilities
  3. Configure DNS
  4. Create a script to redirect ad servers/URLs back to the Pi (and not your device)
  5. Point your device’s DNS server at the Pi-hole [Critical Step]
  6. Test it out!


Install and enable a lighttpd Web server on the Raspberry Pi.

Prepare the Pi

To ensure everything is up-to-date,  run:

sudo apt-get -y update
sudo apt-get -y upgrade

Install DNS

sudo apt-get -y install dnsutils dnsmasq

This will install some DNS troubleshooting utilities like dig , as well as dnsmasq  (the DNS server).

Stop DNS

To make certain it is not running while we modify the files, manually stop the service.

sudo service dnsmasq stop

Edit the DNS Config File

To get a quick view of what options are enabled by default for the dnsmasq  service, run this command:

cat /etc/dnsmasq.conf | grep -v "#" | sed '/^$/d'

This will show all the options that are enabled by parsing out all the comments and blank lines (if it returns nothing, then no options are enabled, which might be the case if this is a brand new install).

We will be making our own file, but this is a nice way to see what is enabled if you happened to already have dnsmasq  running.  The command above can also be applied to other config files with a lot of comments in it.

Since we are going to make our own DNS config file, rename the default one so it is available as a backup in case the system turns unstable:

sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf.orig

Then, create a new file, which will have only the options we want:

sudo vi /etc/dnsmasq.conf

You can either uncomment the options below or just paste the following into the new /etc/dnsmasq.conf file adjusting any values if necessary:

# Uncomment the next line and comment out the remaining lines if you want to use /etc/resolv.conf

The options above are as follows:

  • domain-needed:  never forward names without a dot or domain part
  • interface: specifies that the ethernet port is used
  • min-port: the ports used will always to larger than that specified. This is useful for systems behind firewalls
  • cache-size: set as large as possible (10,000) to allow for super-fast DNS queries
  • log-queries: logs all name resolutions  to /var/log/daemon.log
  • bogus-priv: never forward reverse-lookup queries which have the local subnet’s IP range to upstream
  • no-resolv: do not use /etc/resolv.conf  (see below–this method is a bit easier than using a separate file)
  • server: nameservers to use

Edit the Resolver File

This file sets what servers to use to try to resolve domain names.  dnsmasq  checks this file by default unless you use the no-resolv  option.  Since we are setting up our own DNS server, we can set ourselves as the first one–this will decrease query times.  Edit /etc/resolv.conf:

sudo vi /etc/resolv.conf

There can only be three entries in this file for nameservers, so just add yourself ( and Google’s public DNS servers (for resolving non-ad queries):


This will allow all queries to go through the Raspberry Pi first, and if it cannot resolve the name, it will use Google’s DNS servers.  It is also possible you don’t need this file at all, as if there are no entries, it will just use the default nameserver on the machine.

I have found that this file can get overwritten at reboot, which causes the Pi-hole to stop working.  If you do want to use the /etc/resolv.conf  file, use the following solution from Jeff:

  1. Uncomment and modify /etc/dhcp/dhclient.conf  to make it read: prepend domain-name-servers
  2. Add this additional line in /etc/dnsmasq.conf : strict-order

Open the Black Hole

Next, create a script that will pull known ad URLs from a Website and save them in a file.  This file will tell DNS to re-route any queries to those domains to the IP specified, which for this setup, will be the Raspberry Pi (and not the other devices on the network).  This script will be appropriately named,  as it is pulling in ad URLs.  A trimmed-down version is below, or you can check out the advanced version, which pulls URLs from multiple sources.

Make sure to modify the value for the $piholeIP  variable to be the IP address of your Raspberry Pi ( in my example):

sudo vi /usr/local/bin/
# Address to send ads to (your Raspberry Pi's IP)
curl -s -d mimetype=plaintext -d hostformat=unixhosts | sort | sed '/^$/d' | awk -v "IP=$piholeIP" '{sub(/\r$/,""); print "address=/"$0"/"IP}' > $eventHorizion
service dnsmasq restart

Make the script executable an then manually run it once to verify functionality:

sudo chmod 755 /usr/local/bin/
sudo /usr/local/bin/

If it completed successfully, there will be a new file named /etc/dnsmasq.d/adList.conf .  If you look at this file, it will have a huge list (but not that huge) of ad servers and the IP address– (or whatever you set it to) in this example (the Raspberry Pi).


Now What?

So how does this block ads?  Any file in the /etc/dnsmasq.d  directory is considered a config file and loaded up when dnsmasq  starts.  The syntax of the example line below says “if the address queried is (an ad server) send it to this IP address (the Pi– or whatever you set it to).”


This file is full of these rules, which are loaded when dnsmasq  is.  If a DNS query matches one of the rules, it will send the request to, which is the Raspberry Pi.  Since the request gets redirected there, it won’t reach your other device(s).

DNS is set up to accept queries and re-route ad URLs to an IP address.  The service should have been started when you ran the script.

Create A Recurring Task to Update the Ad List

In case the Website that compiles the list of ad servers ever updates, set  to run once a week using crontab.  This is completely optional, and may be a bit of overkill, but here is is anyway:

sudo crontab -e

Append the following line to the bottom of the file and save it:

@weekly /usr/local/bin/

Now the ad list should always stay up-to-date if the Website it is being pulled from ever updates it.

Verify DNS Functionality

Set Clients To Use The Pi As Their DNS Server

From another device on the network, change your DNS server to point to the Raspberry Pi.  On the Mac, you can change this in the Advanced Network Preferences.

advanced net prefs

Then run the dig  command from that device.  Any ad servers on the list should point to the Raspberry Pi instead of the real address.

Run the dig  command on a Website you have never visited (just to ensure the response is not already cached).  In the example below, I dig a site that I have not been to and is not an ad server.


Take note of two things in the screen shot or the results below.

  1. the Website’s real address is shown
  2. the response time is 64ms.


Now, take a look at the results when I run the same command again.  Thanks to the caching we enabled, the response time is now 6ms; much better!  This happens because we set the option cache=10000  in the /etc/dnsmasq.conf  file.  This cache is stored in RAM and will be flushed whenever the service is restarted.


Next, I dig  a site that is an ad server (and on the adList.conf  file), and the response gets answered by the Raspberry Pi, but the IP that gets returned is not the real IP address of the Website.  Instead, it is the IP address of the Raspberry Pi.



Create A Webpage That Will Pose As the Ad URLs Website

Now that DNS is working and rerouting ad URLs to the Raspberry Pi, we can set up a blank Webpage that will be served (instead of responding with an error message).  After some more testing, I determined the response time is about the same whether you run the Web server or not, so you can decide wether or not to employ it.  However, if you want to replace the ad content with something else, you will want to have the server set up.  Plus, it seems better to serve something instead of continually erroring-out.

First, set up a lighttpd Web server.  The service basically just needs to be turned on and one line added to the config file.

Once your Web server is up and running, create a new directory for the page, download the image.

sudo mkdir -p /var/www/pihole
sudo curl -o /var/www/pihole/pihole.png

Then, edit the file to contain a 1×1 image found here.

sudo vi /var/www/pihole/index.html

And add the following content:

<html><body><img src="pihole/pihole.png"></img></body></html>

Now you have a Webpage that serves up a tiny, transparent image, which will take place of the ads.

Edit the lighttpd Config File

Add the following to the end of /etc/lighttpd/lighttpd.conf :

$HTTP["host"] =~ ".*" {
     url.rewrite = (".*" => "pihole/index.html")

This will rewrite all URLs to point to the blank page instead of a page that doesn’t exist.  You view the entire file here.


Just send all ads directly the the transparent image file by editing  /etc/lighttpd/lighttpd.conf to read:

$HTTP["host"] =~ ".*" {
     url.rewrite = (".*" => "pihole/pihole.png")

Restart Services and Test

If you made a lot of changes while editing the files, restart everything:

sudo service dnsmasq restart
sudo service lighttpd restart

Now, use the curl  command to try to download an advertising domain:

curl -I

The command above will download just the header information from the site (the -I  option), which will be resolved to the Raspberry Pi ( and not the real domain.  There output of the command should be similar to the following:

HTTP/1.1 200 OK
Content-Type: text/html
Accept-Ranges: bytes
ETag: "3978160023"
Last-Modified: Wed, 11 Jun 2014 01:10:23 GMT
Date: Wed, 11 Jun 2014 01:52:40 GMT
Server: lighttpd/1.4.31

The main line you should be interested in is the HTTP/1.1 200 OK , which is a message saying that the content was delivered OK.  You can also take note of the server, which is the lighttpd  server that you set up.

If you decided not to use the Web server, the result of your curl  command would look like this:

curl: (7) Failed to connect to port 80: Connection refused

Serving A Blank Page vs. Blank Image vs. Not Using the lighttpd Server

You can easily determine which is the fastest method by using the time  command.  It will record the time it takes to run a command.  Using the one-liner below, it will attempt to curl  the URL ten times and report back how long it took.

for i in {1..10};do time curl -I >/dev/null;done 2>&1 | awk '/real/ {print $2}'

With the Web server turned off, you get results like this:


Then, with it turned on (serving up a blank page instead of nothing):


So the processing time is pretty negligible either way you go.  But I still opt for the error-free method.

Also, some people have reported some sites display empty boxes where the ads used to be, while other sites remove the ad space completely.  See examples of this below.



I think the transparent 1×1 image works the best for most situations, but you can try it out and see how things work for you.  It’s likely that the Pi-hole won’t be able to block every ad perfectly, but it does work for the most part.  There Internet is organic and advertisers will always find a way to inject ads, but this solution seems to work!


Now your device should be blocking ads from the list as long as it has the Rapberry Pi set at your DNS server.  You can test it here or here.

Advanced Improvements

Watching the Log File

When we edited the dnsmasq.conf  file, we added the debug option (log-queries) in there.  This is very useful for finding out what URLs ads might be coming from.  You can simply watch the log file as you navigate to a site on your computer.  In the example below, I had my Apple TV using the Raspberry Pi as its DNS server.  This allowed me to watch what URLs Hulu was using when the ads appeared on the TV.

cat /var/log/daemon.log | grep -i hulu

Using grep , I was able to parse out the just the links relating to hulu .

If you just want to watch the log file in real time, use this command.  It is kind of fun to see what pops up when you navigate to a site.

tail -f /var/log/daemon.log

Once you find a URL you want to block, you can just append it to your adList.conf  file.

Blocking Even More Ads

The advanced setup gets ad URLs from different locations and compiles them into one place for even more ad-blocking power.  I am also close to skipping Hulu plus video ads, but seem to be stuck.

Open Source

The Pi-hole is completely open source and free.  Any feedback, suggestions, or improvements are welcomed.

127 replies on “Pi-hole: A Raspberry Pi Ad-Blocker with DNS Caching (Ultra-fast)”

Here are a couple of things to try:

  • restart dnsmasq in case any of your settings didn’t get applied
  • if you are trying to dig a URL from another device, make sure you have its DNS servers pointed to the Pi
  • if you are trying to dig a URL from the Pi, its DNS settings need to point to itself
  • make sure the resolv.conf file has first in the list
  • remove the other servers from resolve.conf (leaving only This will help isolate to where the problem is.

Hopefully, this will help!

Thanks for your reply.

What I end up doing was:

dig @192.168.x.x
Where the @192.168.x.x would represent the Raspberry local IP.

Now the dig did what we expected.

But, when I hook my Android Wifi DNS to my local Raspberry pi (connected to the same network ofcrouse) It doesn’t load pages at all anymore.

I have not done the lighttpd part, is it necessary? It seems non relevant.

Can you get out to other non-ad pages from your Pi? If it doesn’t work there either, then it does not have another DNS server to query, such as Google’s ( To answer the second part of your question, yes, dnsmasq can run on its own without lighttpd. It will just error out instead of serving up a blank page. I don’t know what else would be wrong. I wrote this walkthrough and the wiped my Pi and followed my own instructions and it is still working for me. It seems to have something to do with the servers. You are either getting out to the Internet (including ad URLs) or not at all.

If dnsmasq on your Pi is returning to queries from other devices then the lighttpd part is useless. The DNS response is directing the devices to themselves – not to the Pi.

The script includes a variable piholeIP as follows:
# Address to send ads to

It then does the following:
# Parses out the default address and replaces it with the IP where ads will be sent
curl $adListURL | sed “s/$piholeIP/” > $eventHorizion

But yes without setting this variable you are correct.

Perhaps you could add this Jacob?

You don’t actually mention setting the piholeIP variable in, so all clients are going to loopback. But it’s pretty obvious if you look at the code.

So if you dont want to use lighttpd, isn’t it better to use instead of I have little knowledge, but from what I read on other modern adblock sites, nobody uses anymore. is said to be faster.

The other thing is, for the script to work with lighttpd, if my Raspberry Pi’s local network adress is for example, then I would have to replace the part of the script that says “” with, right? So other devices get as a return for ad domains, and then they once again contact the Pi for a SECOND TIME, and he replies with his blank weppage from lighttpd, as intended. Is that right?

And another question that I have, that doesn’t need to be answered: my mobile devices’ browsers (iPad, Android Phones and stuff) don’t show errors when working with the faulty default script. Does that mean they wouldn’t profit from a fixed lighttpd script in any way? Sites load fast and don’t show spinning loading icons. The only browser I know of that shows errors is Internet Explorer, but that one isn’t used anyways.

AFAIK, is akin to listening on all available interfaces–which to me seems it would take up more processing resources–where as listens to just that address.

As for your second question, yes replacing with your IP should do the behavior you described. But I don’t quite follow what you mean by a second time.

Finally, I don’t know this for certain, but it seems to me that processing an error takes more resources than processing something that exists. So theoretically, I would think it would be faster if it was fixed.

By a second time I mean this:

Device sends unresolved ad server domain to Pi.
-> Pi finds it in the adblock list and returns not “” but his own local network IP to the device (bevause we changed that value accordingly)
-> The device contacts that IP (which is again the Pi, so this is the second time)
-> Pi finally returns the webpage that is configured in lighttpd

Again, my knowledge is quite limited, but just from reading and trying to understand your code, and from reading the comments here I believe that the “” part in is critical for the script to work because it needs to manually be replaced with a static LAN IP. Otherwise the devices would try to find the ads at themselves (at their own IP, “”).

And I believe that’s what two other users here, Anonymous and AI, already mentioned, that it’s not clear in the how-to that the value is a static one (a string) that doesn’t automatically get replaced by the Pi’s real LAN IP.

I hope I’m right so I didn’t waste your time!

Please don’t give up on your script. It’s probably the best out there, as you said in your tutorial. But it needs to be fixed, and we must add a way to add multiple sources. I would do this for you but I have yet to learn bash scripting.
It’s so much better than other solutions that depend on WLAN or other drawbacks.

I have been meaning to update the script, but my Pi has been repurposed for some other projects I have been working on. At some point, I will need to follow my own walkthrough and edit things as discussed.

Jacob, this is a great tutorial and the reason why I purchased a Pi!! I’ve followed the Advanced Setup tutorial and am now looking to see if we can block youtube ads on the youtube iOS app. Am looking at the log (tail -200f /var/log/daemon.log) and am trying to track down where these youtube ads are coming from. Good stuff and very much appreciated!

Thanks so much!! I’m glad it worked for you. Blocking those annoying video ads has been my overall goal for a while. tailing the log is how I was able to block some of those Hulu video ads, but I noticed they are using some method to detect when they are blocked and it will either stop playing the video all together, or say something like “..please re-enable ads to keep our costs down…” I suspect it might have something to do with what sort of content gets returned when querying for it. Since Hulu and YouTube play video ads, delivering a static image or blank page gets it confused. So maybe by setting up the lighttpd server to offer up a 1 second video might do something. More experimenting!!

Yes. I have the article mostly written up and have had some success completely eliminating the Hulu video ads, but when I tried to follow through my own steps, I couldn’t re-produce the effect. I’m missing something (maybe a step I forgot to write down), so I just need to figure out what it is…

Looks like something is wrong with the latest file.
I have to disable the yoyo part and some aggregation added a doubled address so the service couldn’t start, I have to manually edit it after running the script.

For some reason I seem to have problems using your reply here – this is my 3rd time I typed out this message… can you suggest another way to contact so I can leave you the details to debug?

I have been using the for a while now. Others have had success, too. In the most recent version I added a 10 second sleep command, which allowed the yoyo list to download properly as it slightly different then all of the other sites. The uniq command takes out any duplicates so I’m not sure how one got in there… You can contact me here.

interesting… way cool.

question: can you setup the pi infront of your router instead? ie:
cable mode > pi > wife router > devices


cheers and great work!

I think so. I have a few people who have emailed me saying it’s possible. You may want to take a look at my other post about turning the Pi into a router. I plan to use that combined with this project to write up another article on replacing your router with an ad-blocking Raspberry Pi.

It might also be possible to set it up as a proxy to the same effect. However, one thing to watch for on both methods is performance. The Pi doesn’t have gigabit Ethernet, so your speeds might suffer. But you can also buy adapters that can get you there.

I’m not sure if it’s a configuration issue, but I’ve always understood lighttp expects references to images and whatnot to be relative to /var/www. In your example index.html file, lighty is going to look for the image in /var/www/var/www/pihole/.

I was actually unable to get the webserver to serve this image because of a permission error. it read the index file just fine, and if I put an alt tag it showed that text. I’m not sure why this is happening yet…

Also, the automated script doesn’t seem to download the single pixel or create the reference in index, but I assumed that it’s still a work in progress.

You are probably right. I actually didn’t test it before updating the post, but I have kind of been using it as my own walkthrough since there is usually a time gap when I have time to work on it.

I have had a few people say you can just point it right to the image, without the HTML code and it works.

You are also right about the automated script not including the transparent image. I don’t want to update the repo until I know everything works.

I appreciate the feedback and will test it out when I can!

The path issue indicated by harmlessgryphon is accurate. One could also just use an index.html with a base64 encoded 1×1 pixel transparent GIF within it. Here’s one with a 22-byte GIF that works in Safari, Chrome, and Firefox.

Hmm, well, most of the system works, but it doesn’t appear to be replacing anything with the png image when I attempt to use the lighttpd server. The server is definitely working (I can get the page to appear when using the server ID in a browser), and the curl test works as expected, but so far as I can tell it is not using that image as a replacement for any of the ads. It just leaves a large blank space. (I’ve tried substituting the image and the exact same thing happens, i.e. just a big space.)

Did you try both methods?

To an actual page

url.rewrite = (".*" => "pihole/index.html")

directly to the png file?

url.rewrite = (".*" => "pihole/pihole.png")

I think some ads give some issues no matter what.

Thanks Jacob for your hard work. I have been using it with an iPad and some websites the blocking technique runs really fast but in others on the other hand it takes ages to load complete the website. It seems like it is waiting a timeout. Any idea what is going on?

The Pi has a limitation since it is only 10/100 and not Gigabit Ethernet. Also, the first time you query a site, it will be slower. Once the domain is cached, it will be much faster.

You could always try setting up a USB-to-Gigabit Ethernet adapter. You could also try overclocking the Pi for faster performance.

Jacob, just a short line to say thank you. It is this kind of tutorials that make the Raspberry fun for technology handicapped people like myself. I’ve got the Windows virus in 1996 and it seems the antidote is not working.

You are welcome! There were lots of tutorials out there, and even though I am pretty technical, I had a hard time following them. I wanted to make a tutorial that I could follow myself and also help others go through step-by-step.

Glad you enjoyed it!

Why do you need the line
in the config? It seems unnecessary and detrimental, since usually no other name server is running on the pi.

Setting server= sets the loopback as the primary DNS server. Then the other two are the secondary and tertiary ones.

I also set it to so that the automated installation would work better.

Idk, to me loopback makes sense in resolv.conf, but not in dnsmasq.conf. You are telling dnsmasq to contact itself when it doesn’t find the DNS entry in its cache.

Btw, thanks for this page and the script, greatly appreciated!

Originally, I did use resolv.conf, but found it easier to put it in dnsmasq.conf. It needs to contact itself first so it can parse the rules in adList.conf and if it finds a match, it redirects the query to the blank page. If it doesn’t find a match, it just goes to the other DNS servers. Once you get enough sites cached (a max of 10,000) the speed at which queries happen will be faster.

If you set the first DNS server to a different one or public one, it wouldn’t know to block the ad domains, since the config file lives on the Pi.

Well, i am using it without server= in my dnsmasq.conf, and ad blocking seems to work just fine. Not using the lighttpd setup though, not sure if that makes a difference.

See above, no need to put it in the dnsmasq.conf, it will just be ignored. You still need to put it in the resolv.conf or dhclient.conf.

OK, so as long as your devices are set to use the Pi as their DNS server, it will query its records first before going on to the alternate servers?

Yes, servers in the dnsmasq conf are upstream servers where the query gets forwarded if nothing found in internal cache.

Btw, there is a cool option all-servers, where you can add multiple servers simultaneously and dnsmasq will use the response from the fastest one. Maybe you want to add that to your page.

Actually, just saw in syslog that dnsmasq ignores automatically, so no need to put it in the conf, but no harm either

Mar 27 22:10:39 raspberrypi dnsmasq[4034]: ignoring nameserver – local interface

This is a great idea and I’d love to try something similar myself. I’m assuming as it uses dnsmasq it could in theory be ported to other platforms that also use this. In my case I’m looking at my router which runs pfSense (based on BSD) and will begin having a play over the next few days and see if I can get it to work. In theory, even other linux-based routers could also support this with a bit of tweaking. This would mean one less device to have plugged in, no need to alter the DNS on all devices (although some more advanced routers may let you specify a custom DNS server in the DHCP options to automate that part of it).

Out of interest I’ve always stayed away from this approach and used adblock instead so it is easily disable-able on sites that won’t let you view them with an adblocker installed. Has anyone noticed any issues accessing sites or display/layout problems with this approach? I guess in some ways Adblock offers advantages that it can use regular expressions to block ads and isn’t just based on a domain list so it could potentially block more ads than the DNS version relying solely on hostnames/domains

In my experience, works surprisingly well. I was using privoxy before, but that one has many more issues. This solution here is clearly superior, much faster and fewer sideffects.

Thanks for sharing your experience. I know Privoxy is what people normally recommend for pfSense firewalls/gateways but I never got round to setting it up and this solution does look much better and easier.

Yes. I have had a few people who have it running on a OpenWRT router. I’m also working a Raspberry Pi 2 project to have it run as a router and a Pi-Hole. The cool thing is the dnsmasq also can be setup as a DHCP server. Let me know what you find out and what model you use.

Thanks, I’m not too skilled on modifying config files so far (I’m more of a GUI point and click kinda person lol) but I’m not too bad at the command line. I’m running pfSense 2.2 ( on Alix 2D3 ( hardware at the moment so hopefully it’s possible to use the block lists with that, it’s based on FreeBSD, I’ll give it a go in the next few days and let you know how I get on.

That’s what I pretty much did with my router, I set the DNS to the address of the pi. That way the router will forward all DNS requests to the pi automatically, no need to manually change the devices on the network.

You can either add a whitelist.txt in /etc/pihole before running the gravity script, or you can always manually add a line to /etc/dnsmasq.d/adList.conf.

Hi, thanks for the script..
Im only asking one question, is there anyway i can insert port on $piholeIP variable?
my pi is serving some webpage on port 80, i set the web dummy on port 81.
I tried to insert port on the, dnsmasq service is error..

I am reasonably certain that this will not work. As I understand it, a DNS lookup will not redirect one port to another (eg. TCP 80 to TCP 81 in this case). On the other hand, one could use iptables to accomplish what Dhoni requires, which I think is:

A) He has a publically accessible webserver running on port 80 that serves some pages, and

B) He wants to run the Pi-Hole webserver (eg. lighttpd) on port 81.

Here is an iptables rule that will get the job done, with the assumption that all the traffic to access his public server will be forwarded from his router, which has the IP address “”.

iptables -t nat -A PREROUTING ! -s -p tcp -m tcp –dport 80 -j REDIRECT –to-port 81

Basically, this rule says that any traffic destined for TCP port 80 coming from IP addresses other than the router will be redirected to TCP port 81 on the Raspberry Pi.

Interesting. It seems like a good route. I agree as the more I read, the more I think my previous comment would not work.

I wonder if you could also use lighttpd.conf‘s redirect ability to do this. I know I have done it before when trying to redirect to a minidlna stream, which was on port 8200.

Definitely – it can also be done using lighttpd’s mod_redirect. And your idea is a cleaner solution with less memory/CPU demand.

Basically, lighttpd should be configured to listen on two ports – 80 & 81. URLs from anything other than a specific IP address (eg. would be redirected to the Pi-Hole URL running on 81. The mod_redirect documentation has an example (the first one) that can be adapted easily for Dhoni’s needs.

I think the /etc/dnsmasq.conf should include a “ttl-local=” line followed by a number specifying for how long clients cache the ad server’s IP address. If such a line is not present, I believe that Pi’s custom adblock files are unnecessarily queried each time a web site is loaded. I think I did some tests a few months ago that proved it. Should be easy to test by watching the log file.

From the manual:

“-T, –local-ttl=

When replying with information from /etc/hosts or the DHCP leases
file dnsmasq by default sets the time-to-live field to zero, meaning
that the requester should not itself cache the information. This is
the correct thing to do in almost all situations. This option allows a
time-to-live (in seconds) to be given for these replies. This will
reduce the load on the server at the expense of clients using stale
data under some circumstances.”

Umm …. giving local-ttl a non-zero value should have an impact only if dnsmasq is using information from /etc/hosts or from the DHCP lease file (usually /var/lib/misc/dnsmasq.leases).

But that isn’t the situation with Pi-hole ….

You are right. What about max-cache-ttl=? According to the man page it Sets a maximum TTL value for entries in the cache.

I do not fully understand how the TTL works as it relates to the cache. I know TTL is basically how long the data is valid before another request needs to be made, so does that mean it doesn’t need to query the config file for that amount of time?

I don’t know what the default value of max-cache-ttl is – usually this is one week (86400 seconds). max-cache-ttl is not supposed to override the TTL sent by the authoriative DNS anyway, so raising that value isn’t going to dramatically improve performance. On the other hand, reducing it will reduce performance (imagine setting it to 0 seconds or 5 seconds ….).

Interesting. Thanks. I wonder if adList.conf were broken up into several config files that the Pi could have a better time parsing through them.

Could you describe your (precise) test setup and results? I don’t understand why giving local-ttl a non-zero value will impact dns lookups that aren’t using /etc/hosts or the DHCP lease file (usually /var/lib/misc/dnsmasq.leases).


It seems that not only /etc/hosts is used by this option, but also other host files (if specified in dnsmasq.conf) and maybe dnsmasq formatted files also. Not absolutely sure of the latter at the moment, because I just realized that by now I use only host files. Pardon me if it turns out it won’t work that way.
As far as I remember I watched the log file output as explained in the tutorial and noticed that clients requested the same ad server IPs over and over. It totally stopped when I added the TTL option – now it’s only 1 request, and then the clients stop requesting. Indicating that they receive and cache a TTL value greater than zero.

I would suggest trying the following:

a) Change so that its output is exactly like a hosts file.
b) Give that hosts file the name of your choice, and place it anywhere except /etc/dnsmasq.d
c) Include that new hosts file in the dnsmasq configuration using the option “addn-hosts=/path_to/pihole_hosts_file

Under these conditions, local-ttl should work.

But which is faster in general: a dnsmasq config file or a hosts file?

Don’t know – from the client perspective a hosts file should be faster in theory. I’m going to try it 🙂

Quick tests indicate that a hosts file is faster. Not that you need the help, but the difference in would be:
‘{sub(/r$/,””); print “address=/”$0″/”IP}'”
‘{sub(/r$/,””); print IP”11″$0}’

It will definitely work with files in the format of a hosts file, if that file is included in the dnsmasq configuration.

After Murtel’s excellent suggestion to place blocked FQDNs in a hosts file, rather than a dnsmasq configuration file, I’ve been thinking about other ways to speed up adblocking with Pi-Hole. Here’s another idea that seems to work – changing the expiration date of the “index.html” file sent to the client as well as the blank image file (i.e. the contents of /var/www/pihole/).

For details, see:

Giving an expiration of 24 hours (or longer) will mean that the client will not contact the pi-hole server again for the same URL; rather use it from its local cache. To do this, make use of the mod_expire module in lighttpd and the expire.url configuration option. The relevant changes would be to add “mod_expire” as a server module and the following directives in lighttpd.conf

# set expiration to 1 day
$HTTP[“url”] =~ “^/pihole/” {
expire.url = ( “” => “access plus 1 days”)

In this particular case, the contents of /var/www/pihole will expire one day after they are first accessed. Here’s a comparision of the original and modified HTTP headers:

pi@dns-adtrap:~> wget -S
–2015-05-08 12:20:31–
Resolving (…
Connecting to (||:80… connected.
HTTP request sent, awaiting response…
HTTP/1.1 200 OK
Content-Type: text/html
Accept-Ranges: bytes
ETag: “3989523662”
Last-Modified: Mon, 02 Mar 2015 16:15:22 GMT
Content-Length: 105
Date: Fri, 08 May 2015 17:20:31 GMT
Server: lighttpd/1.4.31
Length: 105 [text/html]

pi@dns-adtrap:~> wget -S
–2015-05-08 12:24:54–
Resolving (…
Connecting to (||:80… connected.
HTTP request sent, awaiting response…
HTTP/1.1 200 OK
Expires: Sat, 09 May 2015 17:24:54 GMT
Cache-Control: max-age=86400
Content-Type: text/html
Accept-Ranges: bytes
ETag: “3989523662”
Last-Modified: Mon, 02 Mar 2015 16:15:22 GMT
Content-Length: 105
Date: Fri, 08 May 2015 17:24:54 GMT
Server: lighttpd/1.4.31
Length: 105 [text/html]

I’ve made another suggestion in the past that also speeds up the server-client interaction, and that is to encode blank.gif or blank.png as Base64 directly in “index.html”. That way, the client only downloads a SINGLE file from the server rather than two. The number of bytes transfered is reduced as is the connection overhead.

Ciao for now!

Good idea to test it thoroughly before merging it into your release version.

I’d actually recommend not usingw the base64. The smallest HTTP transfer would be if URLs were re-written to directly retrieve a 1×1 transparent GIF image – 42 bytes. That is 63 bytes smaller than the base64 solution, which is contained with an HTML file. The total file size is 105 bytes, although the Base64 GIF is only 22 bytes.

I have verified that a hosts file containing FQDNs is faster than a dnsmasq configuration file, and works with the local-ttl setting in dnsmasq.conf. Adding expiration HTTP headers makes things even faster – not only do lookups not have to be done multiple times, but neither do HTTP requests. The expiration date can be much longer than 1 day (anything up to 1 year – I think).

I use a single Pi for a lot of things – my one-minute load average used to be around 0.3-0.5. I’m rarely over 0.1 now, an indication of how much CPU DNS lookups and HTTP transfers were taking.

I guess I’m not quite sure how to rewrite the URL directly to the image. However, I did find this Stack Overflow answer, which might be a good starting point. I also found this site, where the author made a GIF that is 26 Bytes.

I do know that when a name resolution query is initiated, the hosts file is generally checked first. So the order of a lookup goes something like this:

1. Checks to see if the query requested is the client itself
2. Queries the hosts file
3. Queries the Domain Name System

Your system load is quantifiable evidence of the improvements. Most of the sources for ad lists are already in host file format, so that could save processing when getting them. Formatting the list of just the domains names into the format address=/ also makes the file much bigger due to all the extra characters, so it makes sense that the hosts file is smaller because it only needs the IP address and the domain name.

Sorry, I didn’t make myself clear. Here’s what I use as the rewrite rule in lighttpd.conf

# Redirect to a 1×1 pixel blank gif
$HTTP[“host”] =~ “.*” {
url.rewrite = ( “.*” => “pihole/blank.gif” )

Off course, blank.gif is a file inside /var/www/pihole. I think your finding a 26 byte GIF is excellent – makes use of lower network bandwidth. That may not be so important when Pi-Hole is used in just an intranet (eg. home network), but is important in my use, where I use Pi-Hole over the internet. I run an OpenVPN server on my RPi, and OpenVPN clients on my Mac at work and phone – so all my network traffic ultimately goes through my home network. Consequently, no ads when I use a browser at work or when I’m on my phone.

In my use lighttpd is used solely for Pi-Hole. In this circumstance, its memory footprint can be made smaller – the only essential server modules to load are:
mod_expire, mod_access, and mod_rewrite


I found some more links claiming that the hosts file is faster than a DNS lookup:

I am also thinking that since most of the source lists that the Pi-hole downloads are already in host file format, that it may be prudent to give it a try.

There was recently a huge update to one of the lists and the compiled list is pushing 1,000,000 entries. I think I will try out another branch of the script and convert it into a clean hosts file.

Off course a hosts is faster – all dnsmasq has to do is read a file. As opposed to opening a socket (over the loopback interface), sending a query, reading a file to respond to the query, and finally sending/interpreting the response.

Also consequentially also takes much less cpu to do all this for local addresses (i.e. anything in the hosts file).

I’m currently using a hosts file with well over a million entries.

P.S. Here’s an analogy – opening an HTML file directly using the browser of your choice vs retrieving that file by querying an http server bound to the loopback interface. In both cases, the HTML file has to be read. But in the latter situation, there’s all the extra transactional overhead as well.

The hosts file doesn’t need to be on every client.

When dnsmasq uses a configuration file with hosts/addresses, it queries itself. When it uses a hosts file that doesn’t happen – it directly reads the file. So there’s a lot more overhead when a configuration file is used rather than a hosts file.

So you could still use dnsmasq but instead of the normal adList.conf file, you would put a hosts file in /etc/dnsmasq.d and it would work just the same but faster?

Couple points:
1) Edit dnsmasq.conf to indicate the path to and name of the additional hosts file (addn-hosts=)
2) Don’t place it in /etc/dnsmasq.d/

One other point I forgot to mention. There is an another advantage to using a hosts file – you don’t have to stop/start dnsmasq when this hosts file is updated. All you have to do is send a SIGHUP to the dnsmasq process and it will re-read the updated file.

This means that dnsmasq’s cache is preserved – in sharp contrast to the cache being deleted during a stop/start (i.e. restart). Updates to a dnsmasq configuration file are read only when dnsmasq is stopped/started.

should the adlist.conf be removed so the system doesn’t query it? I’m trying to change my pi-hole install to a hosts version but I’m not quite on your level.

Here’s a simple comparison of directly reading a hosts file vs querying dnsmasq over the loopback interface. (FYI – the entry is in my hosts file and resolves to

Directly reading hosts file:
time getent hosts
real 0m0.054s
user 0m0.000s
sys 0m0.010s

(so real time is 0.054s, and system time is 0.01s)

Querying dnsmasq over the loopback interface:
time dig @

; <> DiG 9.8.4-rpz2+rl005.12-P1 <> @
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39875
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
; IN A
;; Query time: 7 msec
;; WHEN: Tue May 19 15:54:15 2015
;; MSG SIZE rcvd: 43
real 0m0.146s
user 0m0.060s
sys 0m0.050s

0.146s real time and 0.05s system time. About three times slower in real time and five times as much CPU time.

Yeah, I’m beginning to think that the actual IP will work better. I think in the next release, I will uncomment the auto-IP discovery. I am also using a newer gravity script, which is more respectful of the sites hosting the domain lists. It won’t download the list again if there have not been any changes.

Also, the 26 Byte GIF sometimes displays as a different color in Firefox. So the smallest one that is still transparent is 37 Bytes, which I think I might use for the release, but users can put in the smaller one if they want.

Thanks for taking the time to create this project!

Im looking to add some new filter lists (im from Sweden so i need some country specific hostnames), is there a certain format the list has to be in?
Can I use ABP filter lists, like fanboys?

I’m releasing a new version very soon, which will change things up. If you are interested in looking at the script/code, take a look here. But for the current Pi-hole, the domains need to be in the format:


The ABP lists are quite difficult to parse out just the domain due to the convoluted syntax those lists use. If you can figure out how to get just the domains out of that list, you can add that to the script (which is much easier in the new version of the script).

I’ll be posting the new article soon, which details how to add your own lists to the Pi-hole. Stay tuned. I am going to try to get it published this week.

Strange. I’ll have to find out what list that comes from. In the meantime, you can whitelist it and run again. It might be worth my time to make a default whitelist since xkcd was also blocked.

It seems to come from the mahakala list:

pi@raspberrypi ~ $ cat /etc/pihole/ | grep reddit

I’ll probably need to make a default whitelist for common sites that most people would not want blocked.

Hi Jacob, nice work!

I have noticed in the logs (/var/log/pihole.log) errors
: bad name at /etc/pihole/gravity.list line 59026
I pulled the log off via winscp and used notepad++ to go to those lines. The issues is a .. in the list. Example: (it should read
Rather then update those lines and move the gravity.list back via winscp and have it replaced when it updates again are you able to put some smarts in when it consolidates all the lists to check and remove any .. before saving?

I see the same thing: on line 59026. It looks like it is on the mahakala list, which isn’t surprising since it is the biggest list of all of them.

Your fastest fix will be to simple edit the /etc/pihole/ and remove the extra period. Then if you run again, it will re-create the file with corrected entry.

As a long term solution, I have opened an issue on Github to put some sort of data checking to make sure it’s just a the domain name and not any other special characters.

Since other people are making the lists, there is always room for human error, so if can check for and eliminate those errors, it should help.

Out of curiosity, did you get almost 1.3 million entries the last time you ran the script? I did. I looked through the file and it seemed that they were all legitimate entries…

Could these bad names be a root cause of DNS requests timing out? I thought it was because the list is so large (now 1.61M addresses) and when I create a very short test list, it works quite well. If I include the full gravity.list however, DNS requests do not resolve. Wondering if this is because the list is so large or if I need to clean out these bad names. Thoughts?

My guess is it is just bad names. It’s also possible there are invisible characters that cause issues: spaces, carriage returns, or other weird ones. We are actually working on that, too.

That was the culprit. With the bad names removed the Pi-hole is running like a champ. So much gravity! The troublemakers were addresses with the ^ character.

For those interested, I can confirm the Pi-hole successfully runs simultaneously with OpenVPN.

How does this compare to Privoxy? I’ve got Privoxy setup on my Mac mini using directions from here:

I then set my Mini to use the 5ghz channel and setup the proxies on that, so whenever I use a mobile device to connect to 5ghz it uses Privoxy. This is great for filtering out most junk however some sites are broken like Newegg and Walmart etc. While they load certain page elements like radio buttons on Newegg to filter down product categories don’t work. You click and nothing happens. Also it does not block any ads on Youtube on mobile.

And lastly its a bit of a pain to setup and maintain. I tried adding an easylist filter but it didn’t seem to work. Will Pi Hole is just what I need.

I dont wanna adblock all my devices, just my iPad air really. If Im understanding correctly after I setup Pi Hole I just need to point my iPad and other mobile devices to use Pi Hole’s DNS to get adblock, and keep using google DNS for other devices like my Mini/Desktop where I don’t need to use PiHole.

If that sounds right Im gonna order a Raspberry Pi right away.

Privoxy always seemed sluggish to me, but that was a few years ago. I think in your case the Pi-hole will be faster. You can choose which device uses it by changing its DNS server. This allows you to pick and choose what devices use it.

I have been using the Pi-hole since I made it and I have noticed that my entire network is faster because of it and I’m not the only one.

The Pi-hole still struggles with video ads a bit, but that is a work in progress.

You’re comparing apples and oranges. Privoxy is a web proxy that can enhance privacy etc. etc. Pi-hole doesn’t provide any of that functionality. When Privoxy is used – all browsing is “gatewayed” through the server running Privoxy; obviously that isn’t what dnsmasq does. A suitable comparison to Privoxy in terms of function would be squid.

True, they are two very different things. The Pi-hole can be used to block malware/privacy sites if you know what domains to block. You just need to modify the script to add them in.

It can also block tracking sites like the ones provided by Disconnect.

None of these are on by default in the Pi-hole at the moment; they are left to the will of tinkerers.

CNN tracks, Google tracks, Bing tracks, Yahoo tracks.

You can block domains, but it is misleading to give the impression that DNS redirection of FQDNs blocks IP tracking. DNS redirection does one thing and one thing only – preventing access to specific FQDNs – and it does that very well. However, it NEVER hides originating IP addresses, and therefore it is incorrect (inappropriate?) to even imply that it can be used to block tracking; the latter being the sole function of proxying protocols – be they http(s) proxies like Privoxy/squid, SOCKS proxies, or VPNs. The sole function of proxying being to hide an IP address, i.e prevent originating IP addresses from being tracked.

No, the Pi-hole is not a be-all end-all solution to blocking everything. It can be implemented in a layered defense strategy.

If a request from a tracking domain is made, it is blocked because the fake Webpage is served instead. There are also many ways to track people; more than just IP addresses.

would this work on a model A base model pi, or would that make my whole household’s internet really slow. I have 30 mbps ish, probably due to go up over the coming years I guess, just to give you an idea (obviously nothing’s going to slow down dialup/adsl, but I was wondering whether the cheap base model would cope with that internet speed or bottleneck me).

The model A does not have a network interface, so no, it won’t work. It will probably install, but you won’t be able to do anything with it.

I have been running mine on a B+ for quite sometime and it works just fine. It’s a little slow when I want to download and update the lists, but I do not notice any issues on a daily basis with a 25Mbps connection.

Thanks for that, I was just wondering whether there was any appreciable drop in speed when downloading large files, or whether you saw a drop in your score? I know I could obviously get around the whole downloading files issue by just setting something like a rpi or another pc up which bipasses it, and remotely access it, but our family I’m guessing are really hammering the internet connection, they’re all on youtube ect, do you think it would slow them down. Do you think there is any benefit to getting the new raspberry pi over the older generation model 2+?

The Pi-hole has proven to actually make your network faster since it caches DNS queries and doesn’t use your bandwidth to download advertisements.

The older gen is just fine. I think the new one is kind of a waste. I have many users that have just dedicated their older Pi as a Pi-hole since it just sits there and runs all the time and then used the newer one for other things.

Thanks, pennies just dropping that only the dns requests are going to the thing, not the whole connections worth of data!! Sorry, should have read it all a bit more carefully, I’ve been a bit busy lately. Would there be any possible security issues if I were to plug a hdd into it and use it as a backup server, since it’s only getting dns requests? I take you point about dns caching, and also I guess not downloading the stupid video ads etc will massively speed everything up (the whole point).

Also, does this block youtube ads? I mean the video ones that interrupt your video. Is it all legal to block this stuff?

There will always be security issues with technology. The most risky thing the Pi-hole does is it trusts the sites that maintain the lists as it downloads it from them.

I haven’t tested it much on YouTube, but people have told me that it sometimes works there.

Is it illegal to avert your eyes if you do not want to look at a billboard? Is it illegal to change the channel when a commercial comes on?

Thanks for that, that’s really helpful, I was figuring that might be the case with the security. I hope I didn’t come across wrongly when I asked about legality, I take your point, I just wanted to ask the question. Tbh I almost understand the idea that people need revenue from their sites, but the video ads kill my computer, and there are always those dodgy ones like “click here to download” on all the dodgy download sites, which I’m sure are engineered to steer you towards the ads. This should be really really useful.

Nope, it was a legitimate question–one that is often debated along with whether or not it is ethical.

I agree that ads do support a lot of sites that provide free content (mine is one of them, even though I only make like $2.00 a month on them–maybe because my readers are using a Pi-hole 😝).

Leave a Reply to jacobsalmela Cancel reply