NIP-05

DNS-based Verification (NIP-05 Identifiers)

final identity

NIP-05 lets users verify their identity by mapping their Nostr pubkey to a domain name (e.g., alice@example.com), providing human-readable identifiers and proof of domain control.

Author
fiatjaf, mikedilger
Last Updated
15 January 2024
Official Spec
View on GitHub →

NIP-05: DNS-based Verification

Status: Final Authors: fiatjaf, mikedilger Category: Identity & Discovery


Overview

NIP-05 allows Nostr users to verify their identity by mapping their public key to a human-readable internet identifier like alice@example.com.

This provides:

  • Human-readable names - Replace npub1abc...xyz with alice@example.com
  • Domain verification - Prove you control a domain
  • Discoverability - Users can find you by your domain name
  • Trust signals - Verified checkmarks in clients

Important: NIP-05 is NOT decentralized - it relies on DNS and HTTPS. It’s a trust anchor, not a requirement.


Why NIP-05 Matters

The Problem: Ugly Public Keys

Nostr public keys are hex strings or bech32 (npub) identifiers:

Hex: 6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93
Npub: npub1de5gss7lkafc0pe2s2sz8wjsx6v4hvxxg8l6e60v8uuguvs7m5fsq4qwxm

Problems:

  • Hard to remember
  • Impossible to type
  • Difficult to verify
  • Not human-friendly

The Solution: Internet Identifiers

NIP-05 lets you use familiar internet identifiers:

alice@example.com
bob@nostr.com
satoshi@bitcoin.org

Benefits:

  • Easy to remember and share
  • Verifiable (proves domain ownership)
  • Professional (use your own domain)
  • Discoverable (find users by name)

How It Works

1. User Claims an Identifier

In your profile (kind 0 event), add a nip05 field:

{
  "kind": 0,
  "content": "{
    \"name\": \"Alice\",
    \"nip05\": \"alice@example.com\",
    \"picture\": \"https://example.com/alice.jpg\"
  }",
  ...
}

2. Domain Publishes Verification File

The domain example.com hosts a JSON file at:

https://example.com/.well-known/nostr.json?name=alice

This file contains:

{
  "names": {
    "alice": "6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93"
  },
  "relays": {
    "6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93": [
      "wss://relay.example.com",
      "wss://relay.damus.io"
    ]
  }
}

Breakdown:

  • names: Maps identifier names to public keys (hex)
  • relays: (Optional) Suggests relays for each pubkey

3. Client Verifies the Claim

When a client sees alice@example.com in a profile:

  1. Fetch https://example.com/.well-known/nostr.json?name=alice
  2. Check if names.alice matches the user’s pubkey
  3. If match: ✅ Show verified checkmark
  4. If no match: ❌ Show as unverified

Setting Up NIP-05 Verification

Option 1: Use Your Own Domain

Requirements:

  • Own a domain (e.g., example.com)
  • Can host a static JSON file
  • HTTPS enabled

Steps:

1. Create the Verification File

Create .well-known/nostr.json on your web server:

{
  "names": {
    "alice": "YOUR_PUBLIC_KEY_HEX",
    "bob": "BOBS_PUBLIC_KEY_HEX"
  }
}

2. Configure CORS Headers

The file must be accessible with proper CORS headers:

Access-Control-Allow-Origin: *

Example (nginx):

location /.well-known/nostr.json {
    add_header Access-Control-Allow-Origin *;
    default_type application/json;
}

Example (Apache .htaccess):

<FilesMatch "nostr.json">
    Header set Access-Control-Allow-Origin "*"
    Header set Content-Type "application/json"
</FilesMatch>

3. Test the Endpoint

Visit https://example.com/.well-known/nostr.json?name=alice

Should return:

{
  "names": {
    "alice": "your_pubkey_hex"
  }
}

4. Update Your Nostr Profile

Set nip05 in your kind 0 event:

{
  "kind": 0,
  "content": "{\"name\":\"Alice\",\"nip05\":\"alice@example.com\"}"
}

5. Wait for Verification

Clients will fetch and verify. You should see a checkmark appear!


Option 2: Use a NIP-05 Service

Many free and paid services offer NIP-05 identifiers:

Free Services:

Paid Services:

Steps:

  1. Sign up on the service
  2. Link your Nostr pubkey
  3. Get your identifier (e.g., alice@nostrplebs.com)
  4. Update your profile with this nip05 value
  5. Service handles verification file

Option 3: Self-Hosted with GitHub Pages

Free and simple if you have a GitHub account:

1. Create a GitHub Repository

Name it yourdomain.github.io (or use custom domain).

2. Create the File Structure

yourdomain.github.io/
└── .well-known/
    └── nostr.json

3. Add nostr.json

{
  "names": {
    "yourname": "YOUR_PUBKEY_HEX"
  }
}

4. Enable GitHub Pages

Settings → Pages → Deploy from branch main.

5. Access Your Identifier

yourname@yourdomain.github.io

For custom domains: Configure DNS and GitHub Pages settings.


Advanced: Multiple Users

Host NIP-05 verification for multiple users on one domain:

{
  "names": {
    "alice": "6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93",
    "bob": "bf2376e17ba4ec269d10fcc996a4746b451152be9031fa48e74553dde5526bce",
    "charlie": "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"
  },
  "relays": {
    "6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93": [
      "wss://relay.example.com"
    ]
  }
}

Use case: Organizations can offer employee@company.com identifiers.


Relay Hints (Optional)

The relays field suggests where to find a user’s events:

{
  "names": {
    "alice": "pubkey_hex"
  },
  "relays": {
    "pubkey_hex": [
      "wss://relay.example.com",
      "wss://relay.damus.io"
    ]
  }
}

Benefits:

  • Clients can query specific relays for this user
  • Reduces load on general-purpose relays
  • Improves message delivery

Note: This is a hint, not a requirement. Clients may ignore it.


Verification Flow (Technical)

Client-Side Verification

async function verifyNIP05(nip05, pubkey) {
  // 1. Parse the identifier
  const [name, domain] = nip05.split('@');

  // 2. Fetch the verification file
  const url = `https://${domain}/.well-known/nostr.json?name=${name}`;
  const response = await fetch(url);
  const data = await response.json();

  // 3. Check if name maps to pubkey
  if (data.names[name] === pubkey) {
    return {
      verified: true,
      relays: data.relays?.[pubkey] || []
    };
  }

  return { verified: false };
}

// Usage
const result = await verifyNIP05('alice@example.com', userPubkey);
if (result.verified) {
  console.log("✅ Verified!");
  console.log("Suggested relays:", result.relays);
}

Caching

Clients should cache verification results to avoid repeated requests:

  • Cache for 24-48 hours
  • Re-verify periodically
  • Invalidate on profile update

Security Considerations

What NIP-05 Proves

You control the domain - You can host files on example.comThis pubkey is yours - Domain owner claims this key

What NIP-05 Does NOT Prove

Real identity - Anyone can buy alice.comTrustworthiness - Scammers can get domains too ❌ Decentralization - DNS/HTTPS are centralized systems ❌ Permanence - Domain can expire or change owners

Verification ≠ Trust

A verified identifier means:

  • “This pubkey controls this domain”

It does NOT mean:

  • “This is the real Alice”
  • “This person is trustworthy”
  • “This account is official”

Example: satoshi@bitcoin.org doesn’t mean it’s the real Satoshi Nakamoto. Anyone who controls bitcoin.org can create that identifier.


Common Attacks

1. Impersonation

Attacker registers a1ice.com (number “1” instead of “l”):

  • Users may not notice the difference
  • Attacker gets verified checkmark

Mitigation: Clients should highlight similar names.

2. Domain Hijacking

If attacker gains control of domain:

  • Can change verification file
  • Can redirect identifier to their pubkey

Mitigation: Use reputable registrars with 2FA.

3. Typosquatting

Common typos of popular domains:

  • examp1e.com instead of example.com
  • nostr.cam instead of nostr.com

Mitigation: User education, client warnings.


Client Support

Full Support (with Checkmarks)

  • Damus - Blue checkmark for verified users
  • Primal - Gold verification badge
  • Amethyst - Verification indicator
  • Snort - Checkmark display
  • Iris - Built-in verification
  • Coracle - Shows verified status

Display Variations

  • Some show full identifier (alice@example.com)
  • Some show just domain (example.com)
  • Some show custom badges (blue, gold, purple)

Check our Client Directory for specifics.


NIP-05 Services Directory

Free NIP-05 Providers

ServiceIdentifier FormatNotes
nostr.directoryname@nostr.directoryFree, simple setup
nostrplebs.comname@nostrplebs.comLightning verification
iris.toname@iris.toIntegrated with Iris client
nostr.howname@nostr.howEducational focus

Paid/Premium Services

  • Nostr.band - Custom domain support
  • Nostr Verified - Managed verification
  • Zap.stream - Streaming-focused verification

Best Practices

For Users

  1. Use your own domain if possible (maximum control)
  2. Verify the domain matches what you expect
  3. Don’t trust verification alone - check other signals
  4. Renew domains to avoid losing your identifier
  5. Beware of impersonators - check spelling carefully

For Developers

  1. Cache verification results (24-48 hours)
  2. Show full identifier to users (alice@example.com, not just ✅)
  3. Handle failures gracefully (domain down, JSON parse error)
  4. Support relay hints from verification file
  5. Validate domain format (no XSS, no malicious redirects)

For Domain Owners

  1. Use HTTPS (required)
  2. Enable CORS (Access-Control-Allow-Origin: *)
  3. Keep it simple (static JSON, no auth required)
  4. Monitor expiration of domains and SSL certificates
  5. Consider offering verification for your community/organization

Common Questions

Is NIP-05 required to use Nostr?

No. NIP-05 is entirely optional. It’s a convenience and trust signal, not a requirement.

Can I have multiple NIP-05 identifiers?

You can claim multiple, but your profile only shows one at a time. Clients verify the one in your nip05 field.

What if my domain expires?

You lose the identifier. Your Nostr account (pubkey) is unaffected, but the verification fails.

Can I change my NIP-05 identifier?

Yes. Update your profile and verify ownership of the new domain.

Do I need to pay for NIP-05?

Not necessarily. Free services exist, or host your own for ~$10/year (domain cost).


  • NIP-01 - Basic protocol (kind 0 user metadata)
  • NIP-35 - Torrents (uses NIP-05 for torrent identifiers)
  • NIP-39 - External identities (extends NIP-05 concept)

For Developers

Implementation Checklist

Displaying NIP-05:

  • Parse nip05 from user’s kind 0 event
  • Fetch https://{domain}/.well-known/nostr.json?name={name}
  • Verify names[name] matches user’s pubkey
  • Cache result (24-48 hours)
  • Show checkmark if verified
  • Handle errors (network, JSON, mismatch)

Relay Hints:

  • Parse relays[pubkey] from verification file
  • Use as additional relay sources for user
  • Don’t rely solely on hints

Security:

  • Validate URL format (prevent XSS)
  • Handle HTTPS errors
  • Timeout long requests
  • Warn on similar-looking identifiers

Code Example

Full verification function:

async function verifyAndDisplayNIP05(profile) {
  const { nip05, pubkey } = profile;

  if (!nip05) return { verified: false };

  try {
    const [name, domain] = nip05.split('@');
    const url = `https://${domain}/.well-known/nostr.json?name=${name}`;

    const response = await fetch(url, { timeout: 5000 });
    const data = await response.json();

    if (data.names[name]?.toLowerCase() === pubkey.toLowerCase()) {
      return {
        verified: true,
        identifier: nip05,
        relays: data.relays?.[pubkey] || []
      };
    }
  } catch (error) {
    console.error('NIP-05 verification failed:', error);
  }

  return { verified: false };
}

Technical Specification

For the complete technical specification, see NIP-05 on GitHub.


Summary

NIP-05 provides human-readable, verifiable identifiers:

Internet identifiers - alice@example.com instead of npub1abc...Domain verification - Proves domain control ✅ Discoverability - Find users by name ✅ Relay hints - Suggest where to find events

⚠️ Limitations:

  • Centralized (relies on DNS/HTTPS)
  • Verification ≠ real identity
  • Domains can expire or change hands

Best for: Professional identity, branding, discoverability. Not required: Nostr works fine without it.


Next Steps:


Last updated: January 2024 Official specification: GitHub

Client Support

This NIP is supported by the following clients:

damus primal amethyst snort iris coracle nostrudel satellite nos nostur plebstr current yakihonne
View all clients →

Related NIPs

NIP-01 NIP-35 NIP-39
← Browse All NIPs