DNS-based Verification (NIP-05 Identifiers)
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.
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...xyzwithalice@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:
- Fetch
https://example.com/.well-known/nostr.json?name=alice - Check if
names.alicematches the user’s pubkey - If match: ✅ Show verified checkmark
- 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:
- nostr.directory -
yourname@nostr.directory - nostrplebs.com -
yourname@nostrplebs.com - nostr.how -
yourname@nostr.how - iris.to -
yourname@iris.to
Paid Services:
- Nostr.band - Custom domains
- Nostr Verified - Managed verification
Steps:
- Sign up on the service
- Link your Nostr pubkey
- Get your identifier (e.g.,
alice@nostrplebs.com) - Update your profile with this
nip05value - 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.com
✅ This pubkey is yours - Domain owner claims this key
What NIP-05 Does NOT Prove
❌ Real identity - Anyone can buy alice.com
❌ Trustworthiness - 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.cominstead ofexample.comnostr.caminstead ofnostr.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
| Service | Identifier Format | Notes |
|---|---|---|
| nostr.directory | name@nostr.directory | Free, simple setup |
| nostrplebs.com | name@nostrplebs.com | Lightning verification |
| iris.to | name@iris.to | Integrated with Iris client |
| nostr.how | name@nostr.how | Educational focus |
Paid/Premium Services
- Nostr.band - Custom domain support
- Nostr Verified - Managed verification
- Zap.stream - Streaming-focused verification
Best Practices
For Users
- Use your own domain if possible (maximum control)
- Verify the domain matches what you expect
- Don’t trust verification alone - check other signals
- Renew domains to avoid losing your identifier
- Beware of impersonators - check spelling carefully
For Developers
- Cache verification results (24-48 hours)
- Show full identifier to users (
alice@example.com, not just ✅) - Handle failures gracefully (domain down, JSON parse error)
- Support relay hints from verification file
- Validate domain format (no XSS, no malicious redirects)
For Domain Owners
- Use HTTPS (required)
- Enable CORS (
Access-Control-Allow-Origin: *) - Keep it simple (static JSON, no auth required)
- Monitor expiration of domains and SSL certificates
- 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).
Related NIPs
- 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
nip05from 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:
- Learn about bech32 encoding in NIP-19
- Explore reactions in NIP-25
- Understand event deletion in NIP-09
- Browse all NIPs in our reference
Last updated: January 2024 Official specification: GitHub
Client Support
This NIP is supported by the following clients: