Captive Portals - DNSCrypt/dnscrypt-proxy GitHub Wiki
Captive Portals
Introduction
Operating systems perform connectivity checks after network changes to determine if internet access is available or if a captive portal (like a hotel or airport Wi-Fi login page) is in the way.
These checks involve resolving specific domain names and fetching content from known URLs. If these checks fail, the OS may report "No Internet" or "Limited connectivity" even when the network is working fine.
When using dnscrypt-proxy, these checks can fail during startup because:
- The proxy hasn't fully initialized yet
- The upstream DNS servers haven't been contacted yet
- DNS rebinding protection might block the responses
The [captive_portals] feature solves this by providing instant, hard-coded responses for these connectivity check domains.
How it works
When enabled, dnscrypt-proxy:
-
Starts a listener immediately - Before the proxy fully initializes, a lightweight UDP listener starts handling captive portal queries. This "cold start" mode ensures connectivity checks pass even during startup.
-
Returns instant responses - Queries for domains in the map file get immediate responses without contacting upstream servers.
-
Uses short TTLs - Responses have a TTL of 1 second, so they won't be cached for long if the actual IP addresses change.
-
Supports both IPv4 and IPv6 - A records and AAAA records are both handled based on the IPs in your map file.
Configuration
Enable the feature by uncommenting the map_file line in the [captive_portals] section of your dnscrypt-proxy.toml:
[captive_portals]
map_file = 'captive-portals.txt'
The path can be absolute or relative to the configuration file location. An example file example-captive-portals.txt is included with dnscrypt-proxy.
File format
The map file is a simple text file with one entry per line:
hostname IP1, IP2, IP3
Rules:
- Hostname and IPs are separated by whitespace (spaces or tabs)
- Multiple IPs are separated by commas
- IPv6 addresses don't need brackets (no port numbers involved)
- Lines starting with
#are comments - Wildcards are not supported - use exact hostnames only
Example:
# Apple connectivity check
captive.apple.com 17.253.109.201, 17.253.113.202
# Google/Android connectivity check
connectivitycheck.gstatic.com 64.233.162.94, 74.125.132.94
# Windows NCSI
www.msftncsi.com 13.107.4.52
dns.msftncsi.com 131.107.255.255, fd3e:4f5a:5b81::1
Platform-specific entries
Apple (macOS, iOS)
captive.apple.com 17.253.109.201, 17.253.113.202
Android
connectivitycheck.gstatic.com 64.233.162.94, 64.233.164.94, 64.233.165.94
connectivitycheck.android.com 64.233.162.100, 64.233.162.101, 64.233.162.102
Windows
www.msftncsi.com 2.16.106.89, 2.16.106.91, 23.0.175.137
dns.msftncsi.com 131.107.255.255, fd3e:4f5a:5b81::1
www.msftconnecttest.com 13.107.4.52
ipv6.msftconnecttest.com 2a01:111:2003::52
For additional Windows-specific configuration, see Network Connectivity Status Indicator (NCSI).
DNS64
The ipv4only.arpa domain is used by devices to detect NAT64/DNS64 networks:
ipv4only.arpa 192.0.0.170, 192.0.0.171
NTP servers
Adding NTP server entries can help with time synchronization during early boot, before the proxy is fully operational:
time.google.com 216.239.35.0, 2001:4860:4806::
time.apple.com 17.253.34.123
time.windows.com 168.61.215.74
Interaction with DNS rebinding protection
If you're using DNS rebinding protection and a captive portal domain resolves to a private IP address, you can either:
- Add the domain to your
allowed-names.txtfile - Add the domain to your captive portals map file with its expected IP
The captive portals feature takes precedence and returns responses before rebinding protection is applied.