Browser-based network diagnostics toolkit — DNS security, speed test, ad blocker analysis, and HTTP security header scanning. No installs, no accounts; every check runs client-side against Cloudflare's edge network.
Live: netcheck.oilygold.xyz
- Public IP detection with geolocation, ISP/ASN, and Cloudflare PoP
- Reachability and latency testing across 8 public resolvers (Cloudflare, Google, Quad9, OpenDNS, AdGuard DNS, Cloudflare Families, NextDNS, Mullvad), probed over RFC 8484 DoH wire format so every resolver that supports DoH answers
- Security assessment: DNSSEC validation, DNS-over-HTTPS, malware/tracker filtering, WebRTC IP leak detection
- Encrypted-DNS verdict takes the worst hop, not the best one: a router that forwards to your ISP's resolver, which forwards to Cloudflare, is reported as plaintext on the first hop rather than as encrypted because a recognised operator appeared somewhere in the chain
- ECS leak detection — flags resolvers that forward your client subnet upstream, and (with the probe below enabled) reports the subnet a hop actually forwarded about you, encrypted path or not
- Interactive lookup tool for A, AAAA, MX, NS, TXT, CNAME, SOA, SRV records
- Download/upload bandwidth, latency, and jitter against Cloudflare's edge,
speed.cloudflare.com, Netflix's fast.com, or Ookla Speedtest servers (nearest found via your city — proxies only the tiny discovery call; speed traffic goes browser → server direct) - Bufferbloat / loaded-latency grading — latency increase under a saturated link, taken from whichever direction queued worse, with loaded pings capped at 3s so a stalled probe counts instead of vanishing
- Progressive chunk sizing that adapts to connection speed: steps under 5 MB are warm-up only (TCP slow start is excluded from the clock), and the measured steps run 4 connections wide so a fast link isn't bounded by a single stream's window
- Letter grades (A+ to F) for both raw speed and bufferbloat
- 25+ tests across 6 categories: contextual ads, analytics & tracking, banner/display ads, error monitoring, social trackers, cookie/consent annoyances
- Fingerprint surface reported in its own panel, outside the score: clock precision, canvas readback stability, unmasked GPU model, core count, device memory and time zone, read from the APIs a fingerprinter would call
- Scores your blocker 0–100 with per-category breakdowns and per-test "why blocked" detail; categories are weighted by importance (high/medium/low) so blocking serious trackers counts more than cosmetic ads
- Detects 10 filter lists (EasyList, EasyPrivacy, Fanboy's Annoyances/Social, Peter Lowe's, Malware Domains, uBlock Filters, AdGuard Base/Tracking) plus Acceptable Ads whitelist status
- Identifies which blocker you're likely running (Brave Shields, uBlock Origin, AdGuard, browser-native, etc.)
- Test any custom URL as script + image
- Scans any URL's HTTP response and grades it A–F against 10 security headers (HSTS, CSP, X-Frame-Options, COOP/COEP/CORP, etc.)
- Multi-hop SSRF guard with private/loopback/link-local/multicast IP filtering (IPv4, IPv6, and IPv4-mapped IPv6) and redirect destination re-validation, rate-limited per IP
- Explains what each header protects against and flags what's missing
- Speed and ad-block results snapshot to local storage, with color-coded deltas between runs
- Memory-efficient streaming speed downloads via
ReadableStream - PWA with offline precaching and responsive Linear-inspired dark/light theme
- Export a full diagnostic report as Markdown or printable PDF — nothing leaves your device
- Bilingual UI (English / Traditional Chinese) with full keyboard accessibility
- Worker API endpoints that fan out server-side (headers scan, resolver checks, DNS compare) are per-IP rate-limited via Cloudflare's Rate Limiting binding, which counts across isolates; an in-worker counter is the fallback for
wrangler dev - Speed-test endpoints are limited by bytes moved per IP per minute, not by request count — egress is the cost there
Off by default, and off on netcheck.oilygold.xyz. This is the one check that needs a server outside Cloudflare, because Workers cannot serve UDP/53.
probe-server/ is an authoritative nameserver for a zone you delegate to it. The browser resolves <label>.<your-zone>, which forces every recursive resolver in the visitor's real path to query that nameserver directly — so it sees their actual resolver IPs, and any EDNS Client Subnet forwarded. A DoH-only page can never learn this about its visitor, because it only ever talks to the one resolver it picked.
The label is the SHA-256 of a read key the visitor's tab keeps to itself. Every resolver in the path sees the label, and so do the nameserver's own logs, so observing a query must not be enough to read the result back: only the key names the session, and it never travels in a DNS query. The Worker presents the key over TLS to fetch the result.
Enable it by setting all three on the Worker:
| Variable | Where | Example |
|---|---|---|
PROBE_SERVER_URL |
wrangler.toml [vars] |
https://ns-probe.example.com:8443 |
PROBE_ZONE |
wrangler.toml [vars] |
p.example.com |
PROBE_SECRET |
wrangler secret put |
— |
The URL must be https:// (localhost excepted): PROBE_SECRET travels in a request header, so a plaintext URL is refused and reported as unconfigured rather than sent in the clear.
With any of them missing, the client is told the probe does not exist here: it skips the lookup entirely, and the DNS card drops both the encrypted-DNS row and the "your recursive resolver" block rather than showing a check it cannot answer.
Running the nameserver — GCP always-free VM, zone delegation, systemd unit, and the reflection-hardening notes — is documented in probe-server/README.md.
- Runtime: Cloudflare Workers, static assets via the Workers Assets binding
- Frontend: Vite + TypeScript, compiled to vanilla JS/CSS (no framework)
- Build:
@cloudflare/vite-plugin— Workers runtime integrated directly into Vite dev/build - Design System: Linear-inspired dark theme — Inter Variable,
#08090acanvas, indigo-violet accents - DNS Lookups: resolvers probed over RFC 8484 DNS wire format (DoH POST) with a hand-rolled encoder/decoder in
src/shared/dns-wire.ts; interactive lookups via Cloudflare DNS-over-HTTPS
netcheck-site/
├── wrangler.toml # Cloudflare Worker config
├── vite.config.ts # Vite build configuration (Cloudflare plugin)
├── tsconfig.json # TypeScript configuration
├── index.html # Single-page app shell
├── probe-server/ # Authoritative nameserver for recursion-path probing
├── src/
│ ├── client/ # Frontend logic (TypeScript)
│ │ ├── app.ts # Entry point — tab routing, UI orchestration
│ │ ├── i18n.ts # Bilingual (en/zh-TW) translation strings
│ │ └── ... # Domain-specific logic (dns-check, speed-test, adblock-test, etc.)
│ ├── shared/ # Code shared between client, worker and probe server
│ │ ├── dns-wire.ts # RFC 8484 wire-format encoder/decoder
│ │ └── resolvers.ts # Public DNS resolver list
│ └── worker/ # Backend logic (TypeScript)
│ └── index.ts # Worker — API routes
└── public/
├── icon.svg, manifest.json, sw.js
└── css/styles.css # Linear design system implementation
npm install
npm run dev # Vite dev server with the Cloudflare Workers runtime
npm run typecheck # tsc --noEmit
npm test # node --test — no framework, no browser, no networkTo exercise the recursion probe locally, put the three variables in a gitignored
.dev.vars and run the nameserver alongside the dev server — see
probe-server/README.md for the local port to use
(5354; mDNS holds 5353 on macOS).
npm run deploy # vite build && wrangler deployMIT