Running Tailscale and Cloudflare WARP simultaneously has historically been a recipe for networking headaches. The two services compete for routing control, causing authentication failures, broken peer connections, and the dreaded “no route to host” errors.
After troubleshooting with AI, I finally achieved stable coexistence. The solution involves configuring WARP’s split tunnel to exclude Tailscale’s traffic entirely.
Table of contents
Open Table of contents
The problem: competing VPN tunnels
Both Tailscale and Cloudflare WARP operate as system-wide VPNs that want to intercept and route your network traffic. When both are active:
- WARP intercepts Tailscale control plane traffic — Your device cannot reach
controlplane.tailscale.comfor authentication and coordination - WARP captures DERP relay connections — Tailscale’s backup relay servers become unreachable
- WARP hijacks internal tailnet traffic — Devices on your private network cannot communicate
- MagicDNS queries fail —
.ts.netdomain lookups get sent to the wrong resolver
The result is that Tailscale shows “connected” but nothing actually works. Connections time out, authentication loops fail, and you are left wondering which service to disable.
The solution: WARP split tunnel exclusions
The fix involves “punching holes” in WARP’s tunnel so it stops intercepting Tailscale traffic. You need to exclude four categories of addresses in your Cloudflare Zero Trust configuration.
Step 1: Exclude Tailscale service ranges
These IP ranges allow Tailscale to communicate with its control plane and logging infrastructure. Add them to your Split Tunnel (Exclude) list:
Control plane:
192.200.0.0/24(IPv4)2606:B740:49::/48(IPv6)
Logging:
199.165.136.0/24(IPv4)2606:B740:1::/48(IPv6)
These ranges are documented in Tailscale’s firewall ports reference.
Step 2: Exclude tailnet traffic
These ranges cover the virtual IP addresses assigned to devices on your tailnet:
IPv4 (Carrier-Grade NAT range):
100.64.0.0/10
IPv6 (Unique Local Address range):
fd7a:115c:a1e0::/48
Step 3: Exclude control domains
Add these as Domain exclusions to ensure OAuth authentication and API calls work correctly:
controlplane.tailscale.comlogin.tailscale.comlog.tailscale.comapi.tailscale.com
Step 4: Configure local domain fallback (mode-dependent)
This step only applies if your WARP deployment uses DNS filtering. Check your WARP service mode to determine if this applies to you.
If you run WARP with DNS filtering enabled:
In Cloudflare Zero Trust:
- Navigate to Settings → WARP Client → Local Domain Fallback
- Add
ts.netpointing to100.100.100.100
This ensures queries like my-server.ts.net get sent to Tailscale’s internal DNS resolver instead of WARP trying to resolve them on the public internet.
If you run WARP in “Secure Web Gateway without DNS Filtering” mode:
This step does not apply to you. In this mode, WARP does not intercept DNS queries at all—your local DNS resolver (such as AdGuard Home) handles all DNS resolution directly. Configure your local resolver to forward .ts.net queries to 100.100.100.100 instead.
Complete configuration summary
Here is the full list of exclusions you need to add:
IP exclusions (Split Tunnel → Exclude)
| Address | Type | Description |
|---|---|---|
100.64.0.0/10 | IPv4 | Tailscale IPv4 CGNAT range |
fd7a:115c:a1e0::/48 | IPv6 | Tailscale IPv6 ULA range |
192.200.0.0/24 | IPv4 | Tailscale control plane |
2606:B740:49::/48 | IPv6 | Tailscale control plane |
199.165.136.0/24 | IPv4 | Tailscale logging |
2606:B740:1::/48 | IPv6 | Tailscale logging |
Domain exclusions (Split Tunnel → Exclude)
| Domain | Description |
|---|---|
controlplane.tailscale.com | Coordination server |
login.tailscale.com | Authentication |
log.tailscale.com | Telemetry |
api.tailscale.com | API access |
Local domain fallback (DNS filtering modes only)
| Domain | DNS Server |
|---|---|
ts.net | 100.100.100.100 |
Bonus: Automating DERP relay exclusions
Tailscale uses DERP (Designated Encrypted Relay for Packets) servers as fallback when direct peer-to-peer connections fail. Excluding these IPs ensures reliable relay connectivity.
The DERP server IPs change occasionally as Tailscale adds new regions. Here is a Python script that fetches the latest DERP map and updates your Cloudflare split tunnel automatically:
import requests
# Configuration - replace with your values
CF_API_TOKEN = "<YOUR_CLOUDFLARE_API_TOKEN>"
CF_ACCOUNT_ID = "<YOUR_ACCOUNT_ID>"
CF_POLICY_ID = "<YOUR_POLICY_ID>"
def get_latest_exclusions():
# Fetch current DERP relay IPs from Tailscale
derp_data = requests.get("https://controlplane.tailscale.com/derpmap/default").json()
derp_ips = set()
for region in derp_data["Regions"].values():
for node in region.get("Nodes", []):
if node.get("IPv4"):
derp_ips.add(node["IPv4"] + "/32")
if node.get("IPv6"):
derp_ips.add(node["IPv6"] + "/128")
# Define static Tailscale infrastructure
static_rules = [
{"address": "100.64.0.0/10", "description": "Tailscale IPv4 CGNAT"},
{"address": "fd7a:115c:a1e0::/48", "description": "Tailscale IPv6 ULA"},
{"address": "192.200.0.0/24", "description": "Tailscale Control Plane V4"},
{"address": "2606:B740:49::/48", "description": "Tailscale Control Plane V6"},
{"address": "199.165.136.0/24", "description": "Tailscale Logs V4"},
{"address": "2606:B740:1::/48", "description": "Tailscale Logs V6"},
{"address": "controlplane.tailscale.com", "description": "Tailscale Domain"},
{"address": "login.tailscale.com", "description": "Tailscale Domain"},
{"address": "ts.net", "description": "Tailscale MagicDNS"}
]
# Add DERP relay IPs
for ip in derp_ips:
static_rules.append({"address": ip, "description": "Tailscale DERP Relay"})
return static_rules
def update_cloudflare(new_entries):
url = f"https://api.cloudflare.com/client/v4/accounts/{CF_ACCOUNT_ID}/devices/policy/{CF_POLICY_ID}/exclude"
headers = {
"Authorization": f"Bearer {CF_API_TOKEN}",
"Content-Type": "application/json"
}
# PUT replaces the entire list - include your other manual rules too
response = requests.put(url, headers=headers, json=new_entries)
print("Update Status:", response.status_code, response.text)
if __name__ == "__main__":
rules = get_latest_exclusions()
update_cloudflare(rules)You can run this script periodically (for example, weekly via cron) to keep DERP exclusions current. Note that the PUT request replaces the entire exclusion list, so include any non-Tailscale exclusions in your configuration.
Why this works
The key insight is understanding what traffic each service needs:
-
Authentication works — By excluding
2606:B740:49::/48and the control domains, login requests reach Tailscale’s coordination servers directly without hitting WARP’s internal routing table -
Relay connections succeed — By excluding DERP IPs, Tailscale can always reach a relay server via your standard ISP connection when peer-to-peer is blocked
-
DNS resolves correctly — Local domain fallback prevents WARP from trying to resolve
.ts.netaddresses on the public internet, allowing Tailscale’s MagicDNS to handle them -
Tailnet traffic flows — By excluding the
100.64.0.0/10andfd7a:115c:a1e0::/48ranges, packets between your devices bypass WARP entirely
AdGuard Home integration
If you run AdGuard Home as your local DNS server alongside Tailscale, DNS configuration depends on your WARP mode.
For “Secure Web Gateway without DNS Filtering” mode (my setup):
In this mode, WARP does not intercept DNS queries at all. AdGuard Home handles all DNS resolution directly, which simplifies things considerably. Configure AdGuard Home to forward .ts.net queries to Tailscale’s MagicDNS:
- In AdGuard Home, go to Settings → DNS Settings → Upstream DNS servers
- Add a DNS rewrite or upstream rule:
[/ts.net/]100.100.100.100
This ensures AdGuard Home forwards .ts.net queries to Tailscale while handling everything else normally.
For WARP modes with DNS filtering:
You need to configure Local Domain Fallback in Cloudflare Zero Trust (as described in Step 4). This tells WARP to pass .ts.net queries to your local resolver, which then forwards them to Tailscale.
The DNS flow becomes: Query → WARP (passes through) → AdGuard Home → Tailscale MagicDNS.
Verification steps
After applying the configuration:
- Restart both services — Toggle WARP off and on, then do the same for Tailscale
- Check Tailscale status — Run
tailscale statusand verify all peers show as reachable - Test MagicDNS — Try
ping my-server.ts.netand confirm it resolves to a100.x.x.xaddress - Test peer connectivity — SSH or ping between devices on your tailnet
- Verify WARP still works — Confirm that non-Tailscale traffic still routes through WARP
If peers show as “offline” or DNS fails, double-check that all exclusions are properly saved in Cloudflare Zero Trust.
Conclusion
Running Tailscale and Cloudflare WARP together is finally achievable with the right split tunnel configuration. The key is excluding all Tailscale infrastructure from WARP’s tunnel: control plane IPs, tailnet address ranges, control domains, and DERP relay servers.
Once configured, both services coexist peacefully. WARP handles your general internet traffic with its security features, while Tailscale maintains your private mesh network with full MagicDNS and peer-to-peer connectivity.
No more choosing between them.
Tested on macOS Sequoia 15.7.3 with Tailscale 1.92.3 and Cloudflare WARP 2025.10.186.0.