
By Jaeho Shin
Store files in Supabase with public or private bucket support and automatic signed URL generation.
released November 13, 2025
npm install strapi-provider-upload-supabase-bucketUpload files to Supabase Storage from Strapi v5 with support for both public and private buckets.
npm install strapi-provider-upload-supabase-bucketAdd to your .env file:
SUPABASE_API_URL=https://your-project.supabase.co
SUPABASE_API_KEY=your-service-role-key
SUPABASE_BUCKET=your-bucket-name
SUPABASE_DIRECTORY=uploads
SUPABASE_PUBLIC_FILES=true
SUPABASE_SIGNED_URL_EXPIRES=3600Create or update config/plugins.ts (or config/plugins.js for JavaScript):
TypeScript:
export default ({ env }) => ({
upload: {
config: {
provider: 'strapi-provider-upload-supabase-bucket',
providerOptions: {
apiUrl: env('SUPABASE_API_URL'),
apiKey: env('SUPABASE_API_KEY'),
bucket: env('SUPABASE_BUCKET'),
directory: env('SUPABASE_DIRECTORY', ''),
publicFiles: env.bool('SUPABASE_PUBLIC_FILES', true),
signedUrlExpires: env.int('SUPABASE_SIGNED_URL_EXPIRES', 3600),
},
sizeLimit: 250 * 1024 * 1024, // 250MB
},
},
});JavaScript:
module.exports = ({ env }) => ({
upload: {
config: {
provider: 'strapi-provider-upload-supabase-bucket',
providerOptions: {
apiUrl: env('SUPABASE_API_URL'),
apiKey: env('SUPABASE_API_KEY'),
bucket: env('SUPABASE_BUCKET'),
directory: env('SUPABASE_DIRECTORY', ''),
publicFiles: env.bool('SUPABASE_PUBLIC_FILES', true),
signedUrlExpires: env.int('SUPABASE_SIGNED_URL_EXPIRES', 3600),
},
sizeLimit: 250 * 1024 * 1024, // 250MB
},
},
});Update config/middlewares.ts (or config/middlewares.js) to allow Supabase URLs:
module.exports = [
'strapi::logger',
'strapi::errors',
{
name: 'strapi::security',
config: {
contentSecurityPolicy: {
useDefaults: true,
directives: {
'connect-src': ["'self'", 'https:'],
'img-src': [
"'self'",
'data:',
'blob:',
'market-assets.strapi.io',
'https://your-project.supabase.co', // Replace with your project URL
],
'media-src': [
"'self'",
'data:',
'blob:',
'market-assets.strapi.io',
'https://your-project.supabase.co', // Replace with your project URL
],
upgradeInsecureRequests: null,
},
},
},
},
'strapi::cors',
'strapi::poweredBy',
'strapi::query',
'strapi::body',
'strapi::session',
'strapi::favicon',
'strapi::public',
];| Option | Type | Required | Default | Description |
|---|---|---|---|---|
apiUrl | string | Yes | - | Supabase project URL |
apiKey | string | Yes | - | Supabase service role key |
bucket | string | Yes | - | Storage bucket name |
directory | string | No | '' | Subdirectory for file organization |
publicFiles | boolean | No | true | Public (true) or private (false) bucket |
signedUrlExpires | number | No | 3600 | Signed URL expiration (seconds, private only) |
publicFiles: true)Files are accessible via permanent public URLs without authentication.
https://your-project.supabase.co/storage/v1/object/public/bucket/uploads/file.jpgBest for: Public assets, images, documents
publicFiles: false)Files require time-limited signed URLs for access.
https://your-project.supabase.co/storage/v1/object/sign/bucket/uploads/file.jpg?token=...Best for: User documents, sensitive files, access-controlled content
// Get file from Strapi
const file = await strapi.plugins.upload.services.upload.findOne(fileId);
// Generate signed URL
const provider = strapi.plugins.upload.provider;
const { url } = await provider.getSignedUrl(file);
// Use the temporary URL (expires after signedUrlExpires seconds)
console.log(url);SUPABASE_API_URLSUPABASE_API_KEYCheck config/middlewares.js includes your Supabase URL in CSP directives.
apiUrl, apiKey, and bucket are correctservice_role key (not anon key)publicFiles: false in configurationTested and verified on:
| Bucket Type | Node 20 | Node 22 |
|---|---|---|
| Public | ✅ | ✅ |
| Private | ✅ | ✅ |
This provider does not collect, track, or transmit any usage data. All operations are performed directly between your Strapi instance and your Supabase project.
service_role key to version controlsignedUrlExpires duration for private bucketsMIT
Contributions are welcome! Please feel free to submit issues or pull requests.
Made with ❤️ for the Strapi community
Share your work with the community and get it listed in the Strapi ecosystem for everyone to discover and use.
Submit
