<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mike Pultz</title>
    <description>The latest articles on DEV Community by Mike Pultz (@mikepultz).</description>
    <link>https://dev.to/mikepultz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3880804%2F445d0133-ad9e-443c-b307-0ee8423463fe.jpg</url>
      <title>DEV Community: Mike Pultz</title>
      <link>https://dev.to/mikepultz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mikepultz"/>
    <language>en</language>
    <item>
      <title>TLS Is Easy to Enable and Hard to Get Right</title>
      <dc:creator>Mike Pultz</dc:creator>
      <pubDate>Wed, 27 May 2026 16:15:34 +0000</pubDate>
      <link>https://dev.to/mikepultz/tls-is-easy-to-enable-and-hard-to-get-right-581f</link>
      <guid>https://dev.to/mikepultz/tls-is-easy-to-enable-and-hard-to-get-right-581f</guid>
      <description>&lt;p&gt;Enabling TLS is a solved problem. Get a certificate from Let's Encrypt, point your server at it, done. What's not solved is the configuration that comes after: cipher suites, protocol versions, session settings, OCSP stapling. The decisions that determine whether your TLS implementation is actually secure, or just technically present.&lt;/p&gt;

&lt;p&gt;The problem is that a misconfigured TLS setup looks identical to a correct one. Both show the padlock. Both say HTTPS. The difference only surfaces when someone runs a scan, or when an auditor asks why you're still accepting TLS 1.1, or when a CVE lands and you're not sure if you're exposed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's hard
&lt;/h2&gt;

&lt;p&gt;Every application handles TLS differently. The correct cipher string for Nginx is not the correct cipher string for Postfix. PostgreSQL's &lt;code&gt;ssl_min_protocol_version&lt;/code&gt; setting doesn't exist in MySQL, which uses &lt;code&gt;tls_version&lt;/code&gt; instead. RabbitMQ uses an Erlang term file. IIS uses registry keys. Kafka uses Java KeyStore files and properties that differ between brokers and clients.&lt;/p&gt;

&lt;p&gt;The underlying security requirements are identical across all of them: TLS 1.2 minimum, ECDHE for key exchange, AEAD for encryption. But applying those requirements to a specific piece of software means learning its configuration model, finding documentation that reflects current best practices, and figuring out which version introduced the settings you need.&lt;/p&gt;

&lt;p&gt;Most developers aren't TLS specialists. They're configuring TLS for a database or a message broker or a mail server that happens to be one component of a larger system, and they need a correct answer quickly, not a cryptography education.&lt;/p&gt;

&lt;h2&gt;
  
  
  The documentation gap
&lt;/h2&gt;

&lt;p&gt;Vendor documentation tends toward completeness, not correctness. A reference page will show every available TLS option without telling you which combinations are safe. Example configurations show what's possible, not what's recommended. Defaults often favor compatibility over security, which in this context means accepting protocol versions that RFC 8996 deprecated in 2021 and cipher suites that haven't been recommended since before that.&lt;/p&gt;

&lt;p&gt;AI assistants have the same problem at higher velocity: their training data includes every TLS guide ever written, including the outdated ones, and they'll confidently reproduce decade-old cipher strings without flagging that they've been superseded.&lt;/p&gt;

&lt;p&gt;The result is that the most natural paths to a TLS configuration produce configs that work but may not be secure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What GoodTLS does
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://goodtls.com" rel="noopener noreferrer"&gt;GoodTLS&lt;/a&gt; is a configuration reference for 48 applications across 8 categories: web servers, databases, message brokers, mail servers, DNS resolvers, VPN software, and more. Each guide gives you the specific configuration for that application, based on current standards, with no deprecated options and no hedging.&lt;/p&gt;

&lt;p&gt;The cipher suites are the same across every OpenSSL-based application on the site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-ECDSA-CHACHA20-POLY1305
ECDHE-RSA-CHACHA20-POLY1305
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every suite uses ECDHE for forward secrecy. Every suite uses AEAD encryption. None use CBC mode. None use static RSA key exchange. The list is the same whether you're configuring Nginx, HAProxy, Postfix, or OpenVPN, because the security requirements are the same.&lt;/p&gt;

&lt;p&gt;TLS 1.2 minimum, TLS 1.3 alongside it, everywhere. No exceptions for "legacy compatibility." There is no legacy client that justifies enabling a protocol deprecated three years ago.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consistency across your stack
&lt;/h2&gt;

&lt;p&gt;A typical production environment has TLS configured in a lot of places: web server at the edge, database, cache, message broker, mail server. Each configured at different times, by different people, from different sources. The result is usually a patchwork: Nginx hardened because someone ran an SSL Labs scan, PostgreSQL running with defaults, Redis with a cipher string from five years ago.&lt;/p&gt;

&lt;p&gt;GoodTLS covers all of them from the same standard. If you use it as the reference for your stack, you get consistent security posture across every application, not because the syntax is the same, but because the underlying decisions are.&lt;/p&gt;

&lt;h2&gt;
  
  
  The attacks it prevents
&lt;/h2&gt;

&lt;p&gt;These aren't hypothetical. The recommendations map directly to real vulnerabilities.&lt;/p&gt;

&lt;p&gt;Excluding CBC ciphers eliminates the Lucky13 attack surface. Excluding 3DES closes Sweet32. Requiring ECDHE key exchange prevents ROBOT. Disabling TLS 1.0 and 1.1 rules out BEAST and POODLE. Excluding EXPORT ciphers removes FREAK and LOGJAM. Each item in the cipher list has a reason; each exclusion has a CVE behind it.&lt;/p&gt;

&lt;p&gt;The goal is a configuration where the answer to "are you vulnerable to X" is no by construction, because the class of cipher or protocol that enables X isn't present.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who it's for
&lt;/h2&gt;

&lt;p&gt;Developers who need to configure TLS for an application they don't work with every day. Security engineers auditing configurations against a known baseline. Organizations writing internal TLS standards who want something they can link to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://goodtls.com" rel="noopener noreferrer"&gt;goodtls.com&lt;/a&gt; — free, no account required.&lt;/p&gt;

</description>
      <category>security</category>
      <category>tls</category>
      <category>ssl</category>
      <category>webdev</category>
    </item>
    <item>
      <title>DMARC Is Now a Proper Internet Standard: What Changed in RFC 9989/9990/9991</title>
      <dc:creator>Mike Pultz</dc:creator>
      <pubDate>Fri, 22 May 2026 21:46:45 +0000</pubDate>
      <link>https://dev.to/mikepultz/dmarc-is-now-a-proper-internet-standard-what-changed-in-rfc-998999909991-pj6</link>
      <guid>https://dev.to/mikepultz/dmarc-is-now-a-proper-internet-standard-what-changed-in-rfc-998999909991-pj6</guid>
      <description>&lt;p&gt;DMARC has been an Informational RFC since 2015. That changed this month.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9989.html" rel="noopener noreferrer"&gt;RFC 9989&lt;/a&gt; replaces RFC 7489 and elevates DMARC to &lt;strong&gt;Standards Track&lt;/strong&gt;, reflecting over a decade of deployment experience and near-universal adoption across the email ecosystem. The spec was also split into three separate documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9989.html" rel="noopener noreferrer"&gt;RFC 9989&lt;/a&gt;&lt;/strong&gt; — core DMARC protocol&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9990.html" rel="noopener noreferrer"&gt;RFC 9990&lt;/a&gt;&lt;/strong&gt; — aggregate reporting (RUA)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9991.html" rel="noopener noreferrer"&gt;RFC 9991&lt;/a&gt;&lt;/strong&gt; — failure reporting (RUF)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Splitting them allows each component to evolve independently. Here's what changed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The DNS Tree Walk Replaces the Public Suffix List
&lt;/h2&gt;

&lt;p&gt;This is the most significant architectural change.&lt;/p&gt;

&lt;p&gt;RFC 7489 used the &lt;a href="https://publicsuffix.org/" rel="noopener noreferrer"&gt;Public Suffix List&lt;/a&gt; to determine organizational domain boundaries. To find the org domain for &lt;code&gt;mail.example.co.uk&lt;/code&gt;, you'd look up &lt;code&gt;co.uk&lt;/code&gt; in the PSL, then take one label to the left. It works, but the problems are real: external dependency, manual maintenance, potential staleness, no support for non-obvious namespace boundaries.&lt;/p&gt;

&lt;p&gt;RFC 9989 introduces the &lt;strong&gt;DNS Tree Walk&lt;/strong&gt; as the alternative. The algorithm walks up the DNS tree looking for &lt;code&gt;_dmarc&lt;/code&gt; TXT records, starting from the sending domain and moving toward the root. It caps at eight queries to prevent DoS amplification.&lt;/p&gt;

&lt;p&gt;For most domains this changes nothing operationally. For complex organizations with deep subdomain hierarchies, or operators running public suffix domains that want their own DMARC policies, it's a meaningful improvement.&lt;/p&gt;

&lt;p&gt;The new &lt;code&gt;psd&lt;/code&gt; tag lets a domain declare itself as a Public Suffix Domain, enabling DMARC policy coverage at that level.&lt;/p&gt;




&lt;h2&gt;
  
  
  New Tags
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;t&lt;/code&gt; — Test Mode
&lt;/h3&gt;

&lt;p&gt;Replaces the behavioral role of &lt;code&gt;pct&lt;/code&gt;. Where &lt;code&gt;pct=0&lt;/code&gt; meant "apply policy to 0% of failing messages," &lt;code&gt;t=y&lt;/code&gt; explicitly signals that the policy is in test mode and shouldn't be enforced.&lt;/p&gt;

&lt;p&gt;The distinction between "policy exists but is being staged" and "policy is active against a subset of traffic" is now unambiguous.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v=DMARC1; p=quarantine; t=y; rua=mailto:dmarc@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;np&lt;/code&gt; — Non-Existent Subdomain Policy
&lt;/h3&gt;

&lt;p&gt;Allows separate policy handling for subdomains that have no DNS records at all. Previously, &lt;code&gt;sp&lt;/code&gt; applied to all subdomains. Now you can distinguish between legitimate configured subdomains (&lt;code&gt;sp&lt;/code&gt;) and mail claiming to originate from subdomains that don't exist in DNS (&lt;code&gt;np&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Non-existent subdomains sending mail are almost always spoofing, so &lt;code&gt;np=reject&lt;/code&gt; is often the right default.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;psd&lt;/code&gt; — Public Suffix Domain
&lt;/h3&gt;

&lt;p&gt;Marks the domain as a Public Suffix Domain for the DNS Tree Walk algorithm.&lt;/p&gt;




&lt;h2&gt;
  
  
  Removed Tags
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;pct&lt;/code&gt; — Gone
&lt;/h3&gt;

&lt;p&gt;Percentage-based partial enforcement has been removed. Use &lt;code&gt;t=y&lt;/code&gt; for test mode instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;rf&lt;/code&gt; — Report Format
&lt;/h3&gt;

&lt;p&gt;Removed. Only AFRF format was ever widely implemented. The tag was vestigial.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ri&lt;/code&gt; — Report Interval
&lt;/h3&gt;

&lt;p&gt;Removed. Aggregate reports are daily. The tag existed, receivers rarely honored it, and the spec now reflects reality.&lt;/p&gt;




&lt;h2&gt;
  
  
  Aggregate Reporting Changes (RFC 9990)
&lt;/h2&gt;

&lt;p&gt;The aggregate reporting spec was extracted from RFC 7489 into its own document. The format remains XML with several refinements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DKIM selector is now mandatory in reports.&lt;/strong&gt; Previously optional, it's required for understanding which DKIM key configuration is producing results — which matters for key rotation debugging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New discovery method field&lt;/strong&gt; indicates whether the receiver used &lt;code&gt;psl&lt;/code&gt; or &lt;code&gt;treewalk&lt;/code&gt; to find the organizational domain. Useful for diagnosing alignment issues during the transition period.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extension framework&lt;/strong&gt; allows future XML schema additions within defined namespaces without breaking existing parsers.&lt;/p&gt;

&lt;p&gt;The schema changes are backward-compatible. Well-formed RFC 7489 reports will still parse. If you're running your own DMARC report parser, the new fields are additive.&lt;/p&gt;




&lt;h2&gt;
  
  
  Failure Reporting Changes (RFC 9991)
&lt;/h2&gt;

&lt;p&gt;The changes here are more significant for implementors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Identity-Alignment&lt;/code&gt; is now a mandatory ARF header.&lt;/strong&gt; Failure reports must include this field listing which authentication mechanisms failed to produce an aligned pass, or &lt;code&gt;none&lt;/code&gt; if all passed. This was absent from the original spec, leading to inconsistent implementations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rate limiting is now required.&lt;/strong&gt; RFC 9991 explicitly mandates that report generators implement rate limits. This closes a known gap: failure reports can be triggered by an attacker sending large volumes of spoofed mail, making an unrated reporting system a DoS amplification vector.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;dmarc&lt;/code&gt; is now a formal ARF authentication failure type.&lt;/strong&gt; The &lt;code&gt;Authentication-Failure&lt;/code&gt; ARF header now includes &lt;code&gt;dmarc&lt;/code&gt; as a valid type value alongside the existing &lt;code&gt;dkim&lt;/code&gt; and &lt;code&gt;spf&lt;/code&gt; types.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Privacy guidance is significantly expanded.&lt;/strong&gt; The document adds detailed guidance on PII redaction, URI validation, and secure transport for failure reports. If you're forwarding RUF reports to a third-party reporting service, the privacy section of RFC 9991 is worth reading before you do.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You Actually Need to Do
&lt;/h2&gt;

&lt;p&gt;For most domain owners, nothing changes immediately. RFC 9989 is backward-compatible with RFC 7489 and your existing DMARC record continues to work.&lt;/p&gt;

&lt;p&gt;The practical checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Remove &lt;code&gt;pct&lt;/code&gt; tags&lt;/strong&gt; from your DMARC records. Receiver behavior around deprecated tags will vary as implementations update.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you're using &lt;code&gt;pct=0&lt;/code&gt; for staged rollouts&lt;/strong&gt;, move to &lt;code&gt;t=y&lt;/code&gt; to signal test mode explicitly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you're operating mail infrastructure&lt;/strong&gt; (MTAs, DMARC reporting tools, monitoring systems), the &lt;code&gt;Identity-Alignment&lt;/code&gt; requirement in RFC 9991 and the &lt;code&gt;psd&lt;/code&gt;/treewalk changes in RFC 9989 require implementation updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public Suffix Domain operators&lt;/strong&gt; should evaluate adding &lt;code&gt;psd=y&lt;/code&gt; once receiver support is established.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Standards Track designation matters for enterprise and government procurement environments where "Informational RFC" raised compliance questions. DMARC is now a proper internet standard, not a proposal.&lt;/p&gt;

</description>
      <category>email</category>
      <category>security</category>
      <category>dns</category>
      <category>webdev</category>
    </item>
    <item>
      <title>We've Normalized AI Outages, and That Should Bother You</title>
      <dc:creator>Mike Pultz</dc:creator>
      <pubDate>Sun, 17 May 2026 00:38:48 +0000</pubDate>
      <link>https://dev.to/mikepultz/weve-normalized-ai-outages-and-that-should-bother-you-4anm</link>
      <guid>https://dev.to/mikepultz/weve-normalized-ai-outages-and-that-should-bother-you-4anm</guid>
      <description>&lt;p&gt;I've been writing software and running production infrastructure for over 20 years. I've been on call at 3am, written post-mortems, and had the kind of conversations with clients about downtime that you don't forget. The baseline expectation, baked into every product I've ever built or supported, is that reliability is non-negotiable. You don't ship something people depend on and then shrug when it falls over.&lt;/p&gt;

&lt;p&gt;So it's strange to find myself in a world where the tools so many people depend on daily go down with a frequency that would have cost me client confidence ten years ago, and somehow nobody seems particularly bothered.&lt;/p&gt;

&lt;p&gt;The Claude status page is showing another incident as I write this. It's not unusual. Anthropic, OpenAI, GitHub Copilot, Gemini, these services have outages constantly. Not rarely. Not occasionally. Constantly. Multiple times a week, sometimes daily. The status pages are almost always showing something yellow or red.&lt;/p&gt;

&lt;p&gt;And the response from the industry, and from us as users, has been a collective shrug.&lt;/p&gt;

&lt;p&gt;I get why. The technology is genuinely impressive and the productivity gains are real. We've built workflows around these tools and quietly adjusted our expectations to match what vendors actually deliver rather than what we'd normally demand. We've done it so smoothly that most people haven't noticed the adjustment.&lt;/p&gt;

&lt;p&gt;But think about what we've actually accepted. If any other piece of critical infrastructure in your stack had this track record, you would have replaced it. Your database going down twice a week is not a beta-era quirk, it's a fire. Your payment processor "partially degraded" on a Monday afternoon is a crisis.&lt;/p&gt;

&lt;p&gt;AI services? That's just Tuesday.&lt;/p&gt;

&lt;p&gt;The thing that concerns me most is not the outages themselves. It's the normalization. When you stop expecting reliability from a system you depend on, you've made a quiet decision about your own standards. That decision has a way of spreading.&lt;/p&gt;

&lt;p&gt;Twenty years of engineering taught me that reliability is a culture, not a feature. Right now, as an industry, we're not building it in, we're hoping nobody notices.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
      <category>softwareengineering</category>
      <category>sre</category>
    </item>
    <item>
      <title>I Built a Free DNS &amp; Network Tools Site - Here's What I Learned</title>
      <dc:creator>Mike Pultz</dc:creator>
      <pubDate>Wed, 15 Apr 2026 16:31:30 +0000</pubDate>
      <link>https://dev.to/mikepultz/i-built-a-free-dns-network-tools-site-heres-what-i-learned-2ci5</link>
      <guid>https://dev.to/mikepultz/i-built-a-free-dns-network-tools-site-heres-what-i-learned-2ci5</guid>
      <description>&lt;p&gt;For years I kept reaching for the same handful of online tools when debugging email deliverability issues, checking DNS propagation, or diagnosing network problems. Most of the popular ones are fine but ad-heavy, paywalled above basic usage, or just showing their age. So I did what developers do: I built my own and put it at &lt;strong&gt;&lt;a href="https://mrdns.com?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=mrdns-launch" rel="noopener noreferrer"&gt;mrdns.com&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's free, no account required, and covers a fairly wide range of DNS, network, and email tools. Here's what's in it and some of the more interesting technical bits along the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's in the toolbox
&lt;/h2&gt;

&lt;h3&gt;
  
  
  DNS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;DNS Lookup&lt;/strong&gt; is the core feature - query any record type (A, AAAA, MX, TXT, NS, SOA, CNAME, PTR, CAA, SRV, TLSA, HTTPS, SVCB, MTA-STS, BIMI, and more) against authoritative resolvers. Results include country flag icons for A/AAAA records using GeoLite2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DNS Propagation Checker&lt;/strong&gt; queries 8 global resolvers simultaneously and shows whether your change has propagated. This one was fun to build - more on that below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DNSSEC Checker&lt;/strong&gt; walks the chain of trust: checks for DS records, validates DNSKEY digests, and verifies RRSIG records aren't expired.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Whois / RDAP&lt;/strong&gt; looks up domain and IP registration info. It tries RDAP first (the modern structured replacement for whois) using IANA bootstrap files to find the authoritative server, and falls back to the classic &lt;code&gt;whois&lt;/code&gt; binary if needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Network
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Ping and Traceroute&lt;/strong&gt; stream results in real-time using Server-Sent Events. Each traceroute hop gets city-level geolocation and is plotted on a Leaflet map.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is My IP&lt;/strong&gt; detects your IPv4 and IPv6 addresses separately - by pointing the browser at two different subdomains, one with only an A record and one with only an AAAA record. It's a clean technique that forces the browser down each protocol path without any JS tricks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blacklist / RBL Check&lt;/strong&gt; tests an IP or domain against 15+ DNSBL blocklists in parallel. IPv6 addresses get the nibble-reversed treatment before the query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSL Certificate Checker&lt;/strong&gt; connects directly with &lt;code&gt;stream_socket_client&lt;/code&gt;, captures the full chain, and reports expiry, SANs, key type/bits, and SHA-256 fingerprint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTP Headers&lt;/strong&gt; follows redirects manually (up to 5 hops), shows each hop's status and headers, and flags missing security headers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Port Checker&lt;/strong&gt; does a TCP connect with a short timeout and grabs the first 512 bytes of banner if the port is open. Useful for quickly checking whether a service is actually listening.&lt;/p&gt;

&lt;h3&gt;
  
  
  Email
&lt;/h3&gt;

&lt;p&gt;Email tooling is where the site arguably has the most depth:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Email Health Check&lt;/strong&gt; - runs SPF and DMARC together and gives a combined grade&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SPF Checker&lt;/strong&gt; - recursively expands includes and validates mechanisms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DMARC Checker&lt;/strong&gt; - parses and validates every tag&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DKIM Checker&lt;/strong&gt; - looks up the public key, checks key size (warns below 2048 bits, errors below 1024)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email Header Analyzer&lt;/strong&gt; - paste in raw headers and it reconstructs the relay chain with per-hop delays, authentication results (SPF/DKIM/DMARC), and spam scores&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MTA-STS Checker&lt;/strong&gt; - validates the DNS record, fetches and parses the policy file, and cross-checks against MX records&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BIMI Checker&lt;/strong&gt; - validates the DNS record, fetches the SVG logo for preview, and verifies the VMC certificate against known CAs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's also an SPF Generator and DMARC Generator for building records from scratch with a live preview.&lt;/p&gt;




&lt;h2&gt;
  
  
  A few interesting implementation details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  DNS Propagation: hybrid DoH + UDP
&lt;/h3&gt;

&lt;p&gt;Not all public resolvers support &lt;code&gt;application/dns-json&lt;/code&gt; (the DoH JSON API). Cloudflare, Google, Alibaba, NextDNS, and DNS.SB do. Quad9, OpenDNS, and AdGuard don't - at least not from my server. So the propagation checker runs the DoH resolvers in parallel via &lt;code&gt;curl_multi&lt;/code&gt;, then queries Quad9/OpenDNS/AdGuard over plain UDP with NetDNS2. You get 8 resolver results, most of them arriving together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-time ping and traceroute with SSE
&lt;/h3&gt;

&lt;p&gt;Both tools use Server-Sent Events - the server opens a process with &lt;code&gt;proc_open&lt;/code&gt;, reads output line by line, and streams &lt;code&gt;data: &amp;lt;json&amp;gt;\n\n&lt;/code&gt; to the browser. Nginx needs &lt;code&gt;X-Accel-Buffering: no&lt;/code&gt; to prevent it from buffering the response. The client uses &lt;code&gt;EventSource&lt;/code&gt; and appends rows to a table as they arrive. Traceroute geo-locates each hop and rebuilds the map polyline after each update.&lt;/p&gt;

&lt;h3&gt;
  
  
  IPv4/IPv6 dual-stack detection
&lt;/h3&gt;

&lt;p&gt;To separately detect both protocol addresses in the browser, I use two subdomains: &lt;code&gt;ipv4.mrdns.com&lt;/code&gt; has only an A record, and &lt;code&gt;ipv6.mrdns.com&lt;/code&gt; has only an AAAA record. The browser is forced onto the correct protocol purely by DNS - there's no JS protocol negotiation involved. Each returns a tiny JSON blob &lt;code&gt;{"ip":"&amp;lt;remote_addr&amp;gt;"}&lt;/code&gt; directly from nginx.&lt;/p&gt;

&lt;h3&gt;
  
  
  RDAP over legacy whois
&lt;/h3&gt;

&lt;p&gt;The Whois tool uses RDAP as the primary backend. It fetches the IANA bootstrap JSON files (which map IP CIDRs and TLDs to authoritative RDAP servers), caches them for 24 hours, and queries the right server directly. The parsed response gives you structured data - registrar, registrant, status, nameservers, dates - rather than a blob of text to regex apart. The raw whois fallback is there for the edge cases RDAP doesn't cover yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;p&gt;Nothing exotic - PHP 8.5, Bootstrap 5.3, vanilla JS with no framework. NetDNS2 for DNS resolution, GuzzleHTTP for outbound HTTP, and MaxMind GeoLite2 databases for geolocation. Deployed on nginx + PHP-FPM.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Everything is at &lt;strong&gt;&lt;a href="https://mrdns.com?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=mrdns-launch" rel="noopener noreferrer"&gt;mrdns.com&lt;/a&gt;&lt;/strong&gt; - free, no sign-up. If something's broken or you have a tool you'd like to see added, let me know in the comments.&lt;/p&gt;

</description>
      <category>networking</category>
      <category>showdev</category>
      <category>sideprojects</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
