Skip to content

Nostr Web

Nostr Web enables hosting and browsing static websites as Nostr events, creating censorship-resistant, decentralized web publishing.

What is Nostr Web?

Nostr Web is a protocol and ecosystem for publishing static websites as Nostr events. Instead of traditional web servers, content is stored across Nostr relays, making sites:

  • Censorship-resistant — Replicated across multiple relays
  • Verifiable — Cryptographically signed by the author
  • Decentralized — No single point of failure
  • Accessible — Works with existing Nostr infrastructure

Protocol Details

Event Kinds

KindNameTypePurpose
1125AssetRegularAll web assets (HTML, CSS, JS, etc.)
1126Page ManifestRegularLinks assets per page
31126Site IndexAddressableMaps routes (content-addressed)
11126EntrypointReplaceablePoints to current site index

DNS Bootstrap

DNS TXT record at _nweb.<host> contains JSON:

{
"v": 1,
"pk": "5e56a8f2c91b3d4e7f0a9c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e",
"relays": [
"wss://shu01.shugur.net",
"wss://shu02.shugur.net",
"wss://shu03.shugur.net"
]
}

Required Fields:

  • v — Version (currently 1)
  • pk — Public key (hex or npub format)
  • relays — Array of WebSocket relay URLs

Note: The DNS record does NOT contain the site index event ID. Clients query relays for the most recent entrypoint (kind 11126) from the specified pubkey, which points to the current site index (kind 31126). This enables automatic updates without DNS changes.

Data Flow

DNS TXT (_nweb.example.com)
Entrypoint (kind 11126) ← Query by pubkey
Site Index (kind 31126) ← Reference via 'a' tag
Page Manifest (kind 1126) ← Get for specific route
Assets (kind 1125) ← HTML, CSS, JS, etc.

Key Change: DNS is set once. All content updates happen via new entrypoint events pointing to new site indexes, eliminating DNS propagation delays.

Security Model

1. Author Pinning

DNS TXT record pins the site’s public key. All events MUST be authored by this public key. Events from other authors are rejected.

2. Content Addressing & Integrity

All assets (kind 1125) MUST include SHA256 content hash in the x tag:

["x", "a3f9c8b2e1d0..."]

This enables:

  • Content deduplication - Relays can identify identical assets
  • Integrity verification - Clients verify content matches the hash
  • Efficient caching - Assets can be cached by hash

3. Content Security Policy (CSP)

Content is rendered in a sandboxed environment with strict CSP:

Extension Pages:

script-src 'self' 'wasm-unsafe-eval';
object-src 'none';

Sandboxed Pages:

script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';

4. Rate Limiting

  • DNS Queries: Max 10 per host per minute
  • Global Limit: Max 100 DNS queries per minute across all hosts

Caching Strategy

DNS Records

  • TTL: Offline only (no expiration)
  • Strategy: Always fetch fresh from DNS-over-HTTPS
  • Fallback: Use cache only when offline

Site Index (kind 31126)

  • TTL: 30 seconds
  • Strategy: Fetch fresh, content-addressed by d tag (truncated hash)
  • Fallback: Use cache if relays offline

Entrypoint (kind 11126)

  • TTL: 0 seconds (always fresh)
  • Strategy: Always query relays for latest replaceable event
  • Rationale: Must detect site updates immediately

Page Manifests (kind 1126)

  • TTL: 30 seconds
  • Strategy: Fetch fresh, identified by event ID
  • Fallback: Use cache if relays offline

Assets (kind 1125)

  • TTL: 7 days
  • Strategy: Content-addressed by SHA256 hash (from x tag)
  • Cache: In-memory with LRU eviction (max 500 entries)

For Publishers: Host Your Site on Nostr

Prerequisites

  • Node.js (v16 or later)
  • npm or yarn
  • A Nostr private key (hex format)
  • Access to Nostr relays
  • Git (for cloning the repository)

Installation

1. Clone the Repository

First, clone or download the Nostr Web repository:

Terminal window
# Clone the repository
git clone https://github.com/Shugur-Network/nw-publisher.git
# Navigate to the nw-publisher directory
cd nw-publisher

Alternatively, install directly from npm (recommended):

Terminal window
npm install -g nw-publisher

Or download from GitHub Releases.

2. Install Publisher (if cloned from source)

Terminal window
# Install dependencies
npm install
# Create global command
npm link

This creates a global nw-publisher command you can use anywhere.

Quick Start

1. Set Up Environment Variables

Create a .env file in the publisher directory:

Terminal window
# Required: Your Nostr private key (64-character hex)
NOSTR_SK_HEX="your_private_key_hex_here"
# Required: Relay URLs (comma-separated)
RELAYS="wss://shu01.shugur.net,wss://shu02.shugur.net,wss://shu03.shugur.net"
# Recommended: Your domain (for DNS TXT record generation)
NWEB_HOST="yourdomain.com"

2. Publish Your Site

Terminal window
nw-publisher deploy /path/to/your/site

Example:

Terminal window
nw-publisher deploy ../examples/hello-world

3. Set Up DNS

The publisher outputs _nweb.txt with instructions. Copy the JSON value into a TXT record:

Host: _nweb.yourdomain.com
Type: TXT
Value: {"v":1,"pk":"npub1...","relays":["wss://relay.damus.io",...]}

Note: DNS only needs to be set once! All future site updates happen through new entrypoint events (kind 11126), eliminating DNS propagation delays.

DNS Provider Examples:

Cloudflare:

Type: TXT
Name: _nweb
Content: {"v":1,"pk":"npub1...","relays":["wss://..."]}
TTL: Auto

Route 53:

Type: TXT
Name: _nweb.yourdomain.com
Value: "{"v":1,"pk":"npub1...","relays":["wss://..."]}"
TTL: 300

Publishing Workflow

Static Site Folder
├─> Scan files (HTML, CSS, JS, fonts, etc.)
├─> Compute SHA256 hashes
├─> Sign as Nostr events
│ └─> Kind 1125: All assets (HTML, CSS, JS, fonts, images)
├─> Publish to relays (parallel)
├─> Create page manifests (kind 1126)
├─> Update site index (kind 31126)
└─> Update entrypoint (kind 11126)

Smart Caching

The publisher uses intelligent caching to avoid republishing unchanged content:

Immutable assets (kind 1125):

  • Cache source: Nostr relays (queries on every deploy)
  • Cache key: ${kind}:${content-hash} (content-addressed)
  • Behavior: If file unchanged and found on relays, reuses cached event ID
  • Benefit: Only publishes new/changed assets
  • Reliability: Always reflects true relay state, no stale local files

Addressable events (kind 31126 - Site Index):

  • Uses content-addressed d tag (first 7-12 chars of content hash)
  • Different content = different event
  • Each version is preserved on relays

Replaceable events (kind 11126 - Entrypoint):

  • Always republished to point to current site index
  • Only latest event per author is kept
  • Ensures clients find the newest version

Site Structure

Input (static site):

my-site/
├─ index.html # Home page
├─ style.css # Stylesheet
├─ app.js # JavaScript
└─ about.html # Subpage (optional)

Output:

my-site/
├─ _nweb.txt # DNS setup instructions
└─ _nweb.txt.json # DNS TXT JSON (ready to paste)

Advanced Usage

Force Full Republish

Terminal window
# Rebuild cache from relays
nw-publisher deploy /path/to/site --rebuild-cache

Custom Relays

Terminal window
RELAYS="wss://custom-relay.example.com" nw-publisher deploy /path/to/site

Multi-Site Publishing

Terminal window
# Same pubkey, different sites
nw-publisher deploy ../examples/hello-world
nw-publisher deploy ../examples/blog
# Each site uses different version tracking

Environment Variables Reference

VariableRequiredDescriptionExample
NOSTR_SK_HEX✅ YesNostr private key (64-char hex)a1b2c3d4...
RELAYS✅ YesComma-separated relay URLswss://shu01.shugur.net,...
NWEB_HOST⚠️ RecommendedYour domainyourdomain.com

Troubleshooting

”Cannot find module ‘nostr-tools’”

Solution: Run npm install first

”NOSTR_SK_HEX not found”

Solution: Create .env file:

Terminal window
echo "NOSTR_SK_HEX=your_hex_key_here" > .env

“Failed to publish to relays”

Causes:

  1. Relays offline (try different relays)
  2. Events too large (relay limits typically ~64KB)
  3. Rate limiting (wait a few minutes)

Site Not Loading After Publishing

Check:

  1. DNS TXT record at _nweb.<yourdomain.com> is correct
  2. JSON format is valid
  3. Pubkey in DNS matches published events
  4. Wait for DNS propagation (5-30 minutes)

Additional Commands

The publisher includes powerful commands for managing your site:

Check Site Status

Terminal window
# Your own site
nw-publisher status
# Another site (no private key needed)
nw-publisher status npub1abc123...

Version Management

Terminal window
# List all versions
nw-publisher versions list
# Show specific version
nw-publisher versions show 1.0.0
# Compare versions
nw-publisher versions compare 0.9.0 1.0.0

Sync Across Relays

Terminal window
# Ensure all versions exist on all relays
nw-publisher sync

Clean Up Events

Terminal window
# Preview what will be deleted
nw-publisher cleanup --orphans --dry-run
# Delete orphaned events
nw-publisher cleanup --orphans
# Delete a specific version
nw-publisher cleanup --version 0.1.0
# Full reset (delete all events)
nw-publisher cleanup --all

Media Assets

Large media files (images, videos, fonts) are included as assets (kind 1125) with appropriate MIME types:

<!-- Image -->
<img src="image.png" alt="Image" />
<!-- Video -->
<video src="video.mp4" controls></video>
<!-- Font -->
<style>
@font-face {
font-family: 'MyFont';
src: url('font.woff2') format('woff2');
}
</style>

The publisher automatically detects MIME types and includes them in the m tag of each asset event.

For Users: Browse Nostr Web Sites

Install the Browser Extension

The Nostr Web browser extension enables transparent browsing of decentralized websites.

Installation Steps

  1. Download from Chrome Web Store
  2. Or download from Firefox Add-ons
  3. Load in Browser:
    • Chrome: Open chrome://extensions/, enable “Developer mode”, click “Load unpacked”
    • Firefox: Open about:debugging#/runtime/this-firefox, click “Load Temporary Add-on”

Features

  • 🌐 Transparent Browsing — Just type a URL, automatic Nostr Web detection
  • Fast Loading — Parallel relay fetching with smart caching
  • 🔒 Secure — Author verification, SHA256 integrity, sandboxed rendering
  • 💾 Smart Caching — DNS offline-only, site index always fresh
  • 📡 Multi-Relay — Fetches from multiple relays for redundancy

Using the Extension

  1. Type any domain in your browser address bar (e.g., example.com)
  2. Extension automatically checks for _nweb.<domain> DNS TXT record
  3. If found, loads the Nostr Web site automatically

How it works:

User navigates to example.com
Extension intercepts navigation
Checks DNS for _nweb.example.com
Found? → Load from Nostr relays
Not found? → Allow normal browsing

Manual Entry

  1. Click the extension icon in your browser toolbar
  2. Enter a domain (e.g., nweb.shugur.com)
  3. Click “Open” or press Enter
  4. Page loads in the Nostr Web viewer

Settings

Click the ⚙️ icon in the viewer to access:

  • Default Website — Set a site to load on extension first launch
  • Clear Cache — Remove all cached DNS records and events (preserves settings)

Troubleshooting

Extension not detecting sites

Problem: Typing a domain doesn’t load Nostr Web site

Solutions:

  1. Check DNS TXT record exists at _nweb.<domain>
  2. Wait for DNS propagation (5-30 minutes after DNS update)
  3. Try manual entry via extension popup
  4. Check browser console for errors (F12)

Slow loading

Problem: Pages take more than 10 seconds to load

Solutions:

  1. Check your internet connection
  2. Verify relays are online and responding
  3. Clear extension cache (Settings → Clear Cache)
  4. Try a different network

Scripts not working

Problem: JavaScript on page doesn’t execute

Solutions:

  1. Check browser console for CSP errors (F12 → Console)
  2. Verify the site was published correctly
  3. Try refreshing the page
  4. Clear cache and reload

Best Practices

For Publishers

  1. Use Multiple Relays — Include at least 2-3 relays for redundancy
  2. Optimize Assets — Keep HTML, CSS, JS small (under 64KB per file)
  3. Enable DNSSEC — Protect against DNS spoofing
  4. Test Before Publishing — Verify site loads in extension before setting DNS
  5. Optimize Media — Compress images and fonts for faster loading

For Users

  1. Use Latest Extension — Keep extension updated for bug fixes and features
  2. Verify Authors — Check site pubkey matches expected author
  3. Report Issues — Help improve the ecosystem by reporting bugs
  4. Clear Cache — If site looks outdated, try clearing cache
  5. Check Console — Use browser console (F12) to diagnose issues

Example: Complete Publishing Flow

0. Setup Nostr Web Publisher

First, install the Nostr Web publisher:

Terminal window
# Install from npm (recommended)
npm install -g nw-publisher
# Or clone from source
git clone https://github.com/Shugur-Network/nw-publisher.git
cd nw-publisher
npm install
npm link

1. Create Your Site

Terminal window
# Navigate to a working directory
cd ~
# Create your site directory
mkdir my-site
cd my-site

Create index.html:

<!DOCTYPE html>
<html>
<head>
<title>My Nostr Web Site</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Welcome to Nostr Web!</h1>
<p>This site is hosted on Nostr relays.</p>
<script src="app.js"></script>
</body>
</html>

Create style.css:

body {
font-family: sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
}
h1 {
color: #7b16ff;
}

Create app.js:

console.log('Hello from Nostr Web!');

2. Configure Publisher

Create .env:

Terminal window
NOSTR_SK_HEX="your_private_key_here"
RELAYS="wss://shu01.shugur.net,wss://shu02.shugur.net"
NWEB_HOST="example.com"

3. Publish

Terminal window
nw-publisher deploy .

Output:

✓ Using keypair from NOSTR_SK_HEX
📝 Processing assets...
[NEW] index.html -> event_id_1
[NEW] style.css -> event_id_2
[NEW] app.js -> event_id_3
✅ Assets: 0 reused, 3 published
📋 Processing manifests...
[MANIF] / -> event_id_4 (new)
🗂️ Updating site index...
[INDEX] version 0.0.1 -> event_id_5 (new)
🚀 Updating entrypoint...
[ENTRY] -> event_id_6 (updated)
📄 Wrote _nweb.txt

4. Configure DNS

Add TXT record from _nweb.txt:

Host: _nweb.example.com
Type: TXT
Value: {"v":1,"pk":"npub1...","relays":["wss://..."]}

5. Test

  1. Wait for DNS propagation (5-30 minutes)
  2. Type example.com in browser with extension installed
  3. Site loads automatically!

Contributing

Contributions welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Commit your changes
  4. Push to the branch
  5. Open a Pull Request

License

MIT License — See LICENSE file for details.


Nostr Web by Shugur — Decentralizing the web, one site at a time 🌐