Avatar resolver library for Node.js, browsers, and edge runtimes (Cloudflare Workers etc.).
- ENS-Avatar >= 1.0.0 is only compatible with ethers v6. If your project is using v5, keep your ens-avatar on latest 0.x version.
- Version 1.0.4+ uses the native Fetch API for maximum compatibility across platforms including Cloudflare Workers and other edge runtimes.
This library works seamlessly across:
- ✅ Node.js 18+ (uses native fetch or http adapter)
- ✅ Browsers (all modern browsers)
- ✅ Cloudflare Workers
- ✅ Other edge runtimes that support standard Fetch API
- Have your web3 provider ready (web3.js, ethers.js)
- [Only for node env] Have jsdom installed.
And good to go!
# npm
npm i @ensdomains/ens-avatar
# yarn
yarn add @ensdomains/ens-avatarimport { StaticJsonRpcProvider } from '@ethersproject/providers';
import { AvatarResolver, utils as avtUtils } from '@ensdomains/ens-avatar';
// const { JSDOM } = require('jsdom'); on nodejs
// const jsdom = new JSDOM().window; on nodejs
const provider = new StaticJsonRpcProvider(
...
);
...
async function getAvatar() {
const resolver = new AvatarResolver(provider);
const avatarURI = await resolver.getAvatar('tanrikulu.eth', { /* jsdomWindow: jsdom (on nodejs) */ });
// avatarURI = https://ipfs.io/ipfs/QmUShgfoZQSHK3TQyuTfUpsc8UfeNfD8KwPUvDBUdZ4nmR
}
async function getHeader() {
const resolver = new AvatarResolver(provider);
const headerURI = await resolver.getHeader('tanrikulu.eth', { /* jsdomWindow: jsdom (on nodejs) */ });
// headerURI = https://ipfs.io/ipfs/QmRFnn6c9rj6NuHenFVyKXb6tuKxynAvGiw7yszQJ2EsjN
}
async function getAvatarMetadata() {
const resolver = new AvatarResolver(provider);
const avatarMetadata = await resolver.getMetadata('tanrikulu.eth');
// avatarMetadata = { image: ... , uri: ... , name: ... , description: ... }
const headerMetadata = await resolver.getMetadata('tanrikulu.eth', 'header');
// headerMetadata = { image: ... , uri: ... , name: ... , description: ... }
const avatarURI = avtUtils.getImageURI({ metadata: avatarMetadata /*, jsdomWindow: jsdom (on nodejs) */ });
// avatarURI = https://ipfs.io/ipfs/QmUShgfoZQSHK3TQyuTfUpsc8UfeNfD8KwPUvDBUdZ4nmR
}- ERC721
- ERC1155
- HTTP
- Base64
- IPFS
const avt = new AvatarResolver(provider, { cache: 300 }); // 5 min response cache in memoryCustom IPFS Gateway (Default: https://ipfs.io)
const avt = new AvatarResolver(provider, { ipfs: 'https://dweb.link' });Custom Arweave Gateway (Default: https://arweave.net)
const avt = new AvatarResolver(provider, { arweave: 'https://arweave.net' });const avt = new AvatarResolver(provider, {
apiKey: {
opensea: 'YOUR_API_KEY',
},
});const avt = new AvatarResolver(provider, {
urlDenyList: ['https://maliciouswebsite.com'],
});You can provide custom HTTP/HTTPS agents for advanced use cases:
import http from 'http';
import https from 'https';
const avt = new AvatarResolver(provider, {
agents: {
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
},
});If you need SSRF protection with custom agents, wrap them with ssrf-req-filter:
import http from 'http';
import https from 'https';
const { requestFilterHandler } = require('ssrf-req-filter');
const avt = new AvatarResolver(provider, {
agents: {
httpAgent: requestFilterHandler(new http.Agent({ keepAlive: true })),
httpsAgent: requestFilterHandler(new https.Agent({ keepAlive: true })),
},
});For local development when you need to access localhost or private network services:
const avt = new AvatarResolver(provider, {
allowPrivateIPs: true, // Allows localhost, 127.0.0.1, 10.x.x.x, 192.168.x.x, etc.
});Common local development scenarios:
- Local IPFS node:
http://127.0.0.1:5001 - Local Ethereum node:
http://localhost:8545 - Docker containers on private networks
Note: This flag is ignored if you provide custom agents (you control security in that case).
All user-generated SVG content is automatically sanitized to prevent XSS attacks:
- Browser/Node.js: Uses DOMPurify (8.74 KB, battle-tested)
- Cloudflare Workers: Uses sanitize-html (parser-based, no DOM dependency)
Both sanitizers are production-ready, actively maintained, and specifically configured for secure SVG handling.
By default, ens-avatar includes built-in protection against Server-Side Request Forgery (SSRF) attacks in Node.js environments. This prevents malicious actors from using avatar URLs to probe internal networks.
Default behavior (recommended for production):
- ✅ Blocks requests to
localhost,127.0.0.1 - ✅ Blocks private IP ranges:
10.x.x.x,192.168.x.x,172.16.x.x-172.31.x.x - ✅ Blocks link-local and other internal addresses
Custom agents: If you provide your own HTTP/HTTPS agents, ens-avatar will use them as-is without applying SSRF protection. You are responsible for securing your custom agents.
Local development: Set allowPrivateIPs: true to disable SSRF protection when you need to access local services (e.g., local IPFS nodes). Never use this in production.
See the Custom Agents section for more details.
⚠️ SECURITY DISCLAIMERWhile ens-avatar implements security measures to help protect against XSS and SSRF attacks, you are ultimately responsible for the security of your application. We strongly recommend:
- Conducting your own security audits before deploying to production
- Implementing additional security layers appropriate for your use case
- Keeping the library updated to receive security patches
- Following security best practices when handling user-generated content
- Properly configuring all security-related options for your environment
This library is provided "as-is" without warranty. The maintainers are not liable for any security vulnerabilities in applications using this library.
-
Create .env file with INFURA_KEY env variable
-
Build the library
-
Node example
node example/node.js ENS_NAME- Browser example
yarn build:demo
http-server example