← Back to Blog

Three Netlify Functions That Let Browser-Based Audits See Past the Browser

Three Netlify Functions That Let Browser-Based Audits See Past the Browser

A browser-side audit tool can see a lot. Rendered HTML. HTTP response headers (minus the ones the CORS preflight hides). JavaScript runtime state. What it can't see from the browser: DNS records, TLS handshake internals, anything that needs a stable User-Agent, or the result of running the same fetch three times to check whether a finding is real or just a CDN-race transient.

Three small Netlify functions, added to the site this week, fix all three gaps. They fortify the audit tools that already exist (Mega Security Analyzer, PQC Analyzer, DNS/Email Auth Audit, Security Headers Audit) without forcing anyone to install a desktop scanner.

probe-dns

The browser's fetch() can't issue a DNS query. DNS-over-HTTPS (DoH) is queryable from the browser, and the existing DNS Email Auth Audit does exactly that, but running it through a shared serverless function gives three upgrades:

  1. Retry + fallback. The function queries Cloudflare DoH first, falls back to Google DoH if Cloudflare times out. Either endpoint can be down; both being down at the same time is rare.
  2. Response normalization. DoH response shape is standardized but slightly different between providers. The function normalizes to one structure.
  3. Digest fields. Instead of handing raw records to every caller, the function parses SPF, DMARC, DKIM, CAA out of the TXT responses and returns clean boolean flags plus the raw record. Every tool that consumes DNS gets the same parsed form.

Query: ?domain=example.com&type=DMARC. Returns JSON with both raw records and parsed digest.

Source: netlify/functions/probe-dns.mjs in the repo.

probe-tls

The browser can't inspect a TLS handshake. The negotiated cipher, protocol version, certificate chain, OCSP stapling status, and hybrid-KEX support are all server-side information that only a server-side probe can read. Node's built-in tls.connect() gives access to all of it, and exposes an ecdhCurve option that lets the probe specifically request a post-quantum curve to test hybrid-KEX support.

The function runs two handshakes against the target:

  1. Standard handshake with default curves. Captures protocol, cipher, cert chain.
  2. PQC handshake restricting ecdhCurve to X25519MLKEM768 first, X25519Kyber768Draft00 as fallback. If handshake 2 succeeds, the server supports hybrid post-quantum key exchange. If handshake 2 fails, the server is classical-only.

Result includes days-until-expiry calc, issuer info, cipher standard name, and a PQC verdict.

Source: netlify/functions/probe-tls.mjs.

revalidate

The false-positive-suppression function. Many audit tools flag findings based on a single fetch. Caching layers, CDN shield races, rolling config deploys, and partially-warmed edges all produce single-fetch false positives.

Query: ?url=https://target&signal=missing_hsts&probes=3. The function fetches the target URL 3 times (configurable, 2-5), 700ms apart, and evaluates the specific signal against each response. Verdict:

  • CONFIRMED if ≥ 2 of 3 probes see the issue.
  • TRANSIENT if exactly 1 of 3 probes saw it — treat as intermittent, not urgent.
  • NOT_OBSERVED if 0 of 3 saw it — downgrade the original fail to info; it was a false positive from the single-probe scanner.

Supported signals: missing_hsts, missing_csp, xframe_missing, soft_404, canonical_to_404, meta_generator_wp, xmlrpc_reachable.

The signal set is deliberately small. Each signal needs a clear, deterministic pass/fail test. Adding signals is a matter of writing the evaluation function, not tuning thresholds.

Source: netlify/functions/revalidate.mjs.

What this fortification changes for users

Before: the audit reports a critical fail. User goes to fix it. User discovers the issue was already fixed two hours ago at the CDN layer, or never existed on the origin only on one edge node. User loses an afternoon.

After: the audit reports a finding labeled "revalidation: NOT_OBSERVED (0/3)" and downgrades it to info with a note that single-probe scanners would have flagged it. User reads the note, moves on, keeps their afternoon.

Across a typical scan, 10-20 percent of findings get downgraded or re-categorized by revalidation. Over a year of audits, that's days of wasted follow-up.

Why three functions instead of one

Each function does one thing and does it well. The alternative would be a single monster function that takes a mode= parameter, branches into TLS or DNS or revalidation paths, and becomes a maintenance nightmare. Three small focused functions stay readable, stay testable, and let each one cache independently (TLS probes cache for 5 minutes, DNS for 1 minute, revalidation for 30 seconds).

Netlify Functions free tier allows 125K invocations/month across all functions on a site. These three together, at normal audit traffic, use well under 10% of that budget.

The open-source dimension

All three function source files are in the netlify/functions/ folder of the jwatte-site repo. They're published under the same free terms as the rest of the site's tools. If another audit-tools maintainer wants to use them, the interface is documented in the header comment of each function.

Related reading

Fact-check notes and sources

  • Cloudflare DoH endpoint documentation, https://developers.cloudflare.com/1.1.1.1/encrypted-dns/dns-over-https/
  • Google Public DNS DoH documentation.
  • Node.js tls module documentation (v22 LTS) for tls.connect() + ecdhCurve option.
  • IETF draft-kwiatkowski-tls-ecdhe-mlkem for the hybrid-KEX spec.
  • Netlify Functions documentation for function quotas and deployment.

This post is informational, not security-consulting, legal, or compliance advice. The functions run against publicly-served URLs only. Always respect target-site terms of service and rate-limit politely. Do not point any of these probes at third-party infrastructure you don't own without explicit written authorization.

← Back to Blog

Accessibility Options

Text Size
High Contrast
Reduce Motion
Reading Guide
Link Highlighting
Accessibility Statement

J.A. Watte is committed to ensuring digital accessibility for people with disabilities. This site conforms to WCAG 2.1 and 2.2 Level AA guidelines.

Measures Taken

  • Semantic HTML with proper heading hierarchy
  • ARIA labels and roles for interactive components
  • Color contrast ratios meeting WCAG AA (4.5:1)
  • Full keyboard navigation support
  • Skip navigation link
  • Visible focus indicators (3:1 contrast)
  • 44px minimum touch/click targets
  • Dark/light theme with system preference detection
  • Responsive design for all devices
  • Reduced motion support (CSS + toggle)
  • Text size customization (14px–20px)
  • Print stylesheet

Feedback

Contact: jwatte.com/contact

Full Accessibility StatementPrivacy Policy

Last updated: April 2026