draft optional
This NIP defines standard guidelines for how clients SHOULD detect and render inline media URLs and other embeddable content within event .content fields. The goal is to provide a consistent user experience across different Nostr clients.
Currently, each Nostr client implements its own regex patterns and heuristics for detecting media URLs, leading to inconsistent rendering of the same content across clients. A post that displays an embedded image in one client may show a raw URL in another.
This NIP aims to:
- Document canonical file extension lists for media type detection
- Provide reference regex patterns and test vectors
- Establish a fallback hierarchy that prioritizes structured metadata over URL sniffing
- Enable interoperability testing between clients
Clients SHOULD detect media and embeddable content in the following priority order:
imetatags (NIP-92) - Structured metadata attached by the publisherrtags with metadata - URL references with type hints- URL extension sniffing - Inferring type from file extension (this NIP)
- Content-Type fetching - HTTP HEAD request to determine MIME type (optional, expensive)
Clients SHOULD prefer imeta tags when present, as they provide authoritative metadata from the publisher. Extension sniffing SHOULD be used as a fallback for URLs without accompanying metadata.
Clients SHOULD recognize the following file extensions as media types:
png, jpg, jpeg, gif, webp, avif, heic, svg, jxl, bmp, ico, tiff, tif
mp4, webm, mov, mkv, avi, m4v, 3gp, ogv, wmv, flv
mp3, wav, flac, ogg, opus, aac, m4a, wma, aiff, ape
The following patterns are provided as reference implementations. Clients MAY use equivalent patterns suited to their programming language.
https?:\/\/[^\s<>\[\]"']+\.([a-zA-Z0-9]{1,7})(?:[?#]|$)For convenience, clients MAY use combined patterns:
Images:
https?:\/\/[^\s<>\[\]"']+\.(?:png|jpe?g|gif|webp|avif|heic|svg|jxl|bmp|ico|tiff?)(?:[?#].*)?(?=\s|$)Video:
https?:\/\/[^\s<>\[\]"']+\.(?:mp4|webm|mov|mkv|avi|m4v|3gp|ogv)(?:[?#].*)?(?=\s|$)Audio:
https?:\/\/[^\s<>\[\]"']+\.(?:mp3|wav|flac|ogg|opus|aac|m4a)(?:[?#].*)?(?=\s|$)Clients MAY additionally recognize and embed content from specific platforms. These are OPTIONAL and client-specific, but common patterns include:
| Platform | URL Pattern | Embed Type |
|---|---|---|
| YouTube | youtube.com/watch?v=, youtu.be/, youtube.com/shorts/ |
Video player |
| Spotify | open.spotify.com/(track|album|playlist)/ |
Audio player |
| Apple Music | music.apple.com/ |
Audio player |
| SoundCloud | soundcloud.com/ |
Audio player |
| Twitch | twitch.tv/ |
Video player |
| Wavlake | wavlake.com/ |
Audio player |
| Zap.Stream | zap.stream/ |
Live stream |
| Nostr Nests | nostrnests.com/ |
Audio room |
Clients implementing platform embeds SHOULD:
- Provide user controls to disable automatic embedding
- Respect user privacy preferences (e.g., no auto-loading third-party content)
- Fall back to displaying the URL as a link if embedding fails
Clients SHOULD validate their implementation against these test cases:
| Input URL | Expected Type | Notes |
|---|---|---|
https://example.com/photo.png |
image | Basic PNG |
https://example.com/photo.PNG |
image | Case insensitive |
https://nostr.build/i/abc123.webp |
image | WebP format |
https://example.com/image.jpeg?size=large |
image | With query params |
https://example.com/image.jpg#section |
image | With fragment |
https://example.com/photo.heic |
image | HEIC (iPhone) |
https://example.com/icon.svg |
image | SVG vector |
https://example.com/photo.avif |
image | AVIF format |
https://example.com/file.jxl |
image | JPEG XL |
| Input URL | Expected Type | Notes |
|---|---|---|
https://example.com/video.mp4 |
video | Basic MP4 |
https://example.com/clip.webm |
video | WebM format |
https://example.com/movie.mkv |
video | Matroska |
https://example.com/clip.mov |
video | QuickTime |
https://example.com/old.avi |
video | AVI format |
https://example.com/mobile.3gp |
video | Mobile format |
| Input URL | Expected Type | Notes |
|---|---|---|
https://example.com/song.mp3 |
audio | Basic MP3 |
https://example.com/track.flac |
audio | Lossless FLAC |
https://example.com/podcast.ogg |
audio | Ogg Vorbis |
https://example.com/voice.opus |
audio | Opus codec |
https://example.com/music.wav |
audio | Uncompressed WAV |
https://example.com/track.m4a |
audio | AAC container |
| Input URL | Expected Type | Notes |
|---|---|---|
https://example.com/document.pdf |
link | PDF document |
https://example.com/page |
link | No extension |
https://example.com/api/image |
link | No extension (requires HEAD) |
https://example.com/.png |
link | Hidden file, not image |
https://png.example.com/file |
link | Extension in domain |
https://example.com/fake.png.exe |
link | Double extension |
https://example.com/file.mp4.txt |
link | Text file |
https://example.com/download?file=image.png |
link | Extension in query only |
| Input URL | Expected Type | Notes |
|---|---|---|
https://example.com/path/to/image.png?token=abc&size=large |
image | Complex query |
https://example.com/image.PNG |
image | Uppercase extension |
https://example.com/my%20image.png |
image | URL encoded space |
https://example.com/image.jpeg |
image | JPEG alternate spelling |
https://example.com/image.jpg |
image | JPG short form |
When a URL appears in both .content and as an imeta tag, clients SHOULD:
- Use the
imetametadata for rendering decisions - Optionally hide the raw URL from the text content to avoid duplication
- Display the media embed in a consistent location (inline or at end of post)
When media cannot be loaded, clients SHOULD:
- Display the URL as a clickable link
- Optionally show an error indicator
- NOT remove the URL entirely from display
Clients MUST:
- Sanitize URLs before rendering to prevent XSS attacks
- Validate URL schemes (only
https://andhttp://for media) - Consider Content Security Policy implications for embedded content
- Provide users the option to disable automatic media loading
Clients SHOULD:
- Use
rel="noopener noreferrer"on external links - Consider lazy loading for media-heavy content
- Implement rate limiting for media fetching
Regex syntax varies between programming languages. Implementers should note:
\wmay include Unicode in some languages (Python) but not others (JavaScript)- Lookahead/lookbehind support varies
- Case-insensitive flags differ (
/iin JS,(?i)in Python,RegexOptions.IgnoreCasein C#)
The patterns in this NIP use common features available in most regex engines.
Clients processing many events SHOULD:
- Compile regex patterns once at startup
- Consider using string methods (
endsWith,includes) for simple checks before regex - Cache parsing results for frequently-viewed events
| Client | Language | File | Approach |
|---|---|---|---|
| nostr-tools | TypeScript | nip27.ts | Regex on url.pathname |
| Snort | TypeScript | text.ts, const.ts | Regex patterns |
| Jumble | TypeScript | url.ts#L130-L157, constants.ts#L125-L140 | URL.pathname.endsWith() |
| Primal | TypeScript | constants.ts#L338-L348 | Regex patterns |
| Damus | Swift | NoteContent.swift | lastPathComponent + switch |
| Amethyst | Kotlin | RichTextParser.kt | endsWith() on extension list |
| Coracle | TypeScript | welshman/parser.ts | Regex on URL string |
| Nostria | TypeScript | utils.ts, utilities.service.ts | Regex + CDN pattern matching |
| Format | nostr-tools | Snort | Jumble | Primal | Damus | Amethyst | Coracle | Nostria |
|---|---|---|---|---|---|---|---|---|
| Images | ||||||||
| heic | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
| svg | ✅ | varies | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ |
| avif | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ |
| bmp | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ |
| ico | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| tiff | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Video | ||||||||
| webm | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ |
| avi | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ |
| mkv | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| m3u8 | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ |
| wmv | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ |
| flv | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Audio | ||||||||
| mp3 | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | |
| wav | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
| flac | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Method | ||||||||
| Uses URL API | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| CDN patterns | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
*
- NIP-92: Media Attachments - Provides
imetatags, which this NIP recommends as the primary metadata source - NIP-94: File Metadata - Defines file metadata fields used in
imetatags - NIP-27: Text Note References - Defines
nostr:URI handling - NIP-21:
nostr:URI scheme - Protocol handler specification - NIP-B7: Blossom media - SHA-256 addressable media
- Initial draft: Standardizes media detection patterns and test vectors