Skip to content

Last updated: 2026-05-15

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

  • dnsdist 1.5 or later (for DoT with TLS 1.3 cipher control; 1.4 for basic DoT)
  • dnsdist 1.5 or later (for DoH support; 1.6+ for customResponseHeaders)
  • A valid TLS certificate and private key
  • One or more backend DNS resolvers or authoritative servers

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')

Security Notes

The cipher suite and protocol configuration in this guide addresses the following known TLS vulnerabilities:

  • POODLE (CVE-2014-3566, 2014): SSL 3.0 is disabled. TLS_FALLBACK_SCSV was added in OpenSSL 1.0.1j / 1.0.2 (October 2014); SSL 3.0 disabled by default in OpenSSL 1.1.0 (August 2016).
  • BEAST (CVE-2011-3389, 2011): Mitigated by recommending TLS 1.2 as the minimum; AEAD-only ciphers eliminate the CBC padding oracle.
  • CRIME (CVE-2012-4929, 2012): TLS compression is off by default in OpenSSL 1.1.0+; do not enable it.
  • Lucky13 (2013): AEAD-only cipher list eliminates CBC padding timing side-channels entirely.
  • FREAK (CVE-2015-0204, 2015): EXPORT-grade ciphers are excluded. Removed from OpenSSL 1.1.0 (August 2016).
  • LOGJAM (CVE-2015-4000, 2015): Short-key DHE is excluded; only ECDHE key exchange is recommended.
  • Sweet32 (CVE-2016-2183, 2016): 3DES is excluded from the cipher string. Disabled by default in OpenSSL 3.0 (September 2021).
  • ROBOT (CVE-2017-13099, 2017): Static RSA key exchange is excluded; only ECDHE is recommended. Fixed in OpenSSL 1.0.2m / 1.0.1v (November 2017).
  • Downgrade attacks: TLS_FALLBACK_SCSV prevents protocol version rollback.

The following are not addressable through TLS configuration alone:

  • Heartbleed (CVE-2014-0160, 2014): A memory disclosure bug in OpenSSL 1.0.1 through 1.0.1f. Fixed in OpenSSL 1.0.1g (April 7, 2014). Addressed by patching OpenSSL, not by TLS configuration.
  • BREACH (CVE-2013-3587, 2013): Not applicable. BREACH targets HTTP-level response compression; DNS-over-TLS does not involve HTTP. DNS-over-HTTPS uses HTTP but typically without compressible secret data.
  • DROWN (CVE-2016-0800, 2016): Requires SSLv2 to be enabled on any server sharing the same private key. Ensure SSLv2 is disabled on all services that use the same certificate and key pair.

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()'

Confirm the DoT certificate and chain externally with the Mr.DNS SSL/TLS Certificate Check (port 853 for DoT, 443 for DoH).


Related Guides

View all DNS guides →

Configured TLS? Now Monitor It.

Generator Labs alerts you before certificates expire, get revoked, or fail chain validation — across HTTPS, SMTPS, IMAPS, LDAPS, and more.

Certificate Monitoring →