# Cloudflare Gateway DNS on UniFi, Linux, Windows, macOS and iOS

> Send a device's DNS to a Cloudflare Zero Trust Gateway DNS location, encrypted with DNS-over-HTTPS (DoH)
> or DNS-over-TLS (DoT). UniFi needs the endpoint as an sdns:// stamp; Linux uses systemd-resolved; Windows 11
> uses a PowerShell script; Apple devices use a configuration profile. Interactive version: https://sdns.noraina.cloud/

Values in CAPITALS are placeholders. To get this guide with a location's values filled in, request
`https://sdns.noraina.cloud/guide.md?endpoint=<DoH endpoint>`, optionally with `&device=unifi|linux|windows|apple`.

## 1. Create a DNS location in Cloudflare

In the Cloudflare Zero Trust dashboard, open **Gateway → DNS Locations → Add a location**.

1. Name the location and save it.
2. Open it and turn on **DNS over HTTPS**. Turn on **DNS over TLS** too if any Linux machines will use it.
3. Copy the DoH endpoint. It looks like `https://abc123xyz.cloudflare-gateway.com/dns-query`.

Leave the source IP empty. The DoH and DoT hostnames identify the location on their own, so a dynamic IP
doesn't matter. Treat the endpoint like a password: anyone who has it can use the location.

## 2. Set up the device

### UniFi

UniFi gateways take DoH servers as `sdns://` stamps, so the endpoint is converted first.

Stamp for UniFi:

```text
Paste the endpoint on https://sdns.noraina.cloud/ or call https://sdns.noraina.cloud/api/stamp?url=<DoH endpoint>
```

The stamp declares DNSSEC, no logs and no filter (`props=7`), with no fixed address and no certificate pin.


Add it to UniFi, in **Settings → CyberSecure → Encrypted DNS**:

1. Add a custom server and paste the stamp.
2. Apply, then confirm the entry shows as active.

Keep a plain DNS server set on the WAN. The gateway needs it to resolve the DoH hostname on boot.

### Linux (systemd-resolved, DNS-over-TLS)

systemd-resolved speaks DNS-over-TLS natively, so nothing needs installing. It wants each server as an IP
address followed by `#` and the hostname its TLS certificate is checked against. You need systemd 247 or
later: check with `systemctl --version`. Turn on **DNS over TLS** on the location.

Keep a copy of the current file, then open it:

```sh
sudo cp /etc/systemd/resolved.conf /etc/systemd/resolved.conf.bak
sudoedit /etc/systemd/resolved.conf
```

Under `[Resolve]`, set these lines. Uncomment or replace any that are already there:

```ini
[Resolve]
DNS=IPV4-ADDRESS-1#YOUR-SUBDOMAIN.cloudflare-gateway.com IPV4-ADDRESS-2#YOUR-SUBDOMAIN.cloudflare-gateway.com
DNSOverTLS=yes
Domains=~.
FallbackDNS=
```

- `DNS=` the location's servers, each with its TLS hostname.
- `DNSOverTLS=yes` only ever uses TLS. If port 853 is blocked, lookups fail rather than fall back to plain DNS.
- `Domains=~.` sends every domain to these servers instead of the ones your network hands out.
- `FallbackDNS=` left empty turns off the built-in plain-DNS fallback.

Cloudflare Gateway uses the same hostname for DoH and DoT. If the location shows a different DNS over TLS
hostname, use that one after each `#`.

Make sure programs ask systemd-resolved: `/etc/resolv.conf` must link to `/run/systemd/resolve/stub-resolv.conf`.

```sh
ls -l /etc/resolv.conf
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
```

Stop the network's DNS from creeping back. With NetworkManager, on each connection:

```sh
nmcli -t -f NAME connection show --active
sudo nmcli connection modify "CONNECTION NAME" ipv4.ignore-auto-dns yes ipv6.ignore-auto-dns yes
sudo nmcli connection up "CONNECTION NAME"
```

With systemd-networkd instead, add `UseDNS=no` under `[DHCPv4]`, `[DHCPv6]` and `[IPv6AcceptRA]` in the
interface's `.network` file.

Restart and check. Under **Global**, `resolvectl status` should show `+DNSOverTLS` and one of the servers
above as **Current DNS Server**:

```sh
sudo systemctl restart systemd-resolved
resolvectl status
resolvectl query example.com
```

To undo it, restore `resolved.conf.bak` and restart systemd-resolved.

### Windows 11 and Windows Server 2022+ (DNS-over-HTTPS)

Windows 10 can't encrypt DNS system-wide; set the endpoint as a custom secure DNS provider in the browser
instead. Windows only uses DoH for servers in its list of known DoH servers, which maps each IP address to a
URL template. The script adds the location's addresses to that list with no fallback to plain DNS, sets them
as the DNS servers of every connected adapter, and clears the DNS cache.

Run it in PowerShell as administrator (right-click Start → **Terminal (Admin)**):

```powershell
$Template = 'https://YOUR-SUBDOMAIN.cloudflare-gateway.com/dns-query'
$Servers  = @('IPV4-ADDRESS-1', 'IPV4-ADDRESS-2')

# 1. Tell Windows these addresses speak DoH with this template, never plain DNS.
foreach ($ip in $Servers) {
    $doh = @{ ServerAddress = $ip; DohTemplate = $Template; AllowFallbackToUdp = $False; AutoUpgrade = $True }
    if (Get-DnsClientDohServerAddress -ServerAddress $ip -ErrorAction SilentlyContinue) {
        Set-DnsClientDohServerAddress @doh
    } else {
        Add-DnsClientDohServerAddress @doh
    }
}

# 2. Use them on every connected network adapter.
Get-NetAdapter -Physical | Where-Object Status -eq 'Up' | ForEach-Object {
    Set-DnsClientServerAddress -InterfaceIndex $_.ifIndex -ServerAddresses $Servers
}

# 3. Forget answers cached from the old servers.
Clear-DnsClientCache
```

Check it. In **Settings → Network & internet → Wi-Fi (or Ethernet) → Hardware properties**, the DNS servers
should read **Encrypted**:

```powershell
# The addresses Windows now knows as DoH servers for your template
Get-DnsClientDohServerAddress | Where-Object DohTemplate -eq 'https://YOUR-SUBDOMAIN.cloudflare-gateway.com/dns-query'

# The DNS servers each adapter uses
Get-DnsClientServerAddress | Where-Object ServerAddresses

Resolve-DnsName example.com
```

Run the script again after plugging in a new network adapter. To enforce it, set the Group Policy
**Computer Configuration → Administrative Templates → Network → DNS Client → Configure DNS over HTTPS (DoH)
name resolution** to *Require DoH*. Don't do that on domain-joined machines: Active Directory needs plain DNS.

Undo:

```powershell
# Back to the DNS servers DHCP hands out
Get-NetAdapter -Physical | Where-Object Status -eq 'Up' | ForEach-Object {
    Set-DnsClientServerAddress -InterfaceIndex $_.ifIndex -ResetServerAddresses
}

# Forget the template
Get-DnsClientDohServerAddress | Where-Object DohTemplate -eq 'https://YOUR-SUBDOMAIN.cloudflare-gateway.com/dns-query' | ForEach-Object {
    Remove-DnsClientDohServerAddress -ServerAddress $_.ServerAddress
}
Clear-DnsClientCache
```

### macOS, iOS and iPadOS (configuration profile)

iPhone, iPad and Mac take encrypted DNS from a `.mobileconfig` profile with the DoH endpoint in it.

Download the profile: https://sdns.noraina.cloud/profile.mobileconfig?url=<DoH endpoint>

Add `&ssid=<Wi-Fi name>` once per network where it should stay off. On an iPhone or iPad the profile has to
be opened in Safari on the device itself.

iPhone and iPad:

1. In Safari, open the link and tap **Allow**.
2. Open **Settings → General → VPN & Device Management** and tap the downloaded profile.
3. Tap **Install**, enter the passcode and confirm.
4. If another DNS profile or app is installed, pick this one under **VPN & Device Management → DNS**.

Mac:

1. Download the profile. Make sure the file ends in `.mobileconfig`, not `.mobileconfig.txt`, and open it.
2. Open **System Settings → General → Device Management** (macOS 13: Privacy & Security → Profiles).
3. Double-click the profile, review it and click **Install**. macOS asks for an administrator password.

The profile isn't signed, so it shows as *Unverified*. A VPN, iCloud Private Relay or the Cloudflare One
client takes priority over it. Remove it from the same Device Management screen.

## 3. Check it works

Open a site you block in a Gateway policy: it should show the block page. The query also appears under
**Logs → DNS** in Zero Trust, tagged with the location. For DoH and DoT details, see https://one.one.one.one/help/.

## Questions

### How do I use Cloudflare Gateway DNS on a UniFi gateway?

UniFi takes custom encrypted DNS servers as DNS stamps (sdns://), not as DoH URLs. In Cloudflare Zero Trust, open Gateway → DNS Locations, create a location, turn on DNS over HTTPS and copy its DoH endpoint (`https://<id>.cloudflare-gateway.com/dns-query`). Paste it on this page, or call `/api/stamp?url=<endpoint>`, to get the stamp. In UniFi, open Settings → CyberSecure → Encrypted DNS, add a custom server and paste the stamp. Keep a plain DNS server on the WAN so the gateway can resolve the DoH hostname on boot.

### Why does UniFi want an sdns:// stamp instead of the DoH URL?

UniFi's encrypted DNS takes servers in the DNS stamp format used by dnscrypt-proxy. A stamp packs the protocol (0x02 for DoH), the properties the resolver declares, the hostname and the path into one base64url string. For a Cloudflare Gateway location only the hostname and path matter: leave the IP address and certificate hash empty.

### Do I need to set a source IP on the Cloudflare Gateway DNS location?

No. With DNS over HTTPS or DNS over TLS the location's hostname identifies it, so a dynamic public IP doesn't matter. A source IP is only needed for plain DNS on port 53.

### How do I use Cloudflare Gateway DNS-over-TLS on Linux?

Turn on DNS over TLS on the location, then set `DNS=<ip>#<id>.cloudflare-gateway.com`, `DNSOverTLS=yes`, `Domains=~.` and an empty `FallbackDNS=` in /etc/systemd/resolved.conf and restart systemd-resolved (systemd 247 or later). Make /etc/resolv.conf a link to /run/systemd/resolve/stub-resolv.conf.

### How do I use Cloudflare Gateway DNS-over-HTTPS on Windows?

Windows 11 and Windows Server 2022 or later: register the location's IP addresses as DoH servers with `Add-DnsClientDohServerAddress` and the location's template, then set those IPs as the adapters' DNS servers. This page generates the PowerShell script. Windows 10 can't encrypt DNS system-wide; set the endpoint as a custom secure DNS provider in the browser.

### How do I use Cloudflare Gateway DNS on an iPhone, iPad or Mac?

Install a configuration profile (.mobileconfig) with a DNS-over-HTTPS payload pointing at the location's endpoint. Download it from `/profile.mobileconfig?url=<endpoint>`, in Safari on the device itself for iOS and iPadOS, then install it under Settings → General → VPN & Device Management (macOS: System Settings → General → Device Management). It shows as Unverified because it isn't signed.

### How do I check that DNS queries are reaching Cloudflare Gateway?

Open a site you block in a Gateway policy: it should show the block page. The query also appears under Logs → DNS in Zero Trust, tagged with the location.

### Can a script or an AI agent use this tool?

Yes. `GET /guide.md?endpoint=<DoH URL>&device=unifi|linux|windows|apple` returns the steps as Markdown with the stamp, addresses and scripts filled in. `/api/stamp`, `/api/resolve` and `/profile.mobileconfig` are plain HTTP GETs, described in `/openapi.json` and `/llms.txt`.

## API

All GET, no authentication, CORS open. OpenAPI: https://sdns.noraina.cloud/openapi.json

- `https://sdns.noraina.cloud/guide.md?endpoint=<DoH URL>[&device=unifi|linux|windows|apple]` this guide, filled in
- `https://sdns.noraina.cloud/api/stamp?url=<DoH URL>[&props=7][&addr=][&hash=][&format=json]` sdns:// stamp
- `https://sdns.noraina.cloud/api/resolve?host=<hostname>` IPv4 and IPv6 addresses as JSON
- `https://sdns.noraina.cloud/profile.mobileconfig?url=<DoH URL>[&ssid=<Wi-Fi name>…]` Apple profile

---

Built by Noraina (https://www.noraina.cloud). Stamps follow the DNS stamp specification
(https://dnscrypt.info/stamps-specifications); profiles follow https://github.com/paulmillr/encrypted-dns.
