npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

strapi-r2-storage

v1.0.2

Published

Production-ready Cloudflare R2 storage provider for Strapi with advanced features

Readme

strapi-r2-storage

A robust and feature-rich Cloudflare R2 storage provider for Strapi v4 & v5 with advanced file naming, validation, compression, and retry mechanisms.

Features

  • 🚀 Strapi v4 & v5 Compatible - Works with both Strapi v4 and v5
  • 📁 Simple File Naming - Two reliable strategies (hash, UUID)
  • 🔄 Smart File Renaming - Automatic file renaming with original name preservation
  • 🗂️ Smart Folder Structure - Organize files by date or custom logic
  • 🔒 File Validation - Size limits, MIME type filtering
  • 📦 Compression Support - Automatic compression for compatible file types
  • 🔄 Retry Logic - Configurable retry with exponential backoff
  • 🌐 CDN Support - Works with Cloudflare CDN and custom domains
  • 📊 Health Checks - Built-in provider health monitoring
  • 🪵 Logging - Comprehensive logging for debugging
  • TypeScript - Full TypeScript support with type definitions

Installation

npm install strapi-r2-storage

Configuration

Add the provider configuration to your Strapi project:

Strapi Media Library Security Setup

To enable proper thumbnail display in Strapi's Media Library when using external storage providers like R2, you need to configure the Content Security Policy (CSP) to allow loading images from your R2 domain.

Update your config/middlewares.js file:

// config/middlewares.js
module.exports = ({ env }) => [
  'strapi::errors',
  'strapi::cors',
  'strapi::poweredBy',
  'strapi::logger',
  'strapi::query',
  'strapi::body',
  'strapi::session',
  'strapi::favicon',
  'strapi::public',
  {
    name: 'strapi::security',
    config: {
      contentSecurityPolicy: {
        useDefaults: true,
        directives: {
          'connect-src': ["'self'", 'https:'],
          'img-src': [
            "'self'",
            'data:',
            'blob:',
            // Add your R2 domain here
            env('CLOUDFLARE_CDN_URL') ? new URL(env('CLOUDFLARE_CDN_URL')).hostname : null,
          ].filter(Boolean),
          'media-src': [
            "'self'",
            'data:',
            'blob:', 
            // Add your R2 domain here
            env('CLOUDFLARE_CDN_URL') ? new URL(env('CLOUDFLARE_CDN_URL')).hostname : null,
          ].filter(Boolean),
          upgradeInsecureRequests: null,
        },
      },
    },
  },
];

Important: Replace CLOUDFLARE_CDN_URL with your actual environment variable name that contains your R2 public URL.

Basic Configuration

// config/plugins.js
module.exports = {
  upload: {
    config: {
      provider: 'strapi-r2-storage',
      providerOptions: {
        accountId: process.env.CLOUDFLARE_ACCOUNT_ID,
        accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID,
        secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY,
        bucket: process.env.CLOUDFLARE_BUCKET,
        cdnUrl: process.env.CLOUDFLARE_CDN_URL, // optional
      },
    },
  },
};

Advanced Configuration

// config/plugins.js
module.exports = {
  upload: {
    config: {
      provider: 'strapi-r2-storage',
      providerOptions: {
        // Required settings
        accountId: process.env.CLOUDFLARE_ACCOUNT_ID,
        accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID,
        secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY,
        bucket: process.env.CLOUDFLARE_BUCKET,
        
        // Optional settings
        region: 'auto', // default: 'auto'
        baseUrl: process.env.CLOUDFLARE_BASE_URL, // custom domain
        cdnUrl: process.env.CLOUDFLARE_CDN_URL, // CDN URL
        publicDomain: process.env.CLOUDFLARE_PUBLIC_DOMAIN, // R2 public domain
        
        // File naming and organization
        naming: {
          strategy: 'hash', // 'hash' | 'uuid'
          preserveExtension: true,
          folderStructure: 'year-month', // 'flat' | 'year' | 'year-month' | 'year-month-day'
        },
        
        // File validation
        maxFileSize: 50 * 1024 * 1024, // 50MB
        allowedMimeTypes: ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'],
        blockedMimeTypes: ['application/x-executable'],
        
        // Performance and caching
        enableCompression: true,
        cacheControl: 'public, max-age=31536000', // 1 year
        enablePublicRead: true,
        
        // Additional metadata
        metadata: {
          'uploaded-by': 'strapi',
          'environment': process.env.NODE_ENV,
        },
        
        // Retry configuration
        retryOptions: {
          maxRetries: 3,
          retryDelay: 1000, // ms
        },
        
        // Debugging
        enableLogging: process.env.NODE_ENV === 'development',
      },
    },
  },
};

Environment Variables

Create a .env file in your Strapi project root:

# Cloudflare R2 Configuration
CLOUDFLARE_ACCOUNT_ID=your_account_id_here
CLOUDFLARE_ACCESS_KEY_ID=your_access_key_here
CLOUDFLARE_SECRET_ACCESS_KEY=your_secret_key_here
CLOUDFLARE_BUCKET=your_bucket_name

# Optional: CDN/Custom Domain
CLOUDFLARE_CDN_URL=https://your-cdn-domain.com
CLOUDFLARE_BASE_URL=https://your-custom-domain.com

Configuration Options

File Naming Strategies

| Strategy | Description | Example Output | Best For | |----------|-------------|----------------|----------| | hash | Uses Strapi's generated hash (default) | a1b2c3d4e5f6.jpg | Standard usage, consistent with Strapi defaults | | uuid | Generated UUID v4 | 550e8400-e29b-41d4-a716-446655440000.jpg | Complete uniqueness, no hash conflicts |

Recommendation: Use uuid for maximum reliability and to avoid any potential file naming issues.

Folder Structures

| Structure | Description | Example Path | |-----------|-------------|--------------| | flat | All files in root (default) | file.jpg | | year | Organized by year | 2024/file.jpg | | year-month | Year and month | 2024/03/file.jpg | | year-month-day | Full date | 2024/03/15/file.jpg |

File Validation

// Example: Only allow images under 10MB
naming: {
  maxFileSize: 10 * 1024 * 1024, // 10MB
  allowedMimeTypes: [
    'image/jpeg',
    'image/png',
    'image/webp',
    'image/gif'
  ],
}

Strapi v5 Compatibility

This provider is fully compatible with both Strapi v4 and v5. The API includes the new customParams parameter required for v5:

// The provider automatically handles both v4 and v5 API signatures
// v4: upload(file)
// v5: upload(file, customParams)

Usage Examples

Development vs Production

// Different settings per environment
const isDevelopment = process.env.NODE_ENV === 'development';

module.exports = {
  upload: {
    config: {
      provider: 'strapi-r2-storage',
      providerOptions: {
        // ... other config
        enableLogging: isDevelopment,
        enableCompression: !isDevelopment, // Disable compression in dev for faster uploads
        cacheControl: isDevelopment 
          ? 'no-cache' 
          : 'public, max-age=31536000',
        naming: {
          strategy: isDevelopment ? 'hash' : 'uuid',
        },
      },
    },
  },
};

Health Checks

The provider includes a built-in health check method:

// In your Strapi application
const uploadProvider = strapi.plugins.upload.provider;
const healthCheck = await uploadProvider.checkHealth();

console.log(healthCheck);
// { status: 'ok' } or { status: 'error', message: 'Error details' }

Troubleshooting

Common Issues

  1. Authentication Errors

    • Verify your Cloudflare credentials
    • Ensure the API token has R2 permissions
  2. Upload Failures

    • Check file size limits
    • Verify MIME type restrictions
    • Review bucket permissions
  3. URL Generation Issues

    • Confirm CDN URL configuration
    • Check bucket public access settings
  4. Files Not Accessible (Cannot Copy/Download)

    • Problem: Generated URLs return errors or are not accessible
    • Solution: Configure proper public access
    // Option 1: Use custom domain (Recommended)
    providerOptions: {
      cdnUrl: 'https://your-domain.com',
      enablePublicRead: true,
    }
       
    // Option 2: Use R2 public domain
    providerOptions: {
      publicDomain: 'https://pub-abc123.r2.dev', // Your actual R2 public domain
      enablePublicRead: true,
    }
       
    // Option 3: For private files, ensure signed URLs work
    providerOptions: {
      enablePublicRead: false, // Will generate signed URLs
    }
  5. R2 Bucket Configuration

    • In Cloudflare dashboard, ensure your R2 bucket has public access enabled if using enablePublicRead: true
    • Set up custom domain in R2 settings for better URL structure

Debug Mode

Enable logging to troubleshoot issues:

providerOptions: {
  // ... other config
  enableLogging: true,
}

This will output detailed logs for all operations including uploads, deletions, and retries.

Performance Tips

  1. Enable Compression: Reduces bandwidth for text-based files
  2. Use CDN URLs: Faster content delivery worldwide
  3. Configure Caching: Set appropriate cache headers
  4. Optimize Retry Settings: Balance reliability with performance

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License - see LICENSE file for details.

Related