# How to Delete Files from Supabase Storage

- Tool: Supabase
- Difficulty: Intermediate
- Time required: 10-15 min
- Compatibility: Supabase (all plans), @supabase/supabase-js v2+
- Last updated: March 2026

## TL;DR

To delete files from Supabase Storage, use the storage.from('bucket').remove(['path/to/file.png']) method from the JavaScript client. You can delete single files or pass an array of file paths for bulk deletion. Make sure your RLS policies on the storage.objects table allow DELETE operations for the authenticated user, and always verify the file path matches exactly what was used during upload.

## Deleting Files from Supabase Storage Buckets

Supabase Storage is an S3-compatible object storage system integrated with PostgreSQL Row Level Security. Deleting files requires both the correct API call and proper RLS policies on the storage.objects table. This tutorial covers single-file and bulk deletion, writing DELETE policies, handling user-scoped folder patterns, and verifying that files were successfully removed.

## Before you start

- A Supabase project with a storage bucket containing files
- The Supabase JS client installed and initialized
- RLS enabled on the storage.objects table (enabled by default)
- Files previously uploaded to the bucket with known paths

## Step-by-step guide

### 1. Delete a single file from a storage bucket

Use the storage.from('bucket-name').remove() method with an array containing the file path. The path must exactly match the path used during upload, including any folder prefixes. The remove method always takes an array of paths, even for a single file. If the file does not exist, the operation succeeds silently without errors.

```
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

// Delete a single file
const { data, error } = await supabase.storage
  .from('avatars')
  .remove(['public/avatar-123.png']);

if (error) {
  console.error('Delete failed:', error.message);
} else {
  console.log('Deleted files:', data);
}
```

**Expected result:** The file is deleted from storage. The data array contains objects describing the deleted files.

### 2. Delete multiple files in a single request

Pass multiple file paths in the array to delete several files at once. This is more efficient than making separate API calls for each file. All files must be in the same bucket. If any file in the array does not exist, the rest are still deleted — there is no rollback on partial failure.

```
// Bulk delete multiple files from the same bucket
const filesToDelete = [
  'user-123/document-a.pdf',
  'user-123/document-b.pdf',
  'user-123/photos/vacation.jpg',
];

const { data, error } = await supabase.storage
  .from('documents')
  .remove(filesToDelete);

if (error) {
  console.error('Bulk delete failed:', error.message);
} else {
  console.log(`Successfully deleted ${data.length} files`);
}
```

**Expected result:** All specified files are removed from the bucket. The response data array includes an entry for each deleted file.

### 3. Write an RLS policy to allow users to delete their own files

Supabase Storage uses RLS on the storage.objects table to control access. Without a DELETE policy, remove() calls will fail silently or return an error. The most common pattern is user-scoped folders where each user's files are stored under their user ID. The policy below checks that the authenticated user owns the folder by comparing auth.uid() to the first segment of the file path.

```
-- Allow users to delete files in their own folder
-- File paths follow the pattern: {user_id}/filename.ext
CREATE POLICY "Users can delete their own files"
ON storage.objects FOR DELETE
TO authenticated
USING (
  bucket_id = 'documents'
  AND (SELECT auth.uid())::text = (storage.foldername(name))[1]
);

-- If you also need a policy for public bucket cleanup
CREATE POLICY "Users can delete their own uploads from public bucket"
ON storage.objects FOR DELETE
TO authenticated
USING (
  bucket_id = 'avatars'
  AND (SELECT auth.uid())::text = (storage.foldername(name))[1]
);
```

**Expected result:** Authenticated users can delete files in their own folder but cannot delete files belonging to other users.

### 4. Delete all files in a user's folder

To delete all files for a specific user or folder, first list the files in that folder, then pass all paths to remove(). The list method returns file metadata including the name property, which you combine with the folder prefix to construct the full path for deletion.

```
async function deleteAllUserFiles(userId: string, bucket: string) {
  // Step 1: List all files in the user's folder
  const { data: files, error: listError } = await supabase.storage
    .from(bucket)
    .list(userId, { limit: 1000 });

  if (listError) {
    console.error('Failed to list files:', listError.message);
    return;
  }

  if (!files || files.length === 0) {
    console.log('No files to delete');
    return;
  }

  // Step 2: Build full paths and delete
  const filePaths = files.map((file) => `${userId}/${file.name}`);

  const { data, error } = await supabase.storage
    .from(bucket)
    .remove(filePaths);

  if (error) {
    console.error('Delete failed:', error.message);
  } else {
    console.log(`Deleted ${data.length} files from ${userId}/`);
  }
}

// Usage
await deleteAllUserFiles('user-uuid-here', 'documents');
```

**Expected result:** All files in the specified user folder are deleted. The folder itself is virtual and disappears when empty.

### 5. Handle deletion errors and verify removal

After deleting files, verify they are gone by attempting to generate a URL or list the folder contents. Common error causes include missing RLS policies, incorrect file paths, and trying to delete from a non-existent bucket. If the remove() call returns no error but the file still exists, the most likely cause is an RLS policy blocking the operation silently.

```
async function deleteAndVerify(bucket: string, path: string) {
  // Delete the file
  const { data, error } = await supabase.storage
    .from(bucket)
    .remove([path]);

  if (error) {
    console.error(`Delete error: ${error.message}`);
    return false;
  }

  // Verify deletion by trying to get a signed URL
  const { data: urlData, error: urlError } = await supabase.storage
    .from(bucket)
    .createSignedUrl(path, 10);

  if (urlError) {
    console.log('File confirmed deleted (cannot generate URL)');
    return true;
  }

  console.warn('File may still exist — check RLS policies');
  return false;
}
```

**Expected result:** The file is deleted and verification confirms it no longer exists in storage.

## Complete code example

File: `storage-delete-helper.ts`

```typescript
// Supabase Storage deletion helper
// Handles single, bulk, and folder-level file deletion

import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

// Delete a single file
export async function deleteFile(bucket: string, path: string) {
  const { data, error } = await supabase.storage
    .from(bucket)
    .remove([path]);

  if (error) throw new Error(`Delete failed: ${error.message}`);
  return data;
}

// Delete multiple files
export async function deleteFiles(bucket: string, paths: string[]) {
  const { data, error } = await supabase.storage
    .from(bucket)
    .remove(paths);

  if (error) throw new Error(`Bulk delete failed: ${error.message}`);
  return data;
}

// Delete all files in a folder (user-scoped pattern)
export async function deleteFolder(bucket: string, folderPath: string) {
  const allFiles: string[] = [];
  let offset = 0;
  const batchSize = 100;

  // Paginate through all files in the folder
  while (true) {
    const { data: files, error } = await supabase.storage
      .from(bucket)
      .list(folderPath, { limit: batchSize, offset });

    if (error) throw new Error(`List failed: ${error.message}`);
    if (!files || files.length === 0) break;

    allFiles.push(...files.map((f) => `${folderPath}/${f.name}`));
    offset += batchSize;

    if (files.length < batchSize) break;
  }

  if (allFiles.length === 0) return [];

  const { data, error } = await supabase.storage
    .from(bucket)
    .remove(allFiles);

  if (error) throw new Error(`Folder delete failed: ${error.message}`);
  return data;
}

// RLS policy SQL — run this in the Supabase SQL Editor:
//
// CREATE POLICY "Users can delete own files"
// ON storage.objects FOR DELETE
// TO authenticated
// USING (
//   bucket_id = 'your-bucket'
//   AND (SELECT auth.uid())::text = (storage.foldername(name))[1]
// );
```

## Common mistakes

- **Passing a single file path as a string instead of wrapping it in an array** — undefined Fix: The remove() method always expects an array of paths, even for a single file: remove(['path/to/file.png']), not remove('path/to/file.png').
- **Including the bucket name in the file path when calling remove()** — undefined Fix: The bucket is specified in storage.from('bucket'). The path array should only contain paths within that bucket, not the bucket name itself.
- **Having an INSERT and SELECT RLS policy but forgetting to add a DELETE policy on storage.objects** — undefined Fix: Each operation needs its own policy. Add a DELETE policy: CREATE POLICY ON storage.objects FOR DELETE TO authenticated USING (...).
- **Assuming remove() will return an error if the file does not exist** — undefined Fix: remove() succeeds silently for non-existent files. If you need to confirm a file exists before deleting, use list() or createSignedUrl() first.

## Best practices

- Always write explicit DELETE RLS policies on storage.objects for each bucket that needs deletion support
- Use user-scoped folder patterns (user_id/filename) and match them in RLS policies for secure per-user file management
- Paginate through files with list() before bulk deletion when a folder may contain more than 100 files
- Verify deletion in critical workflows by attempting to access the file after removal
- Delete associated database records and storage files together to keep your data consistent
- Log deletion operations for audit purposes, especially in multi-user applications
- Use the service_role key in server-side admin tools only when you need to delete files that bypass RLS

## Frequently asked questions

### Does deleting a file from Supabase Storage also delete it from CDN caches?

Supabase invalidates the CDN cache when a file is deleted, but propagation may take a few seconds. If you need immediate cache invalidation, use a cache-busting query parameter in your file URLs.

### Can I recover a deleted file from Supabase Storage?

No. Supabase Storage does not have a recycle bin or versioning feature. Once a file is deleted, it is permanently removed. Implement your own soft-delete pattern by moving files to an archive bucket before permanent deletion.

### Why does remove() succeed but the file is still accessible?

This usually means the DELETE RLS policy is not matching the file. The remove() call may appear to succeed while RLS silently blocks the actual deletion. Check that your policy matches the correct bucket_id and file path pattern.

### Can I delete an entire bucket at once?

You cannot delete a non-empty bucket. First remove all files using the list-and-delete pattern, then delete the bucket itself using supabase.storage.deleteBucket('bucket-name').

### Is there a limit to how many files I can delete in one remove() call?

There is no documented hard limit, but for reliability, delete in batches of 100-500 files at a time. Very large arrays may time out on slower connections.

### How do I delete files from a public bucket?

Public buckets still require RLS policies for write and delete operations. The 'public' setting only affects read access. Write a DELETE policy on storage.objects just as you would for a private bucket.

### Can RapidDev help build a file management system with Supabase Storage?

Yes. RapidDev can build complete file management solutions with Supabase Storage including upload, deletion, access control, folder organization, and admin interfaces with proper RLS policy design.

---

Source: https://www.rapidevelopers.com/supabase-tutorial/how-to-delete-files-from-supabase-storage
© RapidDev — https://www.rapidevelopers.com/supabase-tutorial/how-to-delete-files-from-supabase-storage
