To integrate Wasabi with Bolt.new, use the @aws-sdk/client-s3 package — the exact same code as AWS S3 — but point the endpoint to s3.wasabisys.com instead of the default AWS endpoint. Wasabi is S3-compatible at roughly 80% lower cost with no egress fees. Store your Wasabi access key and secret in .env, generate pre-signed URLs through a Next.js API route, and upload files directly from the browser.
S3-Compatible Storage at 80% Lower Cost — Wasabi in Bolt.new
Wasabi's core value proposition is simple: it stores objects using the same S3 API your code already knows, but charges roughly 80% less per GB and charges zero egress fees. For a Bolt.new app serving many file downloads — document management, image galleries, media libraries — the absence of egress fees can make the total storage bill a fraction of what AWS S3 would cost. The integration code is almost identical to AWS S3: same @aws-sdk/client-s3 package, same S3Client constructor, same PutObjectCommand and GetObjectCommand — the only change is adding an `endpoint` property pointing to Wasabi's S3-compatible API.
The @aws-sdk/client-s3 package is pure JavaScript with no native C++ bindings, making it fully compatible with Bolt's WebContainer runtime. The SDK communicates exclusively over HTTPS, which is exactly what WebContainers support. You install it via npm inside the Bolt interface and it works immediately during development. The recommended pattern is the same as AWS S3: generate pre-signed URLs server-side in a Next.js API route, then let the browser upload directly to Wasabi's endpoint without routing large file data through your server.
Wasabi has multiple regional endpoints. Choosing the right region for your bucket matters both for latency and compliance. us-east-1 is at s3.wasabisys.com, but us-west-1, eu-central-1, ap-northeast-1, and others each have dedicated endpoints. When configuring your S3Client, match the endpoint URL to the region you selected when creating your Wasabi bucket — a region mismatch causes authentication failures even with correct credentials.
Integration method
Because Wasabi is fully S3-compatible, you use the exact same @aws-sdk/client-s3 package as you would for AWS S3 — the only difference is pointing the S3 client at Wasabi's endpoint (s3.wasabisys.com or a region-specific variant) instead of the default AWS endpoint. The @aws-sdk packages are pure JavaScript and communicate over HTTPS, making them fully compatible with Bolt's WebContainer runtime. Pre-signed URLs generated in a Next.js API route allow the browser to upload files directly to Wasabi without routing through your server.
Prerequisites
- A Wasabi account at wasabi.com — free trial available with no credit card required initially
- A Wasabi bucket created in your chosen region (note the exact region name for endpoint configuration)
- Wasabi access keys from the console: Account → Access Keys → Create New Access Key
- A Bolt.new project using Next.js (for server-side API routes to keep credentials secure)
- Familiarity with environment variables and the concept of pre-signed URLs for secure file access
Step-by-step guide
Create a Wasabi Account, Bucket, and Access Keys
Create a Wasabi Account, Bucket, and Access Keys
Go to wasabi.com and sign up for an account. Wasabi offers a free trial that includes 1TB of storage for 30 days — no credit card required. After creating your account, log in to the Wasabi console at console.wasabisys.com. Create a bucket by clicking 'Create Bucket'. Give it a globally unique name (lowercase letters, numbers, and hyphens only). Select your region — for US users, us-east-1 is fine; for European users, eu-central-1 is lower latency. Take note of which region you choose because you will need the corresponding endpoint URL when configuring the SDK. Click 'Create Bucket' to confirm. Next, generate access keys. In the Wasabi console, go to Account in the top menu, then select 'Access Keys' from the left sidebar. Click 'Create New Access Key'. Wasabi generates an Access Key ID and Secret Access Key pair. Copy both immediately — the Secret Access Key is only shown once. Store them as WASABI_ACCESS_KEY and WASABI_SECRET_KEY in your .env file. Also add WASABI_BUCKET (your bucket name), WASABI_REGION (e.g., us-east-1), and WASABI_ENDPOINT (the corresponding endpoint URL). Wasabi regional endpoints follow this pattern: us-east-1 → s3.wasabisys.com, us-east-2 → s3.us-east-2.wasabisys.com, us-west-1 → s3.us-west-1.wasabisys.com, eu-central-1 → s3.eu-central-1.wasabisys.com, ap-northeast-1 → s3.ap-northeast-1.wasabisys.com. Use the endpoint matching the region you selected when creating your bucket.
Set up the project for Wasabi integration. Install @aws-sdk/client-s3 and @aws-sdk/s3-request-presigner via npm. Create a .env.local file with placeholder values: WASABI_ACCESS_KEY=your_key, WASABI_SECRET_KEY=your_secret, WASABI_BUCKET=your-bucket-name, WASABI_REGION=us-east-1, WASABI_ENDPOINT=https://s3.wasabisys.com. Create a src/lib/wasabi.ts helper file that initializes an S3Client with the Wasabi endpoint configuration.
Paste this in Bolt.new chat
1# .env.local2WASABI_ACCESS_KEY=your_access_key_here3WASABI_SECRET_KEY=your_secret_key_here4WASABI_BUCKET=your-bucket-name5WASABI_REGION=us-east-16WASABI_ENDPOINT=https://s3.wasabisys.comPro tip: Wasabi access keys are account-level by default, giving full access to all your buckets. For production, create a sub-user in the Wasabi console with a bucket-level policy restricted to only the specific bucket your app uses.
Expected result: You have a Wasabi bucket in your chosen region and an access key pair. Your .env file has all five required variables. The @aws-sdk packages are installed in your Bolt project.
Configure the S3 Client for Wasabi
Configure the S3 Client for Wasabi
This is where Wasabi integration diverges from AWS S3: you pass an `endpoint` property to the S3Client constructor pointing at Wasabi's API URL instead of the default AWS endpoint. Everything else — the S3Client class, PutObjectCommand, GetObjectCommand, DeleteObjectCommand, CreatePresignedPostCommand — is identical to AWS S3. This means if you have existing AWS S3 integration code, migrating to Wasabi requires changing exactly one configuration object. Create a shared Wasabi client helper file so you only configure the endpoint in one place. The S3Client must be initialized server-side (in API routes), never in client-side components, because it requires your secret access key. The forcePathStyle: true option is required for Wasabi — AWS S3 defaults to virtual-hosted-style URLs (bucket-name.s3.amazonaws.com), but Wasabi's S3-compatible API requires path-style URLs (s3.wasabisys.com/bucket-name). The @aws-sdk packages are pure JavaScript, communicate over HTTPS, and have zero native C++ dependencies. They install and run inside Bolt's WebContainer without any issues. The SDK handles request signing (AWS Signature V4) automatically — you only need to provide the credentials and endpoint configuration.
Create src/lib/wasabi.ts that exports an S3Client configured for Wasabi with the endpoint from environment variables. Include: credentials (WASABI_ACCESS_KEY, WASABI_SECRET_KEY), region (WASABI_REGION), endpoint (WASABI_ENDPOINT), and forcePathStyle: true. Export the client as a named export 'wasabiClient'. Also export a helper function 'getWasabiConfig' that returns { bucket: WASABI_BUCKET }. Use TypeScript.
Paste this in Bolt.new chat
1// src/lib/wasabi.ts2import { S3Client } from '@aws-sdk/client-s3';34if (!process.env.WASABI_ACCESS_KEY || !process.env.WASABI_SECRET_KEY) {5 throw new Error('Wasabi credentials are not configured in environment variables');6}78export const wasabiClient = new S3Client({9 region: process.env.WASABI_REGION ?? 'us-east-1',10 endpoint: process.env.WASABI_ENDPOINT ?? 'https://s3.wasabisys.com',11 credentials: {12 accessKeyId: process.env.WASABI_ACCESS_KEY,13 secretAccessKey: process.env.WASABI_SECRET_KEY,14 },15 // Required for Wasabi S3-compatible API16 forcePathStyle: true,17});1819export function getWasabiConfig() {20 return {21 bucket: process.env.WASABI_BUCKET ?? '',22 };23}Pro tip: The forcePathStyle: true option is critical for Wasabi. Without it, the SDK tries to use virtual-hosted-style URLs (your-bucket.s3.wasabisys.com), which Wasabi does not support, causing NoSuchBucket errors even when the bucket exists.
Expected result: A reusable wasabiClient is exported from src/lib/wasabi.ts. All API routes can import this client without repeating configuration. The client is configured with the correct Wasabi endpoint and forcePathStyle.
Create Pre-Signed URL API Routes
Create Pre-Signed URL API Routes
Pre-signed URLs are the cornerstone of this integration: instead of routing file data through your server (which adds latency and server costs), you generate a time-limited, cryptographically signed URL server-side, then the browser uploads directly to Wasabi using that URL. The browser never sees your Wasabi secret key — only the temporary signed URL that authorizes a specific upload or download operation. Create two API routes: one that generates pre-signed PUT URLs for uploads, and one that generates pre-signed GET URLs for downloads. The @aws-sdk/s3-request-presigner package's getSignedUrl function works with Wasabi exactly as it does with AWS S3 — pass your configured wasabiClient and a PutObjectCommand or GetObjectCommand with the bucket and key. For uploads, the API route should accept a filename and content type from the client, generate a unique storage key (e.g., using crypto.randomUUID()), create the pre-signed PUT URL with a short expiry (5-15 minutes is typical), and return both the signed URL and the final storage key. The client then PUTs the file binary directly to the signed URL using fetch() — no custom headers needed beyond Content-Type. Note that pre-signed URLs work from the browser only if Wasabi's CORS configuration allows it. This is configured in the next step.
Create two Next.js API routes for Wasabi file operations. First, /api/wasabi/upload-url/route.ts: accept { filename, contentType, sizeBytes } in the request body, generate a unique key like uploads/{uuid}/{filename}, create a pre-signed PutObjectCommand URL expiring in 15 minutes using getSignedUrl from @aws-sdk/s3-request-presigner, return { uploadUrl, key, publicUrl }. Second, /api/wasabi/download-url/route.ts: accept { key } as a query param, generate a pre-signed GetObjectCommand URL expiring in 1 hour, return { downloadUrl }. Import wasabiClient from src/lib/wasabi. Use TypeScript.
Paste this in Bolt.new chat
1// app/api/wasabi/upload-url/route.ts2import { NextRequest, NextResponse } from 'next/server';3import { PutObjectCommand } from '@aws-sdk/client-s3';4import { getSignedUrl } from '@aws-sdk/s3-request-presigner';5import { wasabiClient, getWasabiConfig } from '@/lib/wasabi';67export async function POST(request: NextRequest) {8 const { filename, contentType, sizeBytes } = await request.json();910 if (!filename || !contentType) {11 return NextResponse.json({ error: 'filename and contentType are required' }, { status: 400 });12 }1314 if (sizeBytes > 100 * 1024 * 1024) {15 return NextResponse.json({ error: 'File size exceeds 100MB limit' }, { status: 400 });16 }1718 const { bucket } = getWasabiConfig();19 const key = `uploads/${crypto.randomUUID()}/${filename}`;2021 const command = new PutObjectCommand({22 Bucket: bucket,23 Key: key,24 ContentType: contentType,25 ContentLength: sizeBytes,26 });2728 const uploadUrl = await getSignedUrl(wasabiClient, command, { expiresIn: 900 }); // 15 minutes29 const endpoint = process.env.WASABI_ENDPOINT ?? 'https://s3.wasabisys.com';30 const publicUrl = `${endpoint}/${bucket}/${key}`;3132 return NextResponse.json({ uploadUrl, key, publicUrl });33}3435// app/api/wasabi/download-url/route.ts36import { NextRequest, NextResponse } from 'next/server';37import { GetObjectCommand } from '@aws-sdk/client-s3';38import { getSignedUrl } from '@aws-sdk/s3-request-presigner';39import { wasabiClient, getWasabiConfig } from '@/lib/wasabi';4041export async function GET(request: NextRequest) {42 const key = request.nextUrl.searchParams.get('key');43 if (!key) return NextResponse.json({ error: 'key is required' }, { status: 400 });4445 const { bucket } = getWasabiConfig();46 const command = new GetObjectCommand({ Bucket: bucket, Key: key });47 const downloadUrl = await getSignedUrl(wasabiClient, command, { expiresIn: 3600 }); // 1 hour4849 return NextResponse.json({ downloadUrl });50}Pro tip: Always validate file size server-side in the upload-url route before generating the pre-signed URL. Wasabi will accept any file size the URL authorizes — your validation is the only size enforcement.
Expected result: Two working API routes: POST /api/wasabi/upload-url returns a pre-signed upload URL and the storage key, GET /api/wasabi/download-url?key=... returns a pre-signed download URL. Both use the shared wasabiClient configured with the Wasabi endpoint.
Configure Wasabi CORS for Browser Uploads
Configure Wasabi CORS for Browser Uploads
Browser-direct uploads to Wasabi using pre-signed URLs require a CORS configuration on your bucket that allows PUT requests from your app's origin. Without this, the browser will block the upload with a CORS error even though your pre-signed URL is valid. In the Wasabi console, navigate to your bucket, click the 'Properties' tab, then scroll down to 'CORS Configuration'. Click 'Edit' and enter a CORS policy in JSON format. Your policy needs to allow: your app's origin (and StackBlitz origins for Bolt development), the PUT and GET methods, and the Content-Type and Authorization headers. For development in Bolt's WebContainer, add the StackBlitz WebContainer origin pattern. For Netlify deployment, add your specific .netlify.app domain. Once your app is on a custom domain, add that too. Using the wildcard (*) for AllowedOrigins is acceptable during development but should be restricted to specific domains in production. After saving the CORS configuration, browser uploads from your app should work immediately. CORS configuration in Wasabi takes effect within a few seconds.
Create a CORS configuration helper component that shows the required Wasabi CORS JSON configuration based on the current app URL. Display it as a code block with a copy button. Also build the file upload UI component: a drag-and-drop dropzone that accepts any file type up to 100MB, shows upload progress using the XMLHttpRequest upload progress event (since fetch doesn't support progress), calls /api/wasabi/upload-url to get a pre-signed URL, then PUTs the file directly to Wasabi. After successful upload, display the file key and a download button.
Paste this in Bolt.new chat
1// Wasabi bucket CORS configuration (paste into Wasabi console → Bucket Properties → CORS)2[3 {4 "AllowedOrigins": [5 "https://*.webcontainer-api.io",6 "https://*.stackblitz.io",7 "https://your-app.netlify.app",8 "http://localhost:3000"9 ],10 "AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],11 "AllowedHeaders": ["*"],12 "ExposeHeaders": ["ETag"],13 "MaxAgeSeconds": 360014 }15]Pro tip: CORS only blocks browser-to-Wasabi direct requests. API route uploads (routing file data through your Next.js server) are never subject to CORS — but they are slower and more expensive for large files. Use pre-signed URLs for production.
Expected result: Your Wasabi bucket has a CORS configuration that allows PUT requests from Bolt's WebContainer origins and your deployment domains. Browser-direct uploads using pre-signed URLs succeed without CORS errors.
Build the File Upload Component and Deploy
Build the File Upload Component and Deploy
With the API routes and CORS configured, build a React component that handles the complete upload flow: select a file, call the upload-url API route to get a pre-signed URL, PUT the file directly to Wasabi, and display the result. Track upload progress using XMLHttpRequest (which provides native progress events) instead of the Fetch API (which does not support upload progress). After building and testing the upload flow, deploy to Netlify or Bolt Cloud. In your hosting dashboard, add the same five environment variables you have in .env.local: WASABI_ACCESS_KEY, WASABI_SECRET_KEY, WASABI_BUCKET, WASABI_REGION, and WASABI_ENDPOINT. Trigger a new deploy after adding the variables. Update your Wasabi bucket CORS configuration to add your final deployed domain. The dev-time CORS entries for WebContainer origins can remain — they don't cause security issues, as CORS only controls which origins can make browser requests, and the pre-signed URLs themselves are still required for authentication. Note: during development in Bolt's WebContainer, outbound HTTPS calls to Wasabi work perfectly — you can upload and download files from the preview. There is no webhook requirement for basic file storage, so this integration works end-to-end in Bolt without deployment being a strict prerequisite for testing.
Create a complete FileUploader React component at src/components/FileUploader.tsx. It should: show a drag-and-drop zone and a file picker button, when a file is selected call POST /api/wasabi/upload-url with the filename, contentType, and sizeBytes, then use XMLHttpRequest to PUT the file to the returned uploadUrl while tracking upload progress as a percentage (xhr.upload.addEventListener('progress', ...)), show a progress bar during upload, display a success message with the file key and a download button (which calls GET /api/wasabi/download-url?key=... to get a fresh signed URL) after completion. Handle errors with user-friendly messages.
Paste this in Bolt.new chat
1// src/components/FileUploader.tsx2'use client';3import { useState, useRef } from 'react';45interface UploadResult {6 key: string;7 publicUrl: string;8}910export function FileUploader() {11 const [progress, setProgress] = useState(0);12 const [uploading, setUploading] = useState(false);13 const [result, setResult] = useState<UploadResult | null>(null);14 const [error, setError] = useState<string | null>(null);15 const inputRef = useRef<HTMLInputElement>(null);1617 const uploadFile = async (file: File) => {18 setUploading(true);19 setProgress(0);20 setError(null);21 setResult(null);2223 try {24 // Step 1: Get pre-signed upload URL25 const urlRes = await fetch('/api/wasabi/upload-url', {26 method: 'POST',27 headers: { 'Content-Type': 'application/json' },28 body: JSON.stringify({29 filename: file.name,30 contentType: file.type || 'application/octet-stream',31 sizeBytes: file.size,32 }),33 });34 const { uploadUrl, key } = await urlRes.json();3536 // Step 2: Upload directly to Wasabi using XHR for progress tracking37 await new Promise<void>((resolve, reject) => {38 const xhr = new XMLHttpRequest();39 xhr.upload.addEventListener('progress', (e) => {40 if (e.lengthComputable) setProgress(Math.round((e.loaded / e.total) * 100));41 });42 xhr.addEventListener('load', () => (xhr.status < 300 ? resolve() : reject(new Error(`Upload failed: ${xhr.status}`))));43 xhr.addEventListener('error', () => reject(new Error('Network error during upload')));44 xhr.open('PUT', uploadUrl);45 xhr.setRequestHeader('Content-Type', file.type || 'application/octet-stream');46 xhr.send(file);47 });4849 setResult({ key, publicUrl: `/api/wasabi/download-url?key=${encodeURIComponent(key)}` });50 } catch (err) {51 setError(err instanceof Error ? err.message : 'Upload failed');52 } finally {53 setUploading(false);54 }55 };5657 return (58 <div className="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center">59 <input ref={inputRef} type="file" className="hidden" onChange={(e) => e.target.files?.[0] && uploadFile(e.target.files[0])} />60 {!uploading && !result && (61 <button onClick={() => inputRef.current?.click()} className="bg-orange-500 text-white px-6 py-3 rounded-lg hover:bg-orange-600">62 Choose File to Upload63 </button>64 )}65 {uploading && (66 <div>67 <div className="w-full bg-gray-200 rounded-full h-3 mb-2">68 <div className="bg-orange-500 h-3 rounded-full transition-all" style={{ width: `${progress}%` }} />69 </div>70 <p className="text-sm text-gray-600">Uploading... {progress}%</p>71 </div>72 )}73 {result && (74 <div>75 <p className="text-green-600 font-semibold mb-3">Upload complete!</p>76 <a href={result.publicUrl} className="text-blue-600 underline text-sm" target="_blank" rel="noreferrer">77 Download file78 </a>79 </div>80 )}81 {error && <p className="text-red-600 text-sm">{error}</p>}82 </div>83 );84}Pro tip: Wasabi has no egress fees, which means serving downloads directly from pre-signed Wasabi URLs is cost-free regardless of how many downloads your users make. This is a significant cost advantage over AWS S3, which charges $0.09/GB for egress.
Expected result: A working file upload component that shows drag-and-drop, upload progress, and a download link after completion. Files are stored in Wasabi, not on your server. The integration works both in Bolt's preview and on your deployed site.
Common use cases
Document Storage for SaaS Applications
Store user-uploaded PDFs, contracts, reports, and attachments in Wasabi at a fraction of AWS S3 cost. Pre-signed download URLs let users access their files without making objects public. The no-egress-fee model is especially valuable when users download large documents frequently.
Add document storage to my app using Wasabi cloud storage. Create a /api/wasabi/upload-url route that generates a pre-signed PUT URL for uploading files up to 100MB. Create a /api/wasabi/download-url route that generates a pre-signed GET URL valid for 1 hour. Use @aws-sdk/client-s3 with the Wasabi endpoint. Store WASABI_ACCESS_KEY, WASABI_SECRET_KEY, WASABI_BUCKET, and WASABI_REGION in .env. Add a file upload component that calls the upload-url endpoint, then PUTs the file directly to Wasabi using the signed URL.
Copy this prompt to try it in Bolt.new
Media Library for Content Platforms
Build a media upload and management feature for a blog, course platform, or portfolio site. Images, videos, and audio files upload to Wasabi, and the public object URLs are stored in the database alongside content metadata. Wasabi's no-egress-fee policy keeps media serving costs low at any traffic level.
Create a media library page where users can upload images and videos that are stored in Wasabi. Use @aws-sdk/client-s3 pointing at the Wasabi endpoint to generate pre-signed upload URLs from a /api/media/upload API route. After upload, save the file metadata (name, size, type, wasabi_key, public_url) to the database. Display uploaded media in a responsive grid with delete functionality that calls a /api/media/delete route which removes the object from Wasabi using DeleteObjectCommand. Show upload progress during file transfer.
Copy this prompt to try it in Bolt.new
Backup and Export Archiving
Generate and store data export files in Wasabi as a cost-effective archive. When users request a data export, generate a JSON or CSV file server-side, upload it to Wasabi, and return a time-limited pre-signed download URL. Wasabi's low storage cost makes it practical to retain exports for 30-90 days.
Add a data export feature that generates a CSV of the user's data and stores it in Wasabi. Create a /api/export/generate API route that builds the CSV content, uploads it to Wasabi using PutObjectCommand with a unique key like exports/{userId}/{timestamp}.csv, then returns a pre-signed download URL valid for 24 hours. Show the user a download button that opens the pre-signed URL. List their last 5 exports with creation date and download links.
Copy this prompt to try it in Bolt.new
Troubleshooting
NoSuchBucket error from the S3 SDK even though the bucket exists in the Wasabi console
Cause: The S3Client is configured with the wrong endpoint URL for the region of your bucket. Each Wasabi region has its own endpoint. Using the default s3.wasabisys.com endpoint with a bucket in eu-central-1 causes this error.
Solution: Match the WASABI_ENDPOINT in your .env to the region you chose when creating the bucket. us-east-1 → https://s3.wasabisys.com, eu-central-1 → https://s3.eu-central-1.wasabisys.com, ap-northeast-1 → https://s3.ap-northeast-1.wasabisys.com. Also ensure forcePathStyle: true is set on the S3Client.
1// Ensure forcePathStyle: true in your S3Client config2export const wasabiClient = new S3Client({3 region: process.env.WASABI_REGION,4 endpoint: process.env.WASABI_ENDPOINT, // must match bucket region5 credentials: { accessKeyId: process.env.WASABI_ACCESS_KEY!, secretAccessKey: process.env.WASABI_SECRET_KEY! },6 forcePathStyle: true, // REQUIRED for Wasabi7});CORS error in browser console when trying to PUT a file to the pre-signed Wasabi URL
Cause: The Wasabi bucket's CORS configuration does not include the origin making the request. This is common when testing from Bolt's WebContainer preview (which uses a *.webcontainer-api.io origin) before adding it to the CORS allowlist.
Solution: Update the CORS configuration on your Wasabi bucket to include the WebContainer origin pattern https://*.webcontainer-api.io and your deployment domain. In the Wasabi console, go to your bucket → Properties tab → CORS Configuration → Edit.
SignatureDoesNotMatch error when making API calls from the deployed server
Cause: The WASABI_ACCESS_KEY or WASABI_SECRET_KEY environment variables in your deployment environment are incorrect, have extra whitespace, or were not picked up by a new build after being set.
Solution: Verify the exact key values in Netlify (Site configuration → Environment variables) or Bolt Cloud secrets. Trigger a new deployment after setting environment variables — existing deployments use the old build and do not automatically pick up new environment variable values.
Best practices
- Always set forcePathStyle: true on the S3Client when using Wasabi — this is the most common misconfiguration and causes confusing NoSuchBucket errors.
- Match your WASABI_ENDPOINT to the exact region where your bucket was created — a region mismatch causes authentication failures even with correct credentials.
- Use pre-signed URLs for all file uploads and downloads rather than routing files through your Next.js server — this eliminates server memory pressure and reduces latency.
- Restrict CORS to specific domains in production rather than using a wildcard (*) — add your exact Netlify or Bolt Cloud domain to the AllowedOrigins list.
- Generate unique file keys using crypto.randomUUID() to prevent collisions and avoid leaking original filenames in storage paths.
- Set a reasonable expiry on pre-signed upload URLs (5-15 minutes) — long-lived upload URLs are a security risk if intercepted.
- Store only the Wasabi object key in your database, not the full pre-signed URL — pre-signed URLs expire and should be generated fresh for each download request.
- For production apps, create a Wasabi sub-user with a policy scoped to a single bucket rather than using account-level access keys.
Alternatives
AWS S3 has the most integrations, CDN options, and features in the ecosystem, while Wasabi offers the same SDK at roughly 80% lower storage cost with no egress fees.
Backblaze B2 is similarly priced to Wasabi and also S3-compatible, but Wasabi has been S3-compatible longer and has broader region coverage.
Dropbox is a consumer-oriented file sync service with a sharing-focused API, while Wasabi is a pure object storage service designed for programmatic bulk file storage.
OneDrive integrates with the Microsoft 365 ecosystem and offers file collaboration features, while Wasabi is a raw storage service without collaboration features but at much lower cost.
Frequently asked questions
Is Wasabi really just AWS S3 with a different endpoint?
From a code perspective, yes — you use the exact same @aws-sdk/client-s3 package with the same commands (PutObjectCommand, GetObjectCommand, etc.) and the only code change is adding a Wasabi endpoint URL and forcePathStyle: true to the S3Client constructor. Wasabi built their service to be 100% API-compatible with AWS S3, which is why migrations from S3 to Wasabi typically require only a few minutes of configuration changes.
Can I use Wasabi in Bolt.new during development without deploying?
Yes — unlike some integrations that require deployment (like OAuth flows or webhooks), basic Wasabi file operations work fully inside Bolt's WebContainer. The @aws-sdk/client-s3 package communicates over HTTPS, which WebContainers support. Outbound uploads to Wasabi and pre-signed URL generation both work in the Bolt preview, as long as your bucket's CORS configuration allows the WebContainer origin.
Does Wasabi work with the same code as my existing AWS S3 integration?
Yes, with two small changes: add endpoint: 'https://s3.wasabisys.com' (or the region-specific endpoint for your bucket) and forcePathStyle: true to your S3Client constructor. All commands, pre-signed URL generation, and SDK methods are identical. This makes Wasabi one of the easiest migrations from AWS S3.
Why does Wasabi charge no egress fees?
Wasabi's business model focuses on charging only for storage (per GB per month) rather than charging for data transfer out of their platform. This makes Wasabi significantly cheaper than AWS S3 for apps with high download volumes, like media streaming or large file distribution. Wasabi achieves this through infrastructure partnerships and a different cost structure than AWS.
Can I use Wasabi as a drop-in replacement for Supabase Storage?
They serve similar purposes but have different trade-offs. Wasabi is pure S3-compatible object storage with no additional services, while Supabase Storage integrates with Supabase Auth for row-level security on files, making it better for multi-user apps where different users own different files. Choose Wasabi for high-volume, cost-sensitive storage without the need for per-user file access control.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation