Skip to main content

Overview

ButterCMS media assets are served through a global CDN with built-in security features. This guide covers how image URLs work, access control, and best practices for protecting your media.

URL security model

Random CDN slugs

Each media file in ButterCMS is assigned a unique, random slug that serves as its identifier:
https://cdn.buttercms.com/Cw5z1qzSq5qtVMCbUDwQ
                          └──────────────────┘
                          Random unique slug
Security Properties:
  • Slugs are randomly generated by Filestack during upload
  • They are not sequential or predictable
  • Knowledge of one URL does not reveal others

Public CDN access

By default, all media on the ButterCMS CDN is publicly accessible:
  • No authentication required to access images
  • Images can be embedded anywhere (websites, emails, apps)
  • Search engines can index images if linked publicly
This model follows the same security pattern as other headless CMS platforms—media URLs are designed to be publicly embeddable.

Access control strategies

Keep URLs private

If you need to control who sees your images:
  1. Server-side rendering: Fetch images server-side and serve through your authenticated endpoints
  2. Proxy through your app: Create authenticated routes that proxy CDN requests
  3. Short-lived URLs: Generate URLs only when needed, don’t store them long-term

Proxy implementation

// Server-side proxy for protected images
const express = require('express');
const axios = require('axios');
const app = express();

// Middleware to check user authentication
function requireAuth(req, res, next) {
  if (!req.user) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  next();
}

// Proxy endpoint for protected images
app.get('/api/images/:fileId', requireAuth, async (req, res) => {
  try {
    const response = await axios.get(
      `https://cdn.buttercms.com/${req.params.fileId}`,
      { responseType: 'stream' }
    );

    res.setHeader('Content-Type', response.headers['content-type']);
    response.data.pipe(res);
  } catch (error) {
    res.status(404).json({ error: 'Image not found' });
  }
});

Signed URLs pattern

Signed URLs are not a built-in ButterCMS feature. The following is an example pattern you can implement in your own backend to control access through a proxy.
Generate time-limited, signed URLs for sensitive content:
const crypto = require('crypto');

function generateSignedUrl(fileId, expiresIn = 3600) {
  const expires = Math.floor(Date.now() / 1000) + expiresIn;
  const signature = crypto
    .createHmac('sha256', process.env.URL_SIGNING_SECRET)
    .update(`${fileId}:${expires}`)
    .digest('hex');

  return `/api/images/${fileId}?expires=${expires}&signature=${signature}`;
}

// Verification middleware
function verifySignature(req, res, next) {
  const { fileId } = req.params;
  const { expires, signature } = req.query;

  if (Date.now() / 1000 > expires) {
    return res.status(403).json({ error: 'URL expired' });
  }

  const expectedSignature = crypto
    .createHmac('sha256', process.env.URL_SIGNING_SECRET)
    .update(`${fileId}:${expires}`)
    .digest('hex');

  if (signature !== expectedSignature) {
    return res.status(403).json({ error: 'Invalid signature' });
  }

  next();
}

HTTPS enforcement

All ButterCMS CDN URLs use HTTPS:
✅ https://cdn.buttercms.com/abc123
❌ http://cdn.buttercms.com/abc123  (automatically redirects to HTTPS)
Benefits:
  • Encrypted data transmission
  • Protection against man-in-the-middle attacks
  • Required for modern security headers (HSTS, CSP)

Content security policy

Configure your CSP headers to allow ButterCMS images:
Content-Security-Policy: img-src 'self' https://cdn.buttercms.com;

With inline styles (for background images)

Content-Security-Policy: img-src 'self' https://cdn.buttercms.com; style-src 'self' 'unsafe-inline';

Next.js configuration

// next.config.js
module.exports = {
  images: {
    domains: ['cdn.buttercms.com'],
  },
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: "img-src 'self' https://cdn.buttercms.com;"
          }
        ]
      }
    ];
  }
};

Transformation security

Image transformations don’t require additional authentication:
https://cdn.buttercms.com/resize=width:800/abc123
Considerations:
  • Anyone with the file ID can apply any transformation
  • Transformations are cached at the CDN edge
  • No additional cost for transformations

Rate limiting transformations

If you’re concerned about transformation abuse:
  1. Proxy transformations: Route through your server with rate limiting
  2. Cache aggressively: Use CDN caching for transformed images
  3. Monitor usage: Track transformation requests in your analytics
To prevent other sites from embedding your images:

Server-side referrer checking

function checkReferrer(req, res, next) {
  const referrer = req.headers.referer || req.headers.referrer;
  const allowedDomains = ['yourdomain.com', 'yourapp.com'];

  if (referrer) {
    const referrerHost = new URL(referrer).hostname;
    if (!allowedDomains.some(domain => referrerHost.endsWith(domain))) {
      return res.status(403).json({ error: 'Hotlinking not allowed' });
    }
  }

  next();
}

Nginx configuration

location /api/images/ {
    valid_referers none blocked yourdomain.com *.yourdomain.com;
    if ($invalid_referer) {
        return 403;
    }
    # Proxy to CDN...
}

Best practices

Don't Expose File IDs

Avoid exposing CDN file IDs in client-side code for sensitive content.

Use HTTPS Always

Ensure all image references use HTTPS URLs.

Implement CSP

Configure Content Security Policy headers for your domain.

Monitor Access

Track image access patterns for unusual activity.

API token security

Your ButterCMS API tokens (Read/Write) are separate from image access:
Token TypeImage AccessUse Case
Read TokenNot required for CDN imagesFetching content metadata
Write TokenNot required for CDN imagesCreating/updating content
Never expose your Write API Token in client-side code. It can be used to modify or delete content.

Sensitive content guidelines

For truly sensitive media:
  1. Don’t upload to ButterCMS: Use a dedicated secure storage solution
  2. Encrypt before upload: Encrypt files client-side if you must store them
  3. Access control: Implement server-side access control with signed URLs
  4. Audit logging: Track all access to sensitive media

Next steps

Image Transformations

Transform images via URL

Responsive Images

Optimize for devices

Authentication

API authentication

Media Library

Managing media