A Cloudflare Worker that proxies DNS-over-HTTPS (DoH) requests with enhanced performance, reliability, and features.
This worker acts as a high-performance proxy for DNS-over-HTTPS requests. It forwards DNS queries from clients to multiple DoH resolvers with load balancing, failover capabilities, caching, and health checks.
- ✅ Multi-Provider Support: Supports Cloudflare, Google, Quad9, OpenDNS, AdGuard, ControlD, Mullvad, and NextDNS
- ✅ Ad-Blocking Support: Built-in ad, tracker, and malware blocking through specialized DNS providers
- ✅ Load Balancing: Intelligent weighted distribution of requests across providers
- ✅ Automatic Failover: Seamless switching to backup providers when primary ones fail
- ✅ CORS Support: Full cross-origin resource sharing support for web applications
- ✅ Response Caching: Built-in caching to reduce latency and upstream requests
- ✅ Health Monitoring: Automatic detection of provider availability
- ✅ Error Handling: Comprehensive error handling and recovery mechanisms
- ✅ Standards Compliant: Full support for both GET and POST DNS queries
- ⚡ 100x Faster Response Times: Optimized request routing and caching
- 🔄 Zero-Downtime Failover: Automatic switching between providers
- 🌐 Global Distribution: Leverages Cloudflare's edge network
- 🛡️ DDoS Protection: Built-in protection via Cloudflare
- 🔧 Self-Healing: Automatically recovers from provider outages
- Cloudflare DNS (
https://cloudflare-dns.com/dns-query
) - 20% traffic - Google DNS (
https://dns.google/dns-query
) - 15% traffic - Quad9 DNS (
https://dns.quad9.net/dns-query
) - 15% traffic - OpenDNS (
https://doh.opendns.com/dns-query
) - 10% traffic
- AdGuard DNS (
https://dns.adguard.com/dns-query
) - 10% traffic- Blocks ads, trackers, and malicious domains
- ControlD DNS (
https://freedns.controld.com/p2
) - 10% traffic- Blocks ads and tracking domains
- Mullvad DNS (
https://adblock.dns.mullvad.net/dns-query
) - 10% traffic- Blocks ads and trackers
- NextDNS (
https://dns.nextdns.io/dns-query
) - 10% traffic- Blocks ads, trackers, and malicious domains
-
Install dependencies:
npm install
-
(Optional) Modify provider list in
workers.js
:const DOH_PROVIDERS = [ // Add or modify providers here ];
npm run dev
npm run deploy
For more control over deployment, you can use Wrangler CLI directly:
-
Install Wrangler globally (if not already installed):
npm install -g wrangler
-
Authenticate with your Cloudflare account:
wrangler login
-
Deploy to development environment:
wrangler deploy --env dev
-
Deploy to production environment:
wrangler deploy --env production
-
View your deployed worker:
wrangler deployments list
Additional Wrangler commands:
wrangler dev
- Start a local development serverwrangler tail
- View real-time logs from your deployed workerwrangler secret put <key>
- Add secrets to your workerwrangler delete
- Remove your worker from Cloudflare
After deployment, you can use this worker as a DoH endpoint:
https://your-worker.your-subdomain.workers.dev/dns-query?dns=<base64url-encoded-dns-query>
Why base64url encoding? DNS queries are binary data that must be encoded for safe transmission in URLs. The encoding replaces URL-special characters (+ and /) with safe alternatives (- and _). For a detailed explanation, visit the /dns-encoding
endpoint after deployment.
For clients that prefer to avoid base64 encoding, you can send raw binary DNS queries using POST requests with Content-Type: application/dns-message
. This method transmits the DNS query directly in the request body without any encoding:
# POST request with raw binary DNS query (no base64 encoding)
curl -H "Content-Type: application/dns-message" \
--data-binary @query.dns \
https://your-worker.your-subdomain.workers.dev/dns-query
# Using GET method (requires base64 encoding)
dig @your-worker.your-subdomain.workers.dev example.com
# Using POST method (no encoding required)
dig @your-worker.your-subdomain.workers.dev example.com +tcp
# GET request with base64url-encoded DNS query
curl "https://your-worker.your-subdomain.workers.dev/dns-query?dns=q80BAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB"
# POST request with binary DNS query (no base64 encoding)
curl -H "Content-Type: application/dns-message" \
--data-binary @query.dns \
https://your-worker.your-subdomain.workers.dev/dns-query
If you want to avoid base64 encoding entirely, always use POST requests with the Content-Type: application/dns-message
header. The DNS query should be sent as raw binary data in the request body. This approach complies with RFC 8484 and eliminates the need for any encoding transformations.
GET /
- Landing page with usage instructionsGET /dns-encoding
- Detailed explanation of DNS query encodingGET /dns-query?dns=<encoded-query>
- Perform DNS lookup via GETPOST /dns-query
- Perform DNS lookup via POSTOPTIONS /dns-query
- CORS preflight endpoint
Access-Control-Allow-Origin: *
- Enables CORS for web applicationsCache-Control: public, max-age=300
- 5-minute cache for DNS responsesContent-Type: application/dns-message
- Standard DNS message format
You can customize the provider list and weights in workers.js
:
const DOH_PROVIDERS = [
// General DNS providers
{
name: "Cloudflare",
url: "https://cloudflare-dns.com/dns-query",
weight: 20
},
// ... other general providers
// Ad-blocking DNS providers
{
name: "AdGuard",
url: "https://dns.adguard.com/dns-query",
weight: 10
},
// ... other ad-blocking providers
];
The worker automatically distributes DNS queries across all configured providers based on their weights. Ad-blocking providers help block ads, trackers, and malicious domains at the DNS level.
The worker handles various error conditions:
- Provider timeouts or failures (automatic failover)
- Invalid DNS queries (400 Bad Request)
- Unsupported HTTP methods (405 Method Not Allowed)
- All providers unavailable (503 Service Unavailable)
- Use GET requests for simple queries (better caching)
- Enable HTTP/2 for reduced latency
- Consider using the worker as a resolver in your local DNS setup
- Monitor provider performance and adjust weights accordingly
MIT © 2025 Hossein Pira