Skip to content

Last updated: 2026-02-11

dnsdist DNS over TLS/HTTPS Configuration Guide

This guide provides recommended DNS over TLS (DoT) and DNS over HTTPS (DoH) settings for dnsdist. dnsdist is a DNS load balancer and proxy from the PowerDNS project that can serve as a DoT/DoH frontend for any DNS backend.

Prerequisites

Installation note: dnsdist is not in the base repositories for RHEL/CentOS or SLES. Install from the PowerDNS repository. On Debian/Ubuntu, dnsdist is available in the standard repositories.

DNS over TLS (DoT)

Frontend Listener

Configure dnsdist to accept DNS over TLS connections on port 853:

addTLSLocal('0.0.0.0:853', '/etc/dnsdist/ssl/fullchain.pem', '/etc/dnsdist/ssl/privkey.pem', {
  minTLSVersion='tls1.2',
  ciphers='ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305',
  ciphersTLS13='TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256'
})

IPv6 Support

Add a second listener for IPv6:

addTLSLocal('[::]:853', '/etc/dnsdist/ssl/fullchain.pem', '/etc/dnsdist/ssl/privkey.pem', {
  minTLSVersion='tls1.2'
})

DNS over HTTPS (DoH)

Frontend Listener

Configure dnsdist to accept DNS over HTTPS connections on port 443:

addDOHLocal('0.0.0.0:443', '/etc/dnsdist/ssl/fullchain.pem', '/etc/dnsdist/ssl/privkey.pem', '/dns-query', {
  minTLSVersion='tls1.2',
  ciphers='ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305',
  ciphersTLS13='TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256'
})

The /dns-query path is the standard DoH endpoint defined in RFC 8484.

Custom HTTP Headers

Add security headers to DoH responses (requires dnsdist 1.6+):

addDOHLocal('0.0.0.0:443', '/etc/dnsdist/ssl/fullchain.pem', '/etc/dnsdist/ssl/privkey.pem', '/dns-query', {
  minTLSVersion='tls1.2',
  customResponseHeaders={['Strict-Transport-Security']='max-age=63072000', ['X-Content-Type-Options']='nosniff'}
})

Backend Servers

Plain DNS Backends

Forward queries to backend resolvers over plain DNS:

newServer({address='127.0.0.1:53', name='local-resolver'})
newServer({address='10.0.0.1:53', name='resolver-1'})
newServer({address='10.0.0.2:53', name='resolver-2'})

DNS over TLS Backends

Forward queries to upstream resolvers over TLS:

newServer({address='9.9.9.9:853', tls='openssl', subjectName='dns.quad9.net', name='quad9'})
newServer({address='1.1.1.1:853', tls='openssl', subjectName='cloudflare-dns.com', name='cloudflare'})

Version note: The tls and subjectName options for newServer() require dnsdist 1.7+. On dnsdist 1.4-1.6 (Debian 11 ships 1.6.x), outgoing DoT to backends is not supported — backends must use plain DNS.

The subjectName option verifies the upstream server's certificate hostname.

TLS Session Tickets and Resumption

Configure TLS session tickets for improved performance:

addTLSLocal('0.0.0.0:853', '/etc/dnsdist/ssl/fullchain.pem', '/etc/dnsdist/ssl/privkey.pem', {
  minTLSVersion='tls1.2',
  numberOfTicketsKeys=3,
  ticketKeyFile='/etc/dnsdist/ssl/ticket.key',
  sessionTimeout=86400
})

Generate a ticket key:

dd if=/dev/urandom bs=80 count=1 of=/etc/dnsdist/ssl/ticket.key 2>/dev/null
chmod 600 /etc/dnsdist/ssl/ticket.key

OCSP Stapling

Enable OCSP stapling for DoT and DoH listeners:

addTLSLocal('0.0.0.0:853', '/etc/dnsdist/ssl/fullchain.pem', '/etc/dnsdist/ssl/privkey.pem', {
  minTLSVersion='tls1.2',
  ocspResponses={'/etc/dnsdist/ssl/ocsp.der'}
})

Fetch the OCSP response:

openssl ocsp -issuer /etc/dnsdist/ssl/chain.pem \
  -cert /etc/dnsdist/ssl/fullchain.pem \
  -url http://ocsp.example.com \
  -respout /etc/dnsdist/ssl/ocsp.der

Access Control

Restrict access to the DNS service:

setACL({'127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', '::1/128', 'fe80::/10'})

Load Balancing

Configure load balancing across backend servers:

setServerPolicy(leastOutstanding)

Available policies: firstAvailable, roundrobin, leastOutstanding, wrandom, whashed.

Complete Configuration

-- /etc/dnsdist/dnsdist.conf

-- Plain DNS listener (optional, for local clients)
addLocal('127.0.0.1:53')

-- DNS over TLS listener
addTLSLocal('0.0.0.0:853', '/etc/dnsdist/ssl/fullchain.pem', '/etc/dnsdist/ssl/privkey.pem', {
  minTLSVersion='tls1.2',
  ciphers='ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305',
  ciphersTLS13='TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256'
})

-- DNS over HTTPS listener
addDOHLocal('0.0.0.0:443', '/etc/dnsdist/ssl/fullchain.pem', '/etc/dnsdist/ssl/privkey.pem', '/dns-query', {
  minTLSVersion='tls1.2',
  ciphers='ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305',
  ciphersTLS13='TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256',
  customResponseHeaders={['Strict-Transport-Security']='max-age=63072000'}
})

-- Access control
setACL({'0.0.0.0/0', '::/0'})

-- Backend resolvers
newServer({address='127.0.0.1:5300', name='local-unbound'})

-- Load balancing
setServerPolicy(leastOutstanding)

-- Security
setMaxTCPClientThreads(64)
setMaxTCPQueuedConnections(1000)

-- Web interface (optional, bind to localhost only)
-- webserver('127.0.0.1:8083', 'password', 'apikey')

Verification

Test DNS over TLS:

kdig @dot.example.com +tls example.com A

Or with OpenSSL:

echo -ne '\x00\x1c\xab\xcd\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\x01\x00\x01' | \
  openssl s_client -connect dot.example.com:853 -quiet 2>/dev/null | xxd

Test DNS over HTTPS:

curl -s -H 'Accept: application/dns-json' 'https://doh.example.com/dns-query?name=example.com&type=A'

Check TLS certificate and protocol:

echo | openssl s_client -connect dot.example.com:853 2>/dev/null | grep -E 'Protocol|Cipher'

Check dnsdist statistics:

dnsdist -e 'showServers()'
dnsdist -e 'showTLSContexts()'