<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Adguard on Sysadmin Tales</title>
    <link>https://blog.ssb-tech.net/tags/adguard/</link>
    <description>Recent content in Adguard on Sysadmin Tales</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 02 Oct 2026 21:39:10 -0400</lastBuildDate>
    <atom:link href="https://blog.ssb-tech.net/tags/adguard/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Proxying AdGuard Home DNS-over-HTTPS with Nginx</title>
      <link>https://blog.ssb-tech.net/posts/adguard-home-doh-nginx/</link>
      <pubDate>Tue, 28 Oct 2025 20:38:03 -0400</pubDate>
      <guid>https://blog.ssb-tech.net/posts/adguard-home-doh-nginx/</guid>
      <description>&lt;p&gt;A few months ago I switched from using Pi-hole to AdGuardHome for serving adblocking DNS to not just the clients in my home, but also my mobile devices.&lt;/p&gt;&#xA;&lt;p&gt;There were a few reasons for this, but chief among them was its proper support for DNS-over-TLS (DOT) and DNS-over-HTTPS (DOH).&lt;/p&gt;&#xA;&lt;p&gt;I had previously been running unencrypted DNS via &lt;a href=&#34;https://tailscale.com&#34;&gt;Tailscale&lt;/a&gt;, but because of the way I have things configured, this mean that every DNS request from an external device looked like it was coming from my &lt;a href=&#34;https://tailscale.com/kb/1019/subnets&#34;&gt;subnet router&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;DOT and DOH both support the concept of unique &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome/wiki/Clients&#34;&gt;client IDs&lt;/a&gt;, which makes it easier to track down which device a particular request is coming from.&lt;/p&gt;&#xA;&lt;p&gt;However, one of the few problems that I&amp;rsquo;ve encountered with AdGuard is that there&amp;rsquo;s no way to separate the admin interface from the port that DOH uses.&lt;/p&gt;&#xA;&lt;p&gt;I brought up this conundrum in the Discord server for the since defunct &lt;a href=&#34;https://selfhosted.show&#34;&gt;Self-Hosted Podcast&lt;/a&gt;, and one of the users there (thanks Quietsy!) made an interesting suggestion that I must admit hadn&amp;rsquo;t crossed my mind.&lt;/p&gt;&#xA;&lt;p&gt;Simply put a reverse proxy in front of the application and implement access controls based on path.&lt;/p&gt;&#xA;&lt;p&gt;DOH runs over the &lt;code&gt;/dns-query&lt;/code&gt; endpoint, while the admin interface is at the root &lt;code&gt;/&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I logged into the VPS that&amp;rsquo;s running my public-facing AdGuard server and installed nginx.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo apt install nginx&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Then I stopped AdGuard:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl stop AdGuardHome&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I then edited &lt;code&gt;AdGuardHome.yaml&lt;/code&gt; (The path to this file may vary based on how you installed AdGuard).&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo vim AdGuardHome.yaml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I changed the &lt;code&gt;https_port&lt;/code&gt; from its default 443 to 4433, and the &lt;code&gt;http&lt;/code&gt; listening port from 80 to 8080.&lt;/p&gt;&#xA;&lt;p&gt;Then start AdGuard back up with:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl start AdGuardHome&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then I created a site in Nginx at &lt;code&gt;/etc/nginx/sites-enabled/adguardhome&lt;/code&gt; with the following settings:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;proxy_cache_path /var/cache/adguardhome levels=1:2 keys_zone=adguard_cache:10m max_size=3g inactive=120m use_temp_path=off;&#xA;&#xA;upstream adguardhome {&#xA;    server 127.0.0.1:4433;&#xA;    keepalive 64;&#xA;}&#xA;&#xA;server {&#xA;    server_name adguard.domain.tld;&#xA;    listen 80;&#xA;    # listen [::]:80 default_server;&#xA;&#xA;    return 301 https://$host$request_uri;&#xA;}&#xA;&#xA;server {&#xA;    server_name adguard.domain.tld;&#xA;    listen 443 ssl http2;&#xA;&#xA;    access_log /var/log/nginx/agh.access.log;&#xA;    error_log /var/log/nginx/agh.error.log warn;&#xA;&#xA;    gzip on;&#xA;    gzip_vary on;&#xA;    gzip_proxied any;&#xA;    gzip_comp_level 6;&#xA;    gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;&#xA;&#xA;    ssl_dhparam /etc/nginx/ssl/dhparam.pem;&#xA;    ssl_session_timeout 1d;&#xA;    ssl_session_cache shared:MozSSL:10m;&#xA;    ssl_session_tickets off;&#xA;&#xA;    ssl_protocols TLSv1.2 TLSv1.3;&#xA;    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;&#xA;    ssl_prefer_server_ciphers off;&#xA;&#xA;    ssl_certificate /etc/letsencrypt/live/adguard.domain.tld/fullchain.pem;&#xA;    ssl_certificate_key /etc/letsencrypt/live/adguard.domain.tld/privkey.pem;&#xA;&#xA;    ssl_early_data on;&#xA;&#xA;    add_header X-Frame-Options &amp;#34;SAMEORIGIN&amp;#34; always;&#xA;    add_header X-XSS-Protection &amp;#34;1; mode=block&amp;#34; always;&#xA;    add_header X-Content-Type-Options &amp;#34;nosniff&amp;#34; always;&#xA;    add_header Referrer-Policy no-referrer;&#xA;    add_header Strict-Transport-Security &amp;#34;max-age=63072000&amp;#34; always;&#xA;    add_header Permissions-Policy &amp;#34;interest-cohort=()&amp;#34;;&#xA;&#xA;    # Discourage Google bots from indexing this site&#xA;    add_header X-Robots-Tag &amp;#34;noindex&amp;#34;;&#xA;&#xA;    # Allow the dns-query endpoint&#xA;    location /dns-query {&#xA;    proxy_pass https://adguardhome/dns-query;&#xA;&#xA;    proxy_set_header Host $host;&#xA;    proxy_set_header X-Real-IP $remote_addr;&#xA;    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&#xA;    proxy_set_header X-Forwarded-Proto $scheme;&#xA;&#xA;    proxy_http_version 1.1;&#xA;    proxy_set_header Connection &amp;#34;&amp;#34;;&#xA;&#xA;    # Timeouts&#xA;    proxy_connect_timeout 90;&#xA;    proxy_send_timeout 300;&#xA;    proxy_read_timeout 90s;&#xA;    }&#xA;&#xA;    # For all other locations, return 403.&#xA;    location / {&#xA;            return 403;&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Enable and start the webserver:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl enable --now nginx&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, lets use &lt;a href=&#34;https://github.com/mr-karan/doggo&#34;&gt;doggo&lt;/a&gt; to test our DOH configuration.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;doggo fedoraproject.org @https://adguard.domain.tld/dns-query/client-id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME                    TYPE    CLASS   TTL     ADDRESS         NAMESERVER&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fedoraproject.org.      A       IN      47s     8.43.85.67      https://adguard.domain.tld/dns-query/client-id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fedoraproject.org.      A       IN      47s     8.43.85.73      https://adguard.domain.tld/dns-query/client-id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fedoraproject.org.      A       IN      47s     152.2.23.104    https://adguard.domain.tld/dns-query/client-id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fedoraproject.org.      A       IN      47s     34.211.44.206   https://adguard.domain.tld/dns-query/client-id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fedoraproject.org.      A       IN      47s     140.211.169.196 https://adguard.domain.tld/dns-query/client-id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fedoraproject.org.      A       IN      47s     38.145.32.21    https://adguard.domain.tld/dns-query/client-id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fedoraproject.org.      A       IN      47s     152.2.23.103    https://adguard.domain.tld/dns-query/client-id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fedoraproject.org.      A       IN      47s     67.219.144.68   https://adguard.domain.tld/dns-query/client-id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fedoraproject.org.      A       IN      47s     38.145.32.20    https://adguard.domain.tld/dns-query/client-id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Perfect!&lt;/p&gt;&#xA;&lt;p&gt;Now, lets make sure I can&amp;rsquo;t access the admin interface from the public facing nginx&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;Getting a 403 error as expected for other paths&#34; loading=&#34;lazy&#34; src=&#34;https://blog.ssb-tech.net/posts/adguard-home-doh-nginx/images/adguardnginx403.png&#34;&gt;&#xA;Awesome.&lt;/p&gt;&#xA;&lt;p&gt;I am also notably &lt;strong&gt;&lt;em&gt;NOT&lt;/em&gt;&lt;/strong&gt; exposing port 53. Only 853 and 443, both of which are using TCP.&lt;/p&gt;&#xA;</description>
    </item>
  </channel>
</rss>
