· Nacho Coll · Guides  · 8 min read

IPFS CID Explained: What It Is and How Content Addressing Works

Clear technical explanation of IPFS Content Identifiers (CIDs). How content addressing works, CID versions, and creating your first CID.

Clear technical explanation of IPFS Content Identifiers (CIDs). How content addressing works, CID versions, and creating your first CID.

If you’ve worked with IPFS (InterPlanetary File System), you’ve likely encountered strings like QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG or bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi. These aren’t random gibberish – they’re Content Identifiers (CIDs), the backbone of IPFS’s content addressing system.

Understanding CIDs is crucial for anyone building on IPFS, whether you’re uploading files, building decentralized applications, or implementing content distribution systems. This guide will break down everything you need to know about IPFS CIDs, how content addressing works, and how to start using them in your projects.

IPFS Ninja

What is an IPFS CID?

A Content Identifier (CID) is a unique fingerprint that represents a piece of content in IPFS. Unlike traditional web URLs that point to a location (like https://example.com/file.pdf), CIDs point to the content itself, regardless of where it’s stored.

Think of it this way:

  • Location-based addressing: “Go to 123 Main Street and ask for the red book”
  • Content-based addressing: “Find the book with ISBN 978-0-123456-78-9” (it doesn’t matter which library has it)

CIDs work similarly – they identify content by its cryptographic hash, making the content immutable and verifiable. If even a single byte changes in the file, the CID changes completely.

Why Content Addressing Matters

Traditional web architecture relies on location-based addressing. When you visit https://example.com/image.jpg, you’re trusting:

  1. The domain owner hasn’t changed the content
  2. The server is online and accessible
  3. The content hasn’t been tampered with

With content addressing using CIDs:

  1. Immutability: The CID guarantees the content hasn’t changed
  2. Decentralization: Content can be retrieved from any IPFS node that has it
  3. Verification: You can cryptographically verify you received the correct content
  4. Efficiency: Identical content is deduplicated automatically

Anatomy of a CID

Let’s dissect a typical CID to understand its components:

bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
└─┬─┘└─────────────────┬─────────────────────────────────┘
  │                   │
  │                   └─ Content Hash (Base32 encoded)
  └─ Multibase Prefix (indicates encoding)

A CID contains several pieces of information:

1. Multibase Prefix

The first character indicates how the CID is encoded:

  • Q = Base58 encoding (CIDv0)
  • b = Base32 encoding (CIDv1)
  • f = Base16/hexadecimal (CIDv1)
  • z = Base58 (CIDv1)

2. CID Version

  • CIDv0: Always starts with Qm, uses SHA-256, limited to DAG-PB codec
  • CIDv1: More flexible, supports multiple hash functions and codecs

3. Multicodec

Specifies how the content is structured (DAG-PB, DAG-CBOR, raw bytes, etc.)

4. Multihash

The actual cryptographic hash of the content, including:

  • Hash function identifier (usually SHA-256)
  • Hash length
  • The hash digest

CIDv0 vs CIDv1: Understanding the Differences

IPFS has evolved through two major CID versions, each with distinct characteristics:

CIDv0: The Original Format

CIDv0 CIDs always start with Qm and look like this:

QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG

Characteristics:

  • Base58 encoding only
  • SHA-256 hash function only
  • DAG-PB (Protobuf) codec only
  • 46 characters long
  • Backward compatible with all IPFS implementations

When to use CIDv0:

  • Maximum compatibility with older IPFS nodes
  • Working with existing systems that expect Qm prefixes
  • File storage (most common use case)

CIDv1: The Modern Standard

CIDv1 CIDs are more flexible and can look like:

bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi  # Base32
zb2rhj7crUKTQYRGCRATFaQ6YFLTde2YzdqbbhAASkL9uRDXn      # Base58
f01551220d1e2c35...                                      # Base16

Characteristics:

  • Multiple encoding formats (Base32, Base58, Base16)
  • Support for different hash functions (SHA-256, SHA-512, BLAKE2, etc.)
  • Multiple codecs (Raw, DAG-CBOR, DAG-JSON, etc.)
  • Self-describing format
  • Case-insensitive when using Base32

When to use CIDv1:

  • Building new applications
  • Need case-insensitive identifiers
  • Working with structured data (JSON, CBOR)
  • Using alternative hash functions

Converting Between Versions

You can convert CIDs between versions while maintaining the same content reference:

// CIDv0
const cidv0 = "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG";

// Convert to CIDv1 Base32
const cidv1 = "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi";

// Both reference the same content!

How Content Addressing Works

Content addressing in IPFS follows a deterministic process that ensures the same content always produces the same CID:

1. Content Preparation

When you add content to IPFS, it’s first broken down:

  • Small files: Stored as single blocks
  • Large files: Split into chunks and organized in a Merkle DAG (Directed Acyclic Graph)
  • Directories: Represented as DAG structures linking to files

2. Hashing Process

Each piece of content goes through:

  1. Serialization: Content is formatted according to its codec
  2. Hashing: Cryptographic hash function processes the serialized data
  3. Multihash creation: Hash is wrapped with algorithm and length information
  4. CID assembly: Version, codec, and multihash are combined

3. Merkle DAG Structure

IPFS organizes content in a Merkle DAG where:

  • Each node has a CID
  • Parent nodes reference child nodes by CID
  • Changes to any node propagate up the tree
  • Entire structures can be verified cryptographically
Root CID: bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
├── file1.txt (QmHash1...)
├── file2.jpg (QmHash2...)
└── subdirectory/
    ├── file3.pdf (QmHash3...)
    └── file4.mp4 (QmHash4...)

Practical Examples: Working with CIDs

Let’s explore how to work with CIDs in practice using IPFS Ninja’s API:

Uploading Content and Getting a CID

// Upload a file and get its CID
const uploadFile = async (content, filename) => {
  const response = await fetch('https://api.ipfs.ninja/upload/new', {
    method: 'POST',
    headers: {
      'X-Api-Key': 'bws_1234567890abcdef1234567890abcdef12345678',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      content: btoa(content), // Base64 encode binary content
      description: `Upload of ${filename}`
    })
  });
  
  const result = await response.json();
  console.log('CID:', result.cid);
  console.log('IPFS URL:', result.uris.ipfs);
  console.log('HTTP URL:', result.uris.url);
  
  return result.cid;
};

// Example usage
uploadFile('Hello, IPFS!', 'greeting.txt');
// Returns: bafkreifjxz6zwqh27k5xnr5qfbx4w6n5vuwwwdcngguwjewzj2e3xxfgvi

Pinning Existing Content by CID

If you already have a CID, you can pin it to ensure it stays available:

const pinByCID = async (cid) => {
  const response = await fetch('https://api.ipfs.ninja/pin', {
    method: 'POST',
    headers: {
      'X-Api-Key': 'bws_1234567890abcdef1234567890abcdef12345678',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      cid: cid,
      description: 'Pinned via API'
    })
  });
  
  const result = await response.json();
  console.log('Pinned CID:', result.cid);
  
  return result;
};

// Pin the "Hello World" of IPFS
pinByCID('QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u');

Accessing Content via CID

Once you have a CID, you can access the content through various methods:

// Direct IPFS gateway access
const ipfsUrl = `https://ipfs.ninja/ipfs/${cid}`;

// Custom gateway (if configured)
const customGatewayUrl = `https://my-app.gw.ipfs.ninja/ipfs/${cid}`;

// Fetch content programmatically
const fetchContent = async (cid) => {
  const response = await fetch(`https://ipfs.ninja/ipfs/${cid}`);
  const content = await response.text();
  return content;
};

CID Best Practices for Developers

1. Always Validate CIDs

Before using a CID in your application, validate its format:

const isValidCID = (cid) => {
  // Basic validation patterns
  const cidv0Pattern = /^Qm[1-9A-HJ-NP-Za-km-z]{44}$/;
  const cidv1Pattern = /^[bf][a-z2-7]{58}$/;
  
  return cidv0Pattern.test(cid) || cidv1Pattern.test(cid);
};

2. Handle Both CID Versions

Your application should work with both CIDv0 and CIDv1:

const normalizeCID = (cid) => {
  if (cid.startsWith('Qm')) {
    // CIDv0 - can convert to CIDv1 if needed
    return cid;
  } else if (cid.startsWith('b') || cid.startsWith('f') || cid.startsWith('z')) {
    // CIDv1
    return cid;
  } else {
    throw new Error('Invalid CID format');
  }
};

3. Cache CID Mappings

If you’re generating CIDs frequently, consider caching:

const cidCache = new Map();

const getCachedCID = (content) => {
  const contentHash = btoa(content);
  
  if (cidCache.has(contentHash)) {
    return cidCache.get(contentHash);
  }
  
  // Upload and cache result
  return uploadFile(content).then(cid => {
    cidCache.set(contentHash, cid);
    return cid;
  });
};

4. Use Meaningful Descriptions

When uploading content, include descriptive metadata:

const uploadWithMetadata = async (content, metadata) => {
  return fetch('https://api.ipfs.ninja/upload/new', {
    method: 'POST',
    headers: {
      'X-Api-Key': 'bws_1234567890abcdef1234567890abcdef12345678',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      content: btoa(content),
      description: metadata.name || 'Uploaded file',
      metadata: {
        filename: metadata.filename,
        contentType: metadata.contentType,
        uploadedAt: new Date().toISOString(),
        version: metadata.version || '1.0'
      }
    })
  });
};

Common CID Use Cases

1. Static Website Deployment

Deploy entire websites to IPFS and reference them by CID:

// Upload website directory structure
const deployWebsite = async (files) => {
  const uploads = await Promise.all(
    files.map(file => uploadFile(file.content, file.path))
  );
  
  // Root CID references entire site
  const rootCID = uploads.find(u => u.path === 'index.html').cid;
  console.log(`Website deployed: https://ipfs.ninja/ipfs/${rootCID}`);
  
  return rootCID;
};

To learn more about deploying websites, check out our guide on how to upload files to IPFS.

2. NFT Metadata Storage

Store NFT metadata immutably using CIDs:

const nftMetadata = {
  name: "My Awesome NFT",
  description: "A unique digital collectible",
  image: "ipfs://bafkreibc5sgo2plmjkq2tzmhrn54bk3crhnqekiy7u66fqvqm37pu2e5gw",
  attributes: [
    { trait_type: "Color", value: "Blue" },
    { trait_type: "Rarity", value: "Epic" }
  ]
};

const metadataCID = await uploadFile(
  JSON.stringify(nftMetadata, null, 2), 
  'metadata.json'
);

// Use in smart contract
console.log(`Token URI: ipfs://${metadataCID}`);

3. Content Distribution

Use CIDs for distributed content delivery:

// Upload once, access everywhere
const distributeContent = async (content) => {
  const cid = await uploadFile(content, 'content.txt');
  
  // Content available via multiple gateways
  const gateways = [
    `https://ipfs.ninja/ipfs/${cid}`,
    `https://ipfs.io/ipfs/${cid}`,
    `https://cloudflare-ipfs.com/ipfs/${cid}`
  ];
  
  return { cid, gateways };
};

Understanding IPFS Pinning with CIDs

CIDs are temporary by default – they need to be “pinned” to remain available. Learn more about this crucial concept in our comprehensive guide on what is IPFS pinning.

When choosing an IPFS pinning service, consider reading our comparison of IPFS Ninja vs Pinata or explore our roundup of the best IPFS pinning services available today.

Create Your First CID in 30 Seconds

Ready to generate your first CID? Here’s a quick example using IPFS Ninja’s API:

# Using curl (replace with your actual API key)
curl -X POST https://api.ipfs.ninja/upload/new \
  -H "X-Api-Key: bws_YOUR_API_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "SGVsbG8sIElQRlMgV29ybGQh",
    "description": "My first IPFS upload"
  }'
// Using JavaScript
const createFirstCID = async () => {
  const response = await fetch('https://api.ipfs.ninja/upload/new', {
    method: 'POST',
    headers: {
      'X-Api-Key': 'bws_YOUR_API_KEY_HERE',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      content: btoa('Hello, IPFS World!'), // Base64: "SGVsbG8sIElQRlMgV29ybGQh"
      description: 'My first IPFS upload'
    })
  });
  
  const result = await response.json();
  console.log('🎉 Your first CID:', result.cid);
  console.log('🌐 Access it at:', result.uris.url);
  
  return result;
};

createFirstCID();

This will return something like:

{
  "cid": "bafkreif2pall7dybz7vecqka3zo24irdwabf7rbiiweuhau7a2hjlqvfjw",
  "sizeMB": 0.000017,
  "uris": {
    "ipfs": "ipfs://bafkreif2pall7dybz7vecqka3zo24irdwabf7rbiiweuhau7a2hjlqvfjw",
    "url": "https://ipfs.ninja/ipfs/bafkreif2pall7dybz7vecqka3zo24irdwabf7rbiiweuhau7a2hjlqvfjw"
  }
}

For more detailed API examples, check out our IPFS upload API tutorial.

Conclusion

CIDs are the foundation of IPFS’s content addressing system, providing immutable, verifiable, and decentralized content identification. Understanding how they work – from the technical details of CIDv0 vs CIDv1 to practical implementation patterns – is essential for building robust decentralized applications.

Key takeaways:

  • CIDs uniquely identify content, not locations
  • CIDv0 offers maximum compatibility, CIDv1 provides flexibility
  • Content addressing enables verification and deduplication
  • Proper CID handling is crucial for production applications

Whether you’re storing NFT metadata, deploying decentralized websites, or building content distribution systems, CIDs provide the reliable foundation you need for truly decentralized applications.

Ready to start pinning? Create a free account — 500 files, 1 GB storage, dedicated gateway. No credit card required.

Back to Blog

Related Posts

View All Posts »