domaindetails.com
Knowledge Base/Technical Guides/WHOIS vs RDAP: A Comprehensive Comparison (2025)
Technical Guides

WHOIS vs RDAP: A Comprehensive Comparison (2025)

Complete comparison of WHOIS and RDAP protocols. Learn the technical differences, migration timeline, implementation details, and why RDAP replaced WHOIS as the standard.

18 min
Published 2025-12-01
Updated 2025-12-01
By DomainDetails Team

Quick Answer

WHOIS and RDAP are protocols for accessing domain registration data, but they differ fundamentally in design and capabilities. WHOIS is the legacy protocol from 1982 using plain text over port 43 with inconsistent formatting. RDAP (Registration Data Access Protocol) is the modern replacement using RESTful HTTPS APIs with standardized JSON responses. As of January 28, 2025, RDAP became the official standard for gTLDs, offering superior security, internationalization, access control, and GDPR compliance. While WHOIS continues operating for backward compatibility, all new development and features now use RDAP exclusively.

Table of Contents

Introduction: Why This Comparison Matters

The transition from WHOIS to RDAP represents one of the most significant infrastructure changes in domain name system history. For over 40 years, WHOIS served as the primary method for accessing domain registration data. Now, RDAP has officially replaced it.

Who Needs to Understand This Transition?

Developers: If you're building applications that query domain data, you need to migrate from WHOIS to RDAP APIs to ensure reliability and compliance.

Domain Professionals: Registrars, registries, resellers, and domain investors must understand how RDAP changes data access, privacy controls, and operational workflows.

Security Teams: Cybersecurity professionals rely on registration data for threat intelligence, incident response, and abuse investigations. RDAP's enhanced features provide better tools for security operations.

Legal and Compliance: GDPR and other privacy regulations require sophisticated data access controls that only RDAP can properly implement.

What You'll Learn

This comprehensive comparison goes beyond surface-level differences. You'll understand:

  • Technical architecture: How each protocol works at the transport, data, and application layers
  • Practical implications: Real-world impact on your tools, workflows, and systems
  • Migration strategies: Concrete steps for transitioning from WHOIS to RDAP
  • Future-proofing: How to build systems that remain compliant and functional as the ecosystem evolves

Whether you're maintaining legacy WHOIS code, planning an RDAP implementation, or simply curious about why this change happened, this guide provides the depth and context you need.

Historical Context

The Birth of WHOIS (1982)

WHOIS emerged in 1982 when the internet consisted of a few hundred computers. Ken Harrenstien and Vic White at SRI International created WHOIS to maintain a directory of network users and resources.

Original Design Goals:

  • Simple directory service for network administrators
  • Quick lookups of contact information
  • Minimal protocol overhead for slow connections
  • Plain text for easy human readability

The Internet in 1982:

  • Approximately 500 hosts total
  • Single centralized directory (NIC.DDN.MIL)
  • Everyone knew everyone else
  • No commercial activity
  • No privacy concerns

WHOIS Evolution and Growing Pains

As the internet commercialized in the 1990s, WHOIS faced increasing challenges:

1993-1998: Commercialization Network Solutions (NSI) operated the .com, .net, and .org registries. WHOIS data was public and unregulated. Spammers harvested email addresses. No privacy protections existed.

1998-2008: Competition and Fragmentation ICANN introduced competition among registrars. Each registrar implemented WHOIS differently. No standard format emerged. Thick vs thin WHOIS models created confusion.

2008-2018: Privacy Regulations GDPR and other privacy laws conflicted with WHOIS's all-or-nothing model. Emergency redaction measures were inconsistent. Legal uncertainty paralyzed the industry.

2018-2025: The Breaking Point GDPR enforcement (May 2018) revealed WHOIS's fundamental limitations. Registrars redacted data differently. Access requests had no standardized process. The protocol couldn't distinguish between legitimate and illegitimate requesters.

The RDAP Initiative

Recognizing WHOIS's insurmountable problems, the IETF began developing RDAP in 2012.

Key Development Milestones:

Year Milestone
2012 IETF WEIRDS working group formed
2015 RFC 7480-7484 published (core RDAP specs)
2019 RFC 8521 published (registration data policy)
2020 ICANN begins piloting RDAP
2023 ICANN mandates RDAP implementation
2025 RDAP becomes official standard, WHOIS sunset

Design Philosophy Shift:

WHOIS was designed for a trusted, small network. RDAP was designed for an adversarial, global internet with privacy requirements and legal complexity.

Core Protocol Differences

Transport Layer

WHOIS:

Protocol: TCP Port 43
Encryption: None (plain text)
Connection: Short-lived, single query per connection
Query Format: Plain text string
Response Format: Free-form text
Connection Reuse: Not supported

RDAP:

Protocol: HTTPS (TCP Port 443)
Encryption: TLS 1.2+ required
Connection: HTTP/1.1 or HTTP/2 with keep-alive
Query Format: RESTful URL paths
Response Format: Structured JSON
Connection Reuse: Supported via HTTP keep-alive

Protocol Architecture

WHOIS Architecture:

┌─────────────┐          ┌─────────────┐
│   Client    │          │ WHOIS Server│
│             │  Port 43 │             │
│ TCP Connect ├─────────►│             │
│             │          │             │
│ Send Query  ├─────────►│ Process     │
│             │          │             │
│ Receive Text◄──────────┤ Send Text   │
│             │          │             │
│ Close Conn  │          │ Close Conn  │
└─────────────┘          └─────────────┘

RDAP Architecture:

┌──────────────┐         ┌──────────────┐
│   Client     │         │ RDAP Server  │
│              │  HTTPS  │              │
│ GET Request  ├────────►│ Authenticate │
│              │         │              │
│              │         │ Process      │
│              │         │              │
│              │         │ Apply ACL    │
│              │         │              │
│ JSON Response◄─────────┤ Send JSON    │
│              │         │              │
│ Keep-Alive   │◄───────►│ Persistent   │
└──────────────┘         └──────────────┘

Query Syntax

WHOIS Query Examples:

# Simple domain query
whois example.com

# IP address query
whois 8.8.8.8

# Server-specific query
whois -h whois.verisign-grs.com example.com

# Recursive query disabled
whois +norecurse example.com

Challenges:

  • No standard query syntax
  • Each server interprets queries differently
  • No way to specify desired fields
  • No structured search capabilities
  • Query options vary by server

RDAP Query Examples:

# Domain query
https://rdap.verisign.com/com/v1/domain/example.com

# IP address query
https://rdap.arin.net/registry/ip/8.8.8.8

# Nameserver query
https://rdap.verisign.com/com/v1/nameserver/ns1.example.com

# ASN query
https://rdap.arin.net/registry/autnum/64496

# Entity query
https://rdap.verisign.com/com/v1/entity/123456

Advantages:

  • Standard RESTful URL structure
  • Predictable query format across servers
  • Support for searches (where implemented)
  • Clear resource type identification
  • URL parameters for additional options

Service Discovery

WHOIS Service Discovery:

No standard mechanism exists. You must:

  1. Know which WHOIS server handles the TLD
  2. Manually configure the correct server
  3. Handle referrals when thin WHOIS requires querying both registry and registrar
  4. Maintain updated server lists manually

Example Complexity:

Query: example.com

Step 1: Query whois.verisign-grs.com (registry)
Result: "Registrar WHOIS Server: whois.registrar.com"

Step 2: Query whois.registrar.com (registrar)
Result: Full registration data

Two separate connections to different servers required.

RDAP Service Discovery:

IANA maintains the RDAP Bootstrap Service Registry:

  • Bootstrap File: https://data.iana.org/rdap/dns.json
  • Auto-discovery: Clients automatically find correct server
  • Single query: No referrals needed
  • Centralized updates: IANA maintains mappings

Bootstrap File Structure:

{
  "version": "1.0",
  "publication": "2025-01-28T10:00:00Z",
  "services": [
    [
      ["com", "net"],
      ["https://rdap.verisign.com/com/v1/", "https://rdap.verisign.com/net/v1/"]
    ],
    [
      ["org"],
      ["https://rdap.publicinterestregistry.org/rdap/"]
    ]
  ]
}

Universal Resolver:

RDAP.org provides automatic server routing:

curl https://rdap.org/domain/example.com
# Automatically routes to correct RDAP server

Data Format Comparison

WHOIS Data Format

WHOIS responses are unstructured plain text with no standard format.

Example 1: Verisign Format (.com)

Domain Name: EXAMPLE.COM
Registry Domain ID: 2336799_DOMAIN_COM-VRSN
Registrar WHOIS Server: whois.registrar.com
Registrar URL: http://www.registrar.com
Updated Date: 2024-08-14T07:01:44Z
Creation Date: 1995-08-14T04:00:00Z
Registry Expiry Date: 2025-08-13T04:00:00Z
Registrar: Example Registrar, Inc.
Registrar IANA ID: 376
Registrar Abuse Contact Email: [email protected]
Registrar Abuse Contact Phone: +1.1234567890
Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited
Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
Name Server: A.IANA-SERVERS.NET
Name Server: B.IANA-SERVERS.NET
DNSSEC: unsigned

Example 2: Different Registrar Format

domain:       example.org
status:       active
registrant:   REDACTED FOR PRIVACY
admin-c:      REDACTED FOR PRIVACY
tech-c:       REDACTED FOR PRIVACY
billing-c:    REDACTED FOR PRIVACY
nserver:      ns1.example.org
nserver:      ns2.example.org
created:      1997-08-14
modified:     2024-08-14
expires:      2025-08-14

Parsing Challenges:

// Pseudo-code showing WHOIS parsing complexity
function parseWHOIS(text) {
  const result = {};

  // Different registries use different field names
  const domainPatterns = [
    /Domain Name:\s*(.+)/i,
    /domain:\s*(.+)/i,
    /Domain:\s*(.+)/i,
    /\[Domain Name\]\s*(.+)/i
  ];

  // Different date formats
  const datePatterns = [
    /Creation Date:\s*(.+)/i,
    /created:\s*(.+)/i,
    /Created on:\s*(.+)/i,
    /\[Created on\]\s*(.+)/i
  ];

  // Must handle all variations
  // Still fragile when format changes
}

RDAP Data Format

RDAP responses use standardized JSON defined in RFC 7483.

Standard RDAP Response:

{
  "objectClassName": "domain",
  "handle": "2336799_DOMAIN_COM-VRSN",
  "ldhName": "example.com",
  "status": [
    "client delete prohibited",
    "client transfer prohibited"
  ],
  "events": [
    {
      "eventAction": "registration",
      "eventDate": "1995-08-14T04:00:00Z"
    },
    {
      "eventAction": "expiration",
      "eventDate": "2025-08-13T04:00:00Z"
    },
    {
      "eventAction": "last changed",
      "eventDate": "2024-08-14T07:01:44Z"
    },
    {
      "eventAction": "last update of RDAP database",
      "eventDate": "2025-12-01T12:30:00Z"
    }
  ],
  "nameservers": [
    {
      "objectClassName": "nameserver",
      "ldhName": "a.iana-servers.net"
    },
    {
      "objectClassName": "nameserver",
      "ldhName": "b.iana-servers.net"
    }
  ],
  "secureDNS": {
    "delegationSigned": false
  },
  "entities": [
    {
      "objectClassName": "entity",
      "handle": "376",
      "roles": ["registrar"],
      "publicIds": [
        {
          "type": "IANA Registrar ID",
          "identifier": "376"
        }
      ],
      "vcardArray": [
        "vcard",
        [
          ["version", {}, "text", "4.0"],
          ["fn", {}, "text", "Example Registrar, Inc."],
          ["email", {}, "text", "[email protected]"],
          ["tel", {}, "uri", "tel:+1.1234567890"]
        ]
      ],
      "entities": [
        {
          "objectClassName": "entity",
          "roles": ["abuse"],
          "vcardArray": [
            "vcard",
            [
              ["version", {}, "text", "4.0"],
              ["fn", {}, "text", "Abuse Department"],
              ["email", {}, "text", "[email protected]"]
            ]
          ]
        }
      ]
    }
  ],
  "notices": [
    {
      "title": "Terms of Use",
      "description": [
        "Service subject to Terms of Use."
      ],
      "links": [
        {
          "href": "https://www.verisign.com/domain-names/registration-data-access-protocol/terms-service/index.xhtml",
          "type": "text/html"
        }
      ]
    }
  ],
  "rdapConformance": [
    "rdap_level_0",
    "icann_rdap_response_profile_0",
    "icann_rdap_technical_implementation_guide_0"
  ]
}

Parsing Simplicity:

// RDAP parsing is straightforward
async function parseRDAP(domain) {
  const response = await fetch(
    `https://rdap.verisign.com/com/v1/domain/${domain}`
  );
  const data = await response.json();

  return {
    domain: data.ldhName,
    status: data.status,
    registered: data.events.find(e => e.eventAction === 'registration')?.eventDate,
    expires: data.events.find(e => e.eventAction === 'expiration')?.eventDate,
    updated: data.events.find(e => e.eventAction === 'last changed')?.eventDate,
    nameservers: data.nameservers?.map(ns => ns.ldhName) || [],
    registrar: data.entities?.find(e => e.roles?.includes('registrar'))
      ?.vcardArray?.[1]?.find(v => v[0] === 'fn')?.[3]
  };
}

Field Mapping

Data Element WHOIS Field (varies) RDAP Location
Domain Name "Domain Name:", "domain:", "Domain:" ldhName
Creation Date "Creation Date:", "created:", "Created on:" events[eventAction=registration].eventDate
Expiry Date "Registry Expiry Date:", "expires:", "Expiration Date:" events[eventAction=expiration].eventDate
Status Codes "Domain Status:", "status:" status[]
Nameservers "Name Server:", "nserver:" nameservers[].ldhName
Registrar "Registrar:", "registrar:" entities[roles=registrar].vcardArray

Security Architecture

WHOIS Security Limitations

1. No Encryption

All data transmitted in plain text over port 43:

┌────────┐                  ┌──────────┐
│ Client │ ──────Port 43───►│  Server  │
│        │   Plain Text      │          │
│        │◄──────────────────│          │
└────────┘  example.com...   └──────────┘
         ANYONE can intercept

Vulnerabilities:

  • Man-in-the-middle attacks trivial
  • Query patterns expose research interests
  • Response spoofing possible
  • No integrity verification
  • Credentials (if used) transmitted in clear

2. No Authentication

WHOIS has no mechanism to verify:

  • Who is making the request
  • Whether the requestor has legitimate interest
  • Whether responses come from authorized servers

3. No Authorization

Cannot implement access controls:

  • Everyone sees the same data (or nothing if privacy-protected)
  • No differentiated access levels
  • Cannot provide more data to law enforcement
  • Cannot enforce acceptable use policies programmatically

4. DDoS Vulnerability

Simple protocol makes abuse easy:

  • No rate limiting standards
  • Difficult to distinguish legitimate from malicious queries
  • Amplification attacks possible
  • Resource exhaustion attacks simple

RDAP Security Features

1. Mandatory Encryption

All RDAP communication uses HTTPS with TLS 1.2+:

┌────────┐                    ┌──────────┐
│ Client │ ─────HTTPS/TLS────►│  Server  │
│        │   Encrypted JSON    │          │
│        │◄───────────────────│          │
└────────┘    [encrypted]     └──────────┘
         Confidentiality + Integrity

Benefits:

  • Query and response confidentiality
  • Server authentication via TLS certificates
  • Data integrity verification
  • Protection against interception and tampering

2. Authentication Support

RDAP supports multiple authentication methods:

HTTP Basic Authentication:

GET /domain/example.com HTTP/1.1
Host: rdap.example.com
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

OAuth 2.0:

GET /domain/example.com HTTP/1.1
Host: rdap.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

API Keys:

GET /domain/example.com HTTP/1.1
Host: rdap.example.com
X-API-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890

Client Certificates (mTLS):

Client presents certificate during TLS handshake
Server validates certificate against trusted CA
Access granted based on certificate attributes

3. Differentiated Access Control

RDAP can provide different data based on authentication:

// Anonymous query
{
  "ldhName": "example.com",
  "status": ["active"],
  "events": [...],
  "nameservers": [...],
  "entities": [
    {
      "roles": ["registrar"],
      "vcardArray": [...] // Registrar info only
    }
  ]
}

// Authenticated law enforcement query
{
  "ldhName": "example.com",
  "status": ["active"],
  "events": [...],
  "nameservers": [...],
  "entities": [
    {
      "roles": ["registrant"],
      "vcardArray": [...] // Full registrant details
    },
    {
      "roles": ["technical"],
      "vcardArray": [...] // Technical contact
    },
    {
      "roles": ["registrar"],
      "vcardArray": [...]
    }
  ]
}

4. Rate Limiting and Abuse Prevention

RDAP uses standard HTTP mechanisms:

HTTP/1.1 429 Too Many Requests
Retry-After: 3600
X-Rate-Limit-Limit: 100
X-Rate-Limit-Remaining: 0
X-Rate-Limit-Reset: 1733068800

{
  "errorCode": 429,
  "title": "Too Many Requests",
  "description": ["Rate limit exceeded. Try again in 1 hour."]
}

5. Security Headers

RDAP responses include modern security headers:

HTTP/2 200 OK
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'none'
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: no-referrer

Access Control and Privacy

WHOIS Privacy Challenges

The All-or-Nothing Problem:

WHOIS was designed when privacy wasn't a concern. The protocol has only two modes:

  1. Show everything: Expose all registrant contact information publicly
  2. Show nothing: Use privacy service to hide all details

No middle ground exists for:

  • Legitimate researchers needing partial access
  • Law enforcement requiring full data
  • Trademark holders investigating infringement
  • Security researchers tracking abuse

Pre-GDPR WHOIS:

Domain Name: example.com
Registrant Name: John Smith
Registrant Organization: Smith Consulting LLC
Registrant Street: 123 Main Street
Registrant City: Anytown
Registrant State/Province: CA
Registrant Postal Code: 90210
Registrant Country: US
Registrant Phone: +1.3105551234
Registrant Email: [email protected]
Admin Name: John Smith
Admin Email: [email protected]
Tech Name: John Smith
Tech Email: [email protected]

Problems:

  • Personal information publicly available
  • Email addresses harvested for spam
  • Physical addresses exposed
  • Phone numbers collected
  • Identity theft risk

Post-GDPR WHOIS:

Domain Name: example.com
Registry Domain ID: 123456_DOMAIN_COM-VRSN
Registrar WHOIS Server: whois.registrar.com
Registrar URL: http://www.registrar.com
Updated Date: 2024-12-01T00:00:00Z
Creation Date: 2020-01-01T00:00:00Z
Registry Expiry Date: 2025-01-01T00:00:00Z
Registrar: Example Registrar Inc.
Registrar IANA ID: 1234
Registrar Abuse Contact Email: [email protected]
Registrar Abuse Contact Phone: +1.2025551234
Domain Status: ok https://icann.org/epp#ok
Name Server: NS1.EXAMPLE.COM
Name Server: NS2.EXAMPLE.COM
DNSSEC: unsigned
Registrant Name: REDACTED FOR PRIVACY
Registrant Organization: REDACTED FOR PRIVACY
Registrant Street: REDACTED FOR PRIVACY
Registrant City: REDACTED FOR PRIVACY
Registrant State/Province: REDACTED FOR PRIVACY
Registrant Postal Code: REDACTED FOR PRIVACY
Registrant Country: REDACTED FOR PRIVACY
Registrant Phone: REDACTED FOR PRIVACY
Registrant Email: Please query the RDDS service of the Registrar
Admin Name: REDACTED FOR PRIVACY
Admin Email: Please query the RDDS service of the Registrar
Tech Name: REDACTED FOR PRIVACY
Tech Email: Please query the RDDS service of the Registrar

New Problems:

  • Useful data completely hidden
  • Security research hindered
  • Abuse investigation complicated
  • No standardized access request process
  • Registrar-specific policies create inconsistency

RDAP Privacy Solutions

Differentiated Access Levels:

RDAP implements a tiered access model:

Level 1: Anonymous/Public Access

{
  "ldhName": "example.com",
  "status": ["active"],
  "events": [
    {"eventAction": "registration", "eventDate": "2020-01-01T00:00:00Z"},
    {"eventAction": "expiration", "eventDate": "2025-01-01T00:00:00Z"}
  ],
  "nameservers": [
    {"ldhName": "ns1.example.com"},
    {"ldhName": "ns2.example.com"}
  ],
  "entities": [
    {
      "roles": ["registrar"],
      "vcardArray": [
        "vcard",
        [
          ["fn", {}, "text", "Example Registrar Inc."],
          ["email", {}, "text", "[email protected]"]
        ]
      ]
    }
  ],
  "remarks": [
    {
      "title": "REDACTED FOR PRIVACY",
      "description": [
        "Registrant contact information redacted per GDPR.",
        "To request access, visit: https://registrar.com/data-access"
      ]
    }
  ]
}

Level 2: Authenticated User Access

{
  "ldhName": "example.com",
  // ... public fields ...
  "entities": [
    {
      "roles": ["registrant"],
      "vcardArray": [
        "vcard",
        [
          ["fn", {}, "text", "Smith Consulting LLC"], // Organization shown
          ["email", {}, "text", "[email protected]"] // Privacy service email
        ]
      ]
    }
  ],
  "remarks": [
    {
      "title": "PARTIAL ACCESS",
      "description": [
        "Additional contact information available to verified requesters.",
        "Submit access request with justification."
      ]
    }
  ]
}

Level 3: Authorized Access (Law Enforcement)

{
  "ldhName": "example.com",
  // ... all public fields ...
  "entities": [
    {
      "roles": ["registrant"],
      "vcardArray": [
        "vcard",
        [
          ["version", {}, "text", "4.0"],
          ["fn", {}, "text", "John Smith"],
          ["org", {}, "text", "Smith Consulting LLC"],
          ["adr", {}, "text", [
            "", "", "123 Main Street", "Anytown", "CA", "90210", "US"
          ]],
          ["tel", {}, "uri", "tel:+1.3105551234"],
          ["email", {}, "text", "[email protected]"]
        ]
      ]
    },
    {
      "roles": ["administrative"],
      // ... full admin contact ...
    },
    {
      "roles": ["technical"],
      // ... full tech contact ...
    }
  ],
  "notices": [
    {
      "title": "LAW ENFORCEMENT ACCESS",
      "description": [
        "Full registration data provided pursuant to official request.",
        "Disclosure authorized under applicable law.",
        "Case reference: LE-2025-12345"
      ]
    }
  ]
}

Standardized Access Request Process:

RDAP includes structured information about requesting access:

{
  "notices": [
    {
      "title": "Data Access Policy",
      "description": [
        "Redacted data may be available to authorized parties."
      ],
      "links": [
        {
          "rel": "alternate",
          "href": "https://registrar.com/data-access-policy",
          "type": "text/html",
          "title": "Data Access Request Form"
        }
      ]
    }
  ]
}

Redaction Transparency:

RDAP responses clearly indicate what has been redacted and why:

{
  "entities": [
    {
      "roles": ["registrant"],
      "vcardArray": [
        "vcard",
        [
          ["fn", {}, "text", "REDACTED"],
          ["email", {}, "text", "REDACTED"]
        ]
      ],
      "remarks": [
        {
          "title": "PRIVACY NOTICE",
          "description": [
            "Personal data redacted pursuant to GDPR Article 6(1)(f).",
            "Registrar: Example Registrar Inc.",
            "Privacy Policy: https://registrar.com/privacy",
            "Data Access Requests: [email protected]"
          ]
        }
      ]
    }
  ]
}

Internationalization Support

WHOIS ASCII Limitation

WHOIS was designed for ASCII text only (7-bit encoding).

Problems with Non-ASCII Domains:

# Chinese domain
Domain Name: 例え.com

WHOIS query result:
Domain Name: xn--r8jz45g.com  # Punycode only
Registrant Name: ??? (garbled or absent)

# Arabic domain
Domain Name: مثال.com

WHOIS query result:
Domain Name: xn--mgbh0fb.com  # Punycode only
Registrant Organization: ????? (cannot display)

Contact Information Issues:

# Japanese registrant
Registrant Name: ??? (cannot display)
Registrant Street: ?????? (cannot display)
Registrant City: ??? (cannot display)

# Russian technical contact
Tech Name: ??? (mojibake)
Tech Organization: ??? (lost in translation)

Encoding Inconsistencies:

Different servers attempted different solutions:

  • Some used Latin transliteration (lossy)
  • Others used UTF-8 (broke WHOIS parsers)
  • Many simply omitted non-ASCII text
  • No standard approach existed

RDAP Unicode Support

RDAP fully supports Unicode (UTF-8 encoding).

Internationalized Domain Names (IDNs):

{
  "objectClassName": "domain",
  "ldhName": "xn--r8jz45g.com",  // ASCII-compatible form
  "unicodeName": "例え.com",        // Unicode form
  "status": ["active"],
  "events": [...],
  "lang": "ja"  // Language tag
}

Multilingual Contact Information:

{
  "entities": [
    {
      "roles": ["registrant"],
      "vcardArray": [
        "vcard",
        [
          ["version", {}, "text", "4.0"],
          ["fn", {}, "text", "田中太郎"],  // Japanese name in original script
          ["fn", {"language": "en"}, "text", "Taro Tanaka"],  // English version
          ["org", {}, "text", "株式会社サンプル"],  // Japanese company name
          ["org", {"language": "en"}, "text", "Sample Corporation"],  // English
          ["adr", {}, "text", [
            "",
            "",
            "千代田区霞が関1-1-1",  // Japanese address
            "東京都",
            "",
            "100-0013",
            "JP"
          ]],
          ["adr", {"language": "en"}, "text", [
            "",
            "",
            "1-1-1 Kasumigaseki, Chiyoda-ku",  // Romanized address
            "Tokyo",
            "",
            "100-0013",
            "JP"
          ]]
        ]
      ]
    }
  ]
}

Language Tags:

RDAP uses standard IETF language tags (RFC 5646):

{
  "lang": "ja",  // Primary language: Japanese
  "entities": [
    {
      "vcardArray": [
        "vcard",
        [
          ["fn", {"language": "ja"}, "text", "山田花子"],
          ["fn", {"language": "en"}, "text", "Hanako Yamada"],
          ["fn", {"language": "ja-Kana"}, "text", "ヤマダハナコ"],  // Katakana
          ["fn", {"language": "ja-Hira"}, "text", "やまだはなこ"]   // Hiragana
        ]
      ]
    }
  ]
}

Script and Direction Support:

{
  "entities": [
    {
      "roles": ["registrant"],
      "vcardArray": [
        "vcard",
        [
          ["fn", {"language": "ar"}, "text", "محمد أحمد"],  // Arabic (RTL)
          ["fn", {"language": "ar-Latn"}, "text", "Muhammad Ahmad"],  // Romanized
          ["fn", {"language": "he"}, "text", "דוד כהן"],  // Hebrew (RTL)
          ["fn", {"language": "he-Latn"}, "text", "David Cohen"]  // Romanized
        ]
      ]
    }
  ]
}

Benefits:

  • Original scripts preserved
  • Multiple language representations supported
  • Client can choose appropriate version
  • No information loss
  • Proper cultural representation

Error Handling and Standards

WHOIS Error Handling

WHOIS has no standardized error responses.

Different servers, different errors:

Server 1:

% No match for "NONEXISTENT.COM".

Server 2:

No Data Found

Server 3:

NOT FOUND

Server 4:

This domain is not registered.

Server 5:

ERROR: Domain not found in registry

Parsing Nightmare:

function checkDomainAvailable(whoisText) {
  // Must check dozens of possible error messages
  const notFoundPatterns = [
    /No match/i,
    /NOT FOUND/i,
    /No Data Found/i,
    /not registered/i,
    /No entries found/i,
    /Status: free/i,
    /Domain not found/i,
    // ... 50+ more patterns ...
  ];

  // Still fragile - new servers break code
  return notFoundPatterns.some(pattern => pattern.test(whoisText));
}

Rate Limiting:

No standard. Some close connection, others return:

% Your query limit exceeded

or

ERROR: Rate limit

or disconnect without message.

RDAP Error Handling

RDAP uses standard HTTP status codes and structured JSON errors.

Standard HTTP Status Codes:

Code Meaning RDAP Usage
200 OK Successful query, data returned
400 Bad Request Malformed query syntax
401 Unauthorized Authentication required
403 Forbidden Authenticated but not authorized
404 Not Found Domain/object does not exist
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Server-side problem
501 Not Implemented Query type not supported
503 Service Unavailable Temporary server unavailability

Structured Error Responses:

// 404 Not Found - Domain Available
{
  "errorCode": 404,
  "title": "Not Found",
  "description": [
    "The requested domain does not exist in the registry."
  ]
}
// 400 Bad Request - Invalid Query
{
  "errorCode": 400,
  "title": "Bad Request",
  "description": [
    "Invalid domain name format.",
    "Domain names must contain only alphanumeric characters and hyphens."
  ]
}
// 429 Too Many Requests - Rate Limited
{
  "errorCode": 429,
  "title": "Too Many Requests",
  "description": [
    "Rate limit of 10 requests per minute exceeded.",
    "Retry after 60 seconds."
  ],
  "notices": [
    {
      "title": "Rate Limit Policy",
      "description": [
        "Anonymous users: 10 queries/minute",
        "Authenticated users: 100 queries/minute",
        "Sign up for API access: https://rdap.example.com/signup"
      ]
    }
  ]
}
// 503 Service Unavailable - Maintenance
{
  "errorCode": 503,
  "title": "Service Temporarily Unavailable",
  "description": [
    "Scheduled maintenance in progress.",
    "Service expected to resume at 2025-12-01T14:00:00Z"
  ],
  "notices": [
    {
      "title": "Maintenance Window",
      "description": [
        "Maintenance schedule: https://status.rdap.example.com"
      ]
    }
  ]
}

Client-Friendly Error Handling:

async function queryRDAP(domain) {
  try {
    const response = await fetch(
      `https://rdap.verisign.com/com/v1/domain/${domain}`
    );

    if (response.ok) {
      return await response.json();
    }

    // Structured error handling
    switch (response.status) {
      case 404:
        return { available: true };

      case 429:
        const retryAfter = response.headers.get('Retry-After');
        throw new Error(`Rate limited. Retry after ${retryAfter} seconds`);

      case 400:
        const error = await response.json();
        throw new Error(`Invalid query: ${error.description.join(' ')}`);

      case 500:
      case 503:
        throw new Error('Server temporarily unavailable');

      default:
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
  } catch (error) {
    console.error('RDAP query failed:', error);
    throw error;
  }
}

Performance and Scalability

WHOIS Performance Characteristics

Connection Overhead:

Each query requires:
1. TCP handshake (1 RTT)
2. Send query (1 RTT)
3. Receive response (1+ RTT)
4. Close connection
Total: ~3-4 RTTs per query

No Connection Reuse:

WHOIS typically uses one connection per query. For 100 queries:

  • 100 TCP connections
  • 300-400 round trips
  • Significant overhead

No Caching Mechanisms:

WHOIS protocol provides no cache control:

  • Clients don't know how long to cache
  • Servers can't indicate freshness
  • No conditional requests ("If-Modified-Since")
  • Forces full queries even for unchanged data

Text Parsing Overhead:

// Parsing unstructured text is CPU-intensive
function parseWHOIS(text) {
  // Regex parsing thousands of lines
  // String matching and extraction
  // Date parsing with format detection
  // Encoding detection and conversion
  // Takes 10-50ms per response
}

Benchmark - WHOIS:

Sequential 100 domain queries:
- Connection time: 5-10 seconds
- Query processing: 2-5 seconds
- Response parsing: 1-2 seconds
Total: 8-17 seconds (5-12 queries/second)

RDAP Performance Advantages

HTTP/2 and Connection Reuse:

Single connection for multiple queries:
1. Initial TLS handshake (2 RTTs)
2. Query 1 (1 RTT)
3. Query 2 (1 RTT) - reuses connection
4. Query 3 (1 RTT) - reuses connection
...
100 queries: ~102 RTTs vs 300-400 for WHOIS

HTTP/2 Multiplexing:

Multiple concurrent requests over one connection:
- No head-of-line blocking
- Request pipelining
- Stream prioritization
Enables parallel queries without connection overhead

Cache Control:

HTTP/2 200 OK
Cache-Control: public, max-age=3600
ETag: "a1b2c3d4e5f6"
Last-Modified: Wed, 01 Dec 2025 12:00:00 GMT

{
  "ldhName": "example.com",
  // ... response data ...
}

Conditional Requests:

GET /domain/example.com HTTP/2
Host: rdap.verisign.com
If-None-Match: "a1b2c3d4e5f6"

HTTP/2 304 Not Modified
Cache-Control: public, max-age=3600

Saves bandwidth and processing when data hasn't changed.

JSON Parsing Performance:

// Native JSON parsing is very fast
const data = JSON.parse(response); // 1-2ms
// No regex needed
// No format detection
// Direct object access

Benchmark - RDAP:

Sequential 100 domain queries (HTTP/2):
- Initial connection: 0.2 seconds
- Query processing: 2-5 seconds
- JSON parsing: 0.1-0.2 seconds
Total: 2.3-5.4 seconds (18-43 queries/second)

3-5x faster than WHOIS

Concurrent Queries:

// RDAP enables efficient parallel queries
const domains = ['example1.com', 'example2.com', /* ... 100 domains */];

const results = await Promise.all(
  domains.map(domain => queryRDAP(domain))
);

// With HTTP/2 multiplexing over single connection
// 100 queries complete in ~3-5 seconds
// Effective rate: 20-33 queries/second

Scalability Considerations

WHOIS Scalability Limits:

  • Connection overhead limits query rate
  • Text parsing consumes significant CPU
  • No standard caching reduces efficiency
  • Difficult to implement CDN/proxy caching
  • Rate limiting forces slow query rates

RDAP Scalability Features:

  • HTTP/2 reduces connection overhead
  • JSON parsing is highly efficient
  • Standard caching enables CDN deployment
  • Structured data enables database optimization
  • Clear rate limit signals enable backoff strategies

Implementation Requirements

WHOIS Server Requirements

Minimal Specification:

- TCP socket on port 43
- Accept incoming connections
- Read query string (terminated by CRLF)
- Return plain text response
- Close connection

No Standards For:

  • Response format
  • Character encoding
  • Error messages
  • Rate limiting
  • Access control
  • Logging format
  • Security features

Example Minimal WHOIS Server (Python):

import socket

def whois_server():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('0.0.0.0', 43))
    server.listen(5)

    while True:
        client, addr = server.accept()
        query = client.recv(1024).decode().strip()

        # Process query (implementation-specific)
        response = process_query(query)

        # Send response as plain text
        client.send(response.encode())
        client.close()

def process_query(query):
    # No standard format - implement however you want
    return f"Domain: {query}\nStatus: active\n..."

Challenges:

  • No guidance on proper implementation
  • Each implementer invents their own format
  • Inconsistencies proliferate
  • Security often overlooked

RDAP Server Requirements

Comprehensive Specification:

Required RFCs:

  • RFC 7480: HTTP Usage in RDAP
  • RFC 7481: Security Services for RDAP
  • RFC 7482: Query Format
  • RFC 7483: JSON Responses
  • RFC 7484: Finding the Right RDAP Server

ICANN Additional Requirements:

  • ICANN RDAP Profile
  • ICANN RDAP Technical Implementation Guide
  • gTLD Registration Data Services

Server Implementation Checklist:

Transport:
  - HTTPS on port 443
  - TLS 1.2 or higher
  - Valid TLS certificate
  - HTTP/2 support recommended

Query Support:
  - Domain lookup: /domain/{domain}
  - Nameserver lookup: /nameserver/{nameserver}
  - Entity lookup: /entity/{handle}
  - IP lookup: /ip/{ip}
  - ASN lookup: /autnum/{asn}

Response Format:
  - JSON with proper content-type
  - UTF-8 encoding
  - Proper RDAP conformance tags
  - Standard object classes

Security:
  - Authentication support (optional but recommended)
  - Authorization framework
  - Rate limiting
  - Abuse prevention
  - Security headers

Privacy:
  - Differentiated access control
  - GDPR compliance
  - Redaction mechanisms
  - Access request process

Operational:
  - Service status page
  - Contact information
  - Terms of service
  - Privacy policy
  - Logging and monitoring

Example RDAP Server Structure (Node.js/Express):

const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');

const app = express();

// Security middleware
app.use(helmet());
app.use(express.json());

// Rate limiting
const limiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 10, // 10 requests per minute
  message: {
    errorCode: 429,
    title: "Too Many Requests",
    description: ["Rate limit exceeded. Try again later."]
  }
});

app.use('/rdap', limiter);

// Domain lookup endpoint
app.get('/rdap/domain/:domain', async (req, res) => {
  try {
    const { domain } = req.params;

    // Validate domain format
    if (!isValidDomain(domain)) {
      return res.status(400).json({
        errorCode: 400,
        title: "Bad Request",
        description: ["Invalid domain name format"]
      });
    }

    // Query database
    const data = await lookupDomain(domain);

    if (!data) {
      return res.status(404).json({
        errorCode: 404,
        title: "Not Found",
        description: ["Domain not found in registry"]
      });
    }

    // Apply access control
    const accessLevel = determineAccessLevel(req);
    const filteredData = applyPrivacyFilters(data, accessLevel);

    // Return standard RDAP response
    res.json(filteredData);

  } catch (error) {
    console.error('RDAP query error:', error);
    res.status(500).json({
      errorCode: 500,
      title: "Internal Server Error",
      description: ["An error occurred processing your request"]
    });
  }
});

// Standard error handler
app.use((err, req, res, next) => {
  res.status(err.status || 500).json({
    errorCode: err.status || 500,
    title: err.title || "Internal Server Error",
    description: [err.message]
  });
});

// Start server
app.listen(443, () => {
  console.log('RDAP server listening on port 443');
});

Testing and Validation:

ICANN provides:

  • RDAP validation tools
  • Test suite for compliance
  • Reference implementations
  • Interoperability testing

Migration Timeline and Strategy

Official Sunset Schedule

January 28, 2025: ICANN officially sunset WHOIS for gTLDs and mandated RDAP.

What This Means:

  • RDAP is now the required protocol for gTLD registration data access
  • All ICANN-accredited registries must provide RDAP services
  • WHOIS is no longer the official standard
  • New features and capabilities will be RDAP-only

What This Doesn't Mean:

  • WHOIS servers haven't been shut off
  • Many registries continue operating WHOIS for backward compatibility
  • No specific date announced for WHOIS decommissioning
  • Transition period continues while ecosystem adapts

Registry Implementation Status

Already Implemented (100%):

All major gTLD registries have RDAP:

  • Verisign (.com, .net, .name, .cc, .tv)
  • Public Interest Registry (.org)
  • Donuts (portfolio of new gTLDs)
  • Google Registry (.dev, .app, .page, etc.)
  • Identity Digital (Afilias)

ccTLD Implementation (Varies):

Country-code TLDs are not under ICANN mandate:

  • ✅ .uk - RDAP available
  • ✅ .de - RDAP available
  • ✅ .nl - RDAP available
  • ✅ .ca - RDAP available
  • ⚠️ .cn - Limited RDAP
  • ❌ Many smaller ccTLDs - WHOIS only

Client Migration Strategy

Phase 1: Add RDAP Support (Completed for Most)

// Support both protocols during transition
async function lookupDomain(domain) {
  try {
    // Try RDAP first
    return await queryRDAP(domain);
  } catch (error) {
    console.warn('RDAP failed, falling back to WHOIS:', error);
    // Fallback to WHOIS
    return await queryWHOIS(domain);
  }
}

Phase 2: Prefer RDAP, Keep WHOIS Fallback (Current)

// Most tools now here
async function lookupDomain(domain) {
  const tld = domain.split('.').pop();

  // Check if TLD supports RDAP
  if (await hasRDAPSupport(tld)) {
    try {
      return await queryRDAP(domain);
    } catch (error) {
      if (error.code === 'RDAP_UNAVAILABLE') {
        console.warn('RDAP temporarily unavailable, trying WHOIS');
        return await queryWHOIS(domain);
      }
      throw error;
    }
  }

  // Use WHOIS for TLDs without RDAP
  return await queryWHOIS(domain);
}

Phase 3: RDAP-Only (Future)

// Future state - RDAP only
async function lookupDomain(domain) {
  return await queryRDAP(domain);
  // No WHOIS fallback
}

Migration Checklist for Developers

Immediate Actions:

  • Audit codebase for WHOIS usage
  • Identify all domain lookup implementations
  • Test RDAP support for your TLDs
  • Update error handling for RDAP responses
  • Implement proper JSON parsing

Short-term (3-6 months):

  • Implement RDAP as primary lookup method
  • Keep WHOIS fallback for ccTLDs
  • Update documentation and API specs
  • Add support for authentication (if needed)
  • Implement rate limiting and retry logic

Medium-term (6-12 months):

  • Remove WHOIS code for gTLDs
  • Optimize RDAP query patterns
  • Implement response caching
  • Add monitoring and alerting
  • Update UI to handle RDAP-specific features

Long-term (12+ months):

  • Remove all WHOIS code
  • Full RDAP feature utilization
  • Implement differentiated access (if applicable)
  • Leverage RDAP-only capabilities
  • Archive WHOIS parsing libraries

Developer Perspective

Migration Code Examples

Before: WHOIS Implementation

// Legacy WHOIS client (Node.js)
const net = require('net');

function queryWHOIS(domain) {
  return new Promise((resolve, reject) => {
    const client = new net.Socket();
    let data = '';

    // Connect to WHOIS server on port 43
    client.connect(43, 'whois.verisign-grs.com', () => {
      client.write(domain + '\r\n');
    });

    // Accumulate response
    client.on('data', (chunk) => {
      data += chunk.toString();
    });

    // Connection closed, parse response
    client.on('close', () => {
      try {
        const parsed = parseWHOIS(data);
        resolve(parsed);
      } catch (error) {
        reject(error);
      }
    });

    client.on('error', reject);

    // Timeout after 10 seconds
    client.setTimeout(10000);
    client.on('timeout', () => {
      client.destroy();
      reject(new Error('WHOIS query timeout'));
    });
  });
}

// Fragile text parsing
function parseWHOIS(text) {
  const result = {};

  // Try multiple patterns for each field
  const domainMatch = text.match(/Domain Name:\s*(.+)/i);
  result.domain = domainMatch ? domainMatch[1].trim() : null;

  const createdMatch = text.match(/Creation Date:\s*(.+)/i) ||
                       text.match(/created:\s*(.+)/i);
  result.created = createdMatch ? new Date(createdMatch[1].trim()) : null;

  const expiresMatch = text.match(/Registry Expiry Date:\s*(.+)/i) ||
                       text.match(/expires:\s*(.+)/i);
  result.expires = expiresMatch ? new Date(expiresMatch[1].trim()) : null;

  // Extract nameservers
  const nsMatches = text.match(/Name Server:\s*(.+)/gi);
  result.nameservers = nsMatches
    ? nsMatches.map(ns => ns.replace(/Name Server:\s*/i, '').trim())
    : [];

  return result;
}

// Usage
queryWHOIS('example.com')
  .then(data => console.log(data))
  .catch(error => console.error(error));

After: RDAP Implementation

// Modern RDAP client (Node.js)
const fetch = require('node-fetch');

// Bootstrap data could be cached
const BOOTSTRAP_URL = 'https://data.iana.org/rdap/dns.json';

async function queryRDAP(domain) {
  // Find correct RDAP server for domain
  const rdapUrl = await getRDAPServer(domain);

  const response = await fetch(`${rdapUrl}/domain/${domain}`, {
    headers: {
      'Accept': 'application/rdap+json'
    }
  });

  if (!response.ok) {
    if (response.status === 404) {
      return { available: true };
    }
    throw new Error(`RDAP query failed: ${response.status}`);
  }

  const data = await response.json();
  return parseRDAP(data);
}

// Cache bootstrap data
let bootstrapCache = null;

async function getRDAPServer(domain) {
  const tld = domain.split('.').pop().toLowerCase();

  if (!bootstrapCache) {
    const response = await fetch(BOOTSTRAP_URL);
    bootstrapCache = await response.json();
  }

  // Find service for this TLD
  for (const service of bootstrapCache.services) {
    if (service[0].includes(tld)) {
      return service[1][0]; // First URL
    }
  }

  throw new Error(`No RDAP server found for TLD: ${tld}`);
}

// Simple, reliable parsing
function parseRDAP(data) {
  return {
    domain: data.ldhName || data.unicodeName,
    status: data.status || [],
    created: data.events?.find(e => e.eventAction === 'registration')?.eventDate,
    expires: data.events?.find(e => e.eventAction === 'expiration')?.eventDate,
    updated: data.events?.find(e => e.eventAction === 'last changed')?.eventDate,
    nameservers: data.nameservers?.map(ns => ns.ldhName) || [],
    registrar: extractRegistrar(data.entities),
    dnssec: data.secureDNS?.delegationSigned || false
  };
}

function extractRegistrar(entities = []) {
  const registrar = entities.find(e => e.roles?.includes('registrar'));
  if (!registrar) return null;

  const vcard = registrar.vcardArray?.[1] || [];
  const name = vcard.find(v => v[0] === 'fn')?.[3];
  const email = vcard.find(v => v[0] === 'email')?.[3];

  return { name, email };
}

// Usage
queryRDAP('example.com')
  .then(data => console.log(data))
  .catch(error => console.error(error));

Comparison:

Aspect WHOIS Code RDAP Code
Lines of Code ~80 ~60
Complexity High (parsing) Low (JSON)
Reliability Fragile Robust
Maintainability Difficult Easy
Performance Slower Faster
Error Handling Custom Standard HTTP

Testing RDAP Implementation

// Unit tests for RDAP client
const assert = require('assert');

describe('RDAP Client', () => {
  describe('queryRDAP', () => {
    it('should return domain data for registered domain', async () => {
      const data = await queryRDAP('google.com');

      assert(data.domain === 'google.com');
      assert(data.status.includes('client transfer prohibited'));
      assert(data.created);
      assert(data.expires);
      assert(data.nameservers.length > 0);
    });

    it('should return available=true for unregistered domain', async () => {
      const data = await queryRDAP('thisisnotarealdomain123456789.com');

      assert(data.available === true);
    });

    it('should handle internationalized domains', async () => {
      const data = await queryRDAP('例え.com');

      assert(data.domain === '例え.com' || data.domain === 'xn--r8jz45g.com');
    });

    it('should handle rate limiting gracefully', async () => {
      // Make many rapid requests
      const promises = [];
      for (let i = 0; i < 100; i++) {
        promises.push(queryRDAP(`test${i}.com`).catch(e => e));
      }

      const results = await Promise.all(promises);
      const rateLimited = results.filter(r => r.message?.includes('429'));

      assert(rateLimited.length > 0, 'Should receive rate limit errors');
    });
  });

  describe('parseRDAP', () => {
    it('should extract all standard fields', () => {
      const mockResponse = {
        ldhName: 'example.com',
        status: ['active'],
        events: [
          { eventAction: 'registration', eventDate: '2020-01-01T00:00:00Z' },
          { eventAction: 'expiration', eventDate: '2025-01-01T00:00:00Z' }
        ],
        nameservers: [
          { ldhName: 'ns1.example.com' },
          { ldhName: 'ns2.example.com' }
        ]
      };

      const parsed = parseRDAP(mockResponse);

      assert(parsed.domain === 'example.com');
      assert(parsed.status.includes('active'));
      assert(parsed.created === '2020-01-01T00:00:00Z');
      assert(parsed.expires === '2025-01-01T00:00:00Z');
      assert(parsed.nameservers.length === 2);
    });
  });
});

Use Case Analysis

Domain Availability Checking

WHOIS Approach:

async function isDomainAvailable(domain) {
  try {
    const whoisData = await queryWHOIS(domain);

    // Must check for various "not found" patterns
    const notFoundIndicators = [
      'No match for',
      'NOT FOUND',
      'No Data Found',
      'not registered',
      'No entries found',
      'Status: free'
    ];

    return notFoundIndicators.some(indicator =>
      whoisData.toLowerCase().includes(indicator.toLowerCase())
    );
  } catch (error) {
    // Connection errors might mean available OR server down
    // No way to distinguish
    throw error;
  }
}

Challenges:

  • Unreliable pattern matching
  • False positives/negatives
  • Different messages per server
  • Error handling ambiguous

RDAP Approach:

async function isDomainAvailable(domain) {
  const response = await fetch(
    `https://rdap.org/domain/${domain}`
  );

  // Clear, standardized status codes
  if (response.status === 404) {
    return true; // Domain available
  }

  if (response.status === 200) {
    return false; // Domain registered
  }

  // Other statuses handled clearly
  if (response.status === 429) {
    throw new Error('Rate limited');
  }

  throw new Error(`Unexpected status: ${response.status}`);
}

Advantages:

  • Reliable status codes
  • No pattern matching
  • Clear error semantics
  • Works consistently across TLDs

Security Research and Threat Intelligence

WHOIS Limitations:

// Monitoring for domain changes (WHOIS)
async function monitorDomain(domain) {
  // Must query periodically
  const initialData = await queryWHOIS(domain);

  setInterval(async () => {
    const currentData = await queryWHOIS(domain);

    // Fragile comparison of text data
    if (initialData !== currentData) {
      // But what changed? Must re-parse and compare fields
      const changes = diffWHOIS(initialData, currentData);
      alertOnChanges(changes);
    }
  }, 3600000); // Check hourly
}

Problems:

  • No structured change detection
  • Text comparison unreliable
  • Resource-intensive polling
  • No historical data access

RDAP Advantages:

// Monitoring for domain changes (RDAP)
async function monitorDomain(domain) {
  let lastETag = null;

  setInterval(async () => {
    const response = await fetch(
      `https://rdap.org/domain/${domain}`,
      {
        headers: lastETag ? {
          'If-None-Match': lastETag
        } : {}
      }
    );

    if (response.status === 304) {
      // No changes since last check
      return;
    }

    if (response.status === 200) {
      const currentData = await response.json();
      lastETag = response.headers.get('ETag');

      // Structured comparison
      const changes = detectChanges(currentData);

      if (changes.nameserversChanged) {
        alert('Nameservers modified - potential hijacking');
      }

      if (changes.statusChanged) {
        alert('Domain status changed - review immediately');
      }

      if (changes.registrarChanged) {
        alert('Domain transferred to new registrar');
      }
    }
  }, 3600000);
}

function detectChanges(newData) {
  // Structured change detection
  return {
    nameserversChanged: /* compare arrays */,
    statusChanged: /* compare status arrays */,
    registrarChanged: /* compare registrar entity */,
    expirationChanged: /* compare expiration event */
  };
}

Benefits:

  • Efficient conditional queries (ETags)
  • Structured change detection
  • Clear field-level monitoring
  • Better historical tracking

WHOIS Problems:

  • No standard way to request access to redacted data
  • Contact information inconsistent or missing
  • No audit trail of who accessed what data
  • Cannot prove data provenance

RDAP Solutions:

{
  "ldhName": "example.com",
  "entities": [
    {
      "roles": ["registrant"],
      "vcardArray": ["vcard", [["fn", {}, "text", "REDACTED"]]],
      "remarks": [
        {
          "title": "DATA ACCESS REQUEST",
          "description": [
            "To request access to redacted data, submit form at:",
            "https://registrar.com/data-access"
          ],
          "links": [
            {
              "rel": "alternate",
              "href": "https://registrar.com/data-access",
              "type": "text/html"
            }
          ]
        }
      ]
    }
  ],
  "notices": [
    {
      "title": "LEGAL ACCESS PROCESS",
      "description": [
        "Law enforcement and legal requests:",
        "Email: [email protected]",
        "Include: case number, jurisdiction, data requested",
        "Response time: 3-5 business days"
      ]
    }
  ]
}

Authenticated Access Example:

// Law enforcement access with credentials
async function getLegalAccess(domain, credentials) {
  const response = await fetch(
    `https://rdap.registrar.com/domain/${domain}`,
    {
      headers: {
        'Authorization': `Bearer ${credentials.token}`,
        'X-Case-Number': credentials.caseNumber,
        'X-Jurisdiction': credentials.jurisdiction
      }
    }
  );

  const data = await response.json();

  // Full contact details provided to authorized requester
  return data;
}

Cost and Resource Implications

Infrastructure Costs

WHOIS Server Costs:

Hardware/VM: Basic server
CPU: Low (text processing)
RAM: Minimal (simple queries)
Storage: Moderate (text logs)
Bandwidth: Low (small responses)
TLS Certificates: None
Load Balancer: Optional
CDN: Not applicable

Estimated monthly cost: $50-200

RDAP Server Costs:

Hardware/VM: Modern server (HTTP/2 support)
CPU: Moderate (JSON processing, authentication)
RAM: Moderate (connection pooling, caching)
Storage: Higher (structured logs, authentication)
Bandwidth: Moderate (JSON overhead ~2-3x text)
TLS Certificates: Required (~$0-200/year)
Load Balancer: Recommended
CDN: Beneficial for global performance

Estimated monthly cost: $200-800

Cost Increase Factors:

  • TLS infrastructure
  • Authentication systems
  • Database for access control
  • Monitoring and logging
  • Compliance features

Cost Reduction Opportunities:

  • Caching reduces backend load
  • HTTP/2 reduces connection overhead
  • Structured data enables better optimization
  • Automated compliance reduces manual review costs

Development Costs

Migration Effort:

Task Estimated Hours
Requirements analysis 8-16
Protocol implementation 40-80
Authentication/authorization 20-40
Privacy controls 20-40
Testing and validation 40-80
Documentation 16-32
Total 144-288 hours

Typical cost: $14,000 - $43,000 (at $100-150/hour)

Maintenance Costs:

RDAP ongoing maintenance is generally lower than WHOIS:

  • Structured data easier to maintain
  • Standard error handling
  • Better monitoring and debugging
  • Fewer format-related bugs

Operational Benefits

RDAP Reduces Costs For:

  1. Customer Support: Clearer error messages reduce support tickets
  2. Compliance: Automated access controls reduce manual review
  3. Development: Standard formats reduce custom parsing code
  4. Monitoring: Structured data enables better alerting
  5. Integration: RESTful API easier to integrate with other systems

ROI Timeline:

Initial investment: $15,000-45,000 (migration)
Annual savings: $5,000-15,000 (reduced support/maintenance)
Break-even: 1-3 years
Long-term benefit: Ongoing savings + better functionality

Future Outlook

WHOIS Phase-Out Timeline

Realistic Expectation:

2025: RDAP mandatory, WHOIS continues (current)
2026-2027: Major registries begin deprecating WHOIS
2028-2029: WHOIS becomes optional/unsupported
2030+: WHOIS largely decommissioned for gTLDs

Factors Affecting Timeline:

  • Legacy client migration speed
  • ccTLD adoption rates
  • Industry stakeholder readiness
  • Regulatory requirements
  • Cost considerations

Warning Signs WHOIS Shutdown Is Near:

  • Formal deprecation notices from major registries
  • ICANN sunset timeline announcement
  • Registrar agreements updated to remove WHOIS requirements
  • Major tools (ICANN Lookup, registrar tools) remove WHOIS support

RDAP Evolution

Short-term Enhancements (1-2 years):

  1. Search Functionality: Standardized domain search APIs

    GET /domains?name=example*
    GET /domains?nsLdhName=ns1.example.com
    
  2. Enhanced Notifications: Server-side change alerts

    {
      "subscriptions": [
        {
          "event": "domain changed",
          "callback": "https://monitor.example.com/webhook"
        }
      ]
    }
    
  3. Bulk Query APIs: Efficient multi-domain lookups

    POST /domains/bulk
    ["domain1.com", "domain2.com", "domain3.com"]
    

Medium-term Development (3-5 years):

  1. Historical Data Access: Query past registration states

    GET /domain/example.com?date=2024-01-01
    
  2. Advanced Filtering: Complex access control policies

  3. Real-time Feeds: Streaming registration changes

  4. Machine Learning Integration: Abuse pattern detection

Long-term Vision (5+ years):

  • Fully automated privacy compliance
  • Blockchain-based audit trails
  • AI-powered threat detection
  • Decentralized RDAP nodes
  • Integration with DNS infrastructure

Industry Impact

Registries and Registrars:

  • Must maintain RDAP infrastructure long-term
  • Opportunity to differentiate with value-added RDAP features
  • Compliance becomes easier with structured data

Security Companies:

  • Better threat intelligence from structured data
  • More reliable monitoring and alerting
  • Improved abuse detection capabilities

Domain Investors:

  • Better research tools and data access
  • More reliable expiration monitoring
  • Enhanced market intelligence

Legal and Compliance:

  • Clearer data access procedures
  • Better audit trails
  • Improved GDPR compliance

Best Practices

For RDAP Implementers

1. Follow All Specifications:

// Implement complete RDAP conformance
{
  "rdapConformance": [
    "rdap_level_0",
    "icann_rdap_response_profile_0",
    "icann_rdap_technical_implementation_guide_0"
  ],
  // ... response data ...
}

2. Implement Proper Security:

// Require TLS 1.2+
server.setSecureContext({
  minVersion: 'TLSv1.2',
  maxVersion: 'TLSv1.3'
});

// Set security headers
app.use(helmet({
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
}));

3. Provide Clear Documentation:

{
  "notices": [
    {
      "title": "RDAP Terms of Service",
      "links": [
        {
          "href": "https://rdap.example.com/tos",
          "type": "text/html"
        }
      ]
    },
    {
      "title": "Rate Limits",
      "description": [
        "Anonymous: 10 queries/minute",
        "Authenticated: 100 queries/minute",
        "Contact us for higher limits"
      ]
    }
  ]
}

4. Handle Privacy Properly:

function applyPrivacyFilters(data, accessLevel) {
  const filtered = { ...data };

  switch (accessLevel) {
    case 'public':
      // Redact personal information
      filtered.entities = redactPersonalData(data.entities);
      addRedactionNotices(filtered);
      break;

    case 'authenticated':
      // Provide organization but not personal
      filtered.entities = redactIndividualData(data.entities);
      break;

    case 'authorized':
      // Full data access
      filtered.entities = data.entities;
      addAccessNotices(filtered, 'Law enforcement access granted');
      break;
  }

  return filtered;
}

For RDAP Clients

1. Implement Proper Error Handling:

async function robustRDAPQuery(domain) {
  try {
    const response = await fetch(
      `https://rdap.org/domain/${domain}`,
      { timeout: 10000 }
    );

    switch (response.status) {
      case 200:
        return await response.json();

      case 404:
        return { available: true };

      case 429:
        const retryAfter = response.headers.get('Retry-After');
        await sleep(retryAfter * 1000);
        return robustRDAPQuery(domain); // Retry

      case 503:
        throw new Error('Service temporarily unavailable');

      default:
        const error = await response.json();
        throw new Error(error.description?.[0] || `HTTP ${response.status}`);
    }
  } catch (error) {
    console.error('RDAP query failed:', error);
    throw error;
  }
}

2. Respect Rate Limits:

class RDAPClient {
  constructor(queriesPerMinute = 10) {
    this.queue = [];
    this.interval = (60 * 1000) / queriesPerMinute;
    this.lastQuery = 0;
  }

  async query(domain) {
    const now = Date.now();
    const timeSinceLastQuery = now - this.lastQuery;

    if (timeSinceLastQuery < this.interval) {
      await sleep(this.interval - timeSinceLastQuery);
    }

    this.lastQuery = Date.now();
    return await queryRDAP(domain);
  }
}

3. Implement Caching:

class CachedRDAPClient {
  constructor() {
    this.cache = new Map();
  }

  async query(domain) {
    // Check cache
    const cached = this.cache.get(domain);
    if (cached && cached.expires > Date.now()) {
      return cached.data;
    }

    // Query RDAP
    const response = await fetch(`https://rdap.org/domain/${domain}`);
    const data = await response.json();

    // Cache based on Cache-Control header
    const cacheControl = response.headers.get('Cache-Control');
    const maxAge = this.parseMaxAge(cacheControl) || 3600; // Default 1 hour

    this.cache.set(domain, {
      data,
      expires: Date.now() + (maxAge * 1000)
    });

    return data;
  }

  parseMaxAge(cacheControl) {
    const match = cacheControl?.match(/max-age=(\d+)/);
    return match ? parseInt(match[1]) : null;
  }
}

4. Handle Redirects and Bootstrap:

class SmartRDAPClient {
  constructor() {
    this.bootstrapCache = null;
  }

  async query(domain) {
    const server = await this.findRDAPServer(domain);
    return await this.queryServer(server, domain);
  }

  async findRDAPServer(domain) {
    // Load bootstrap data
    if (!this.bootstrapCache) {
      const response = await fetch('https://data.iana.org/rdap/dns.json');
      this.bootstrapCache = await response.json();
    }

    // Find appropriate server
    const tld = domain.split('.').pop().toLowerCase();
    for (const service of this.bootstrapCache.services) {
      if (service[0].includes(tld)) {
        return service[1][0];
      }
    }

    throw new Error(`No RDAP server found for .${tld}`);
  }
}

Frequently Asked Questions

Is WHOIS being shut down immediately?

No. As of January 28, 2025, RDAP became the official standard, but WHOIS servers continue operating for backward compatibility. There's no announced shutdown date yet. However, RDAP is now required for all gTLDs, and new features will only be added to RDAP. Expect WHOIS to gradually phase out over the next 3-5 years.

Do I need to update my WHOIS queries right now?

Yes, you should begin migrating to RDAP as soon as practical. While WHOIS still works, it's no longer the official standard and won't receive updates or improvements. RDAP provides better reliability, security, and features. Most major tools and services have already migrated or are in the process.

Can I use both WHOIS and RDAP simultaneously?

Yes, and this is recommended during the transition period. Implement RDAP as your primary method with WHOIS as a fallback for TLDs that haven't fully implemented RDAP or during temporary RDAP service disruptions. This hybrid approach ensures maximum reliability while you complete your migration.

Is RDAP slower than WHOIS because of JSON overhead?

No, RDAP is actually faster in most cases. While JSON responses are slightly larger than WHOIS text, RDAP benefits from HTTP/2 connection reuse, multiplexing, and proper caching mechanisms. In practice, RDAP queries are 3-5x faster than WHOIS, especially when making multiple queries.

Does RDAP cost more to implement than WHOIS?

Initial implementation costs are higher for RDAP (TLS infrastructure, authentication, structured data). However, long-term operational costs are lower due to better maintainability, automated compliance, clearer error handling, and reduced customer support needs. Most organizations see positive ROI within 1-3 years.

Will RDAP make more registration data public?

No, the opposite. RDAP was designed specifically to better handle privacy requirements like GDPR. RDAP enables differentiated access control—public users see limited data while law enforcement and authorized parties can access more information through proper channels. WHOIS had no mechanism for this nuanced approach.

Can I still use command-line tools for RDAP?

Yes. While there's no RDAP equivalent to the whois command built into most operating systems, you can query RDAP using curl and parse JSON with jq:

curl -s "https://rdap.verisign.com/com/v1/domain/example.com" | jq .

Several third-party RDAP command-line tools are also emerging.

How do I know which RDAP server to query?

Use the IANA RDAP Bootstrap Service at https://data.iana.org/rdap/dns.json which maps TLDs to their RDAP servers. Alternatively, use https://rdap.org which automatically routes queries to the correct server. Most RDAP client libraries handle this automatically.

Does RDAP work for IP addresses and ASNs?

Yes. RDAP supports:

  • Domains: /domain/{domain}
  • IP addresses: /ip/{ip-address}
  • ASNs: /autnum/{asn}
  • Nameservers: /nameserver/{nameserver}
  • Entities: /entity/{handle}

This makes RDAP a unified protocol for all registration data, not just domains.

What happens to privacy services with RDAP?

Privacy services (WHOIS protection) continue working with RDAP. In fact, RDAP better supports privacy services through standardized redaction mechanisms and clear notices about privacy protection. The main difference is that RDAP can provide more nuanced privacy (showing some data while protecting other fields) rather than WHOIS's all-or-nothing approach.

Key Takeaways

  • RDAP officially replaced WHOIS as of January 28, 2025, becoming the standard for gTLD registration data access

  • Technical superiority: RDAP uses modern RESTful HTTPS APIs with JSON responses, offering security, structure, and internationalization that WHOIS cannot provide

  • Better privacy compliance: RDAP enables differentiated access control, allowing compliance with GDPR and other regulations while still serving legitimate data access needs

  • Transition period continues: While RDAP is now required, WHOIS servers remain operational for backward compatibility with no announced shutdown date

  • Developers must migrate: Update your code to use RDAP as the primary method, maintaining WHOIS fallback only for ccTLDs or temporary issues

  • Improved performance: RDAP queries are typically 3-5x faster than WHOIS due to HTTP/2, connection reuse, and caching

  • Higher initial costs, lower long-term costs: RDAP implementation requires more upfront investment but reduces operational expenses over time

  • Standardized error handling: RDAP uses HTTP status codes and structured error responses, eliminating WHOIS's fragile text parsing

  • Future-proof: All new features and capabilities will be RDAP-only; WHOIS receives no further development

  • Ecosystem support: All major registries and registrars now support RDAP, with tools and libraries widely available

Next Steps

For Developers

  1. Audit your codebase: Identify all WHOIS usage in your applications
  2. Implement RDAP support: Use the code examples in this guide as starting points
  3. Test thoroughly: Validate RDAP implementation across multiple TLDs
  4. Deploy hybrid approach: RDAP primary with WHOIS fallback during transition
  5. Plan WHOIS removal: Set timeline for deprecating WHOIS code entirely

For Organizations

  1. Assess requirements: Determine if you need differentiated access or authentication
  2. Budget appropriately: Plan for initial implementation costs and long-term savings
  3. Train staff: Ensure your team understands RDAP capabilities and limitations
  4. Update documentation: Revise internal and customer-facing docs to reflect RDAP
  5. Monitor the transition: Track WHOIS sunset announcements and industry updates

For Registries and Registrars

  1. Complete RDAP implementation: Ensure full compliance with ICANN requirements
  2. Test access controls: Validate differentiated access and privacy redaction
  3. Document your API: Provide clear RDAP documentation and examples
  4. Plan WHOIS deprecation: Set internal timeline for WHOIS phase-out
  5. Communicate with customers: Announce RDAP availability and WHOIS timeline

Research Sources

This comprehensive comparison was researched using official specifications and authoritative sources: