How to Generate Pre-signed URLs for Supabase Storage with Astro on Cloudflare Workers
LaunchFast Logo LaunchFast

How to Generate Pre-signed URLs for Supabase Storage with Astro on Cloudflare Workers

Rishi Raj Jain
How to Generate Pre-signed URLs for Supabase Storage with Cloudflare Workers ℹ️  Available today with LaunchFast Starter Kits

In this guide, you will learn how to generate pre-signed URLs for Supabase Storage with Astro on Cloudflare Workers. You will go through the process of setting up a new Astro project, enabling server-side rendering using Cloudflare adapter, obtaining Supabase credentials and then creating functions to generate pre-signed URLs for retrieval and upload from Supabase Storage.

High Quality Starter Kits with built-in authentication flow (Auth.js), object uploads (AWS, Clouflare R2, Firebase Storage, Supabase Storage), integrated payments (Stripe, LemonSqueezy), email verification flow (Resend, Postmark, Sendgrid), and much more. Compatible with any database (Redis, Postgres, MongoDB, SQLite, Firestore).
Next.js Starter Kit
SvelteKit Starter Kit
Need a custom landing page built?

Prerequisites

To follow along, you will need:

Table Of Contents

Create a new Astro application

Let’s get started by creating a new Astro project. Open your terminal and run the following command:

Terminal window
npm create astro@latest my-app

npm create astro is the recommended way to scaffold an Astro project quickly.

When prompted, choose:

  • Use minimal (empty) template when prompted on how to start the new project.
  • Yes when prompted to install dependencies.
  • Yes when prompted to initialize a git repository.

Once that’s done, you can move into the project directory and start the app:

Terminal window
cd my-app
npm run dev

The app should be running on localhost:4321. Next, execute the command below to install the necessary library for building the application:

Terminal window
npm install

No additional libraries are needed as we’ll be using the native fetch API to communicate directly with Supabase’s REST API.

Integrate Cloudflare adapter in your Astro project

To generate pre-signed URLs for each object dynamically, you will enable server-side rendering in your Astro project via the Cloudflare adapter. Execute the following command:

Terminal window
npx astro add cloudflare

When prompted, choose the following:

  • Y when prompted whether to install the Cloudflare dependencies.
  • Y when prompted whether to make changes to Astro configuration file.

You have successfully enabled server-side rendering in Astro.

To make sure that the output is deployable to Cloudflare Workers, create a wrangler.toml file in the root of the project with the following code:

wrangler.toml
name = "supabase-storage-astro-workers"
main = "dist/_worker.js"
compatibility_date = "2025-04-01"
compatibility_flags = [ "nodejs_compat" ]
[assets]
directory="dist"
binding="ASSETS"
[vars]
SUPABASE_URL=""
SUPABASE_ANON_KEY=""
SUPABASE_BUCKET_NAME=""

Post that, make sure that you have both an .env file and a wrangler.toml file with the variables defined so that they can be accessed during npm run dev and when deployed on Cloudflare Workers respectively.

Further, update the astro.config.mjs file with the following to be able to access these variables in code programmatically:

astro.config.mjs
// ... Existing imports...
import { defineConfig, envField } from 'astro/config'
export default defineConfig({
env: {
schema: {
SUPABASE_URL: envField.string({ context: 'server', access: 'secret', optional: false }),
SUPABASE_ANON_KEY: envField.string({ context: 'server', access: 'secret', optional: false }),
SUPABASE_BUCKET_NAME: envField.string({ context: 'server', access: 'secret', optional: false }),
}
}
// adapter
})

Create a Supabase project

  1. Go to supabase.com and sign in to your account.

  2. Click on New Project to create a new project.

  3. Fill in the project details:

    • Name: Choose a name for your project
    • Database Password: Set a strong password for your database
    • Region: Select a region closest to your users
  4. Click Create new project and wait for the project to be set up.

  5. Once the project is ready, go to the Settings tab in your project dashboard.

  6. Navigate to API Keys section to find your API key, and copy the following value:

    • anon public key (this will be your SUPABASE_ANON_KEY)
  7. Navigate to the Data API section to find your project URL, and copy the following value:

    • Project URL (this will be your SUPABASE_URL)

Configure Supabase Storage Bucket

  1. In your Supabase dashboard, navigate to Storage in the left sidebar.

  2. Click on Create a new bucket.

  3. Enter a bucket name (this will be your SUPABASE_BUCKET_NAME value).

  4. Choose whether the bucket should be public or private:

    • Public: Files can be accessed by anyone with the URL
    • Private: Files require authentication to access
  5. Click Create bucket.

Configure Supabase Storage RLS Policies

Row Level Security (RLS) policies control access to your storage buckets. For pre-signed URLs, you’ll need to create policies that allow authenticated users to upload and download files.

  1. In your Supabase dashboard, go to Storage > Policies.

  2. Select your bucket and click New Policy.

  3. For upload access, create a policy with the following settings:

    • Policy Name: Allow authenticated uploads
    • Allowed Operations: INSERT
    • Target roles: anon
    • Policy Definition: bucket_id = ‘your-bucket-name’
  4. For download access, create another policy:

    • Policy Name: Allow authenticated downloads
    • Allowed Operations: SELECT
    • Target roles: anon
    • Policy Definition: bucket_id = ‘your-bucket-name’
  5. Click Review and then Save policy.

Generate the pre-signed URLs

1. Access the Environment Variables

The first step is to access the necessary environment variables during the runtime to communicate with Supabase’s REST API. From Astro 5.6 and beyond, the way you want to access runtime environment variables in your code is by using the getSecret function from astro:env/server to keep things provider agnostic. This is crucial for storing sensitive information securely without hardcoding it into your application. You’ll retrieve the following variables:

  • Supabase Project URL
  • Supabase Anon Key
  • Supabase Storage Bucket Name
src/storage/supabase.ts
import { getSecret } from 'astro:env/server'
const body = JSON.stringify({ expiresIn: 60 * 60 })
const SUPABASE_URL = getSecret('SUPABASE_URL')
const SUPABASE_BASE_STORAGE_API = `${SUPABASE_URL}/storage/v1`
const headers = new Headers()
headers.append('x-upsert', 'true')
headers.append('Content-Type', 'application/json')
headers.append('Authorization', `Bearer ${getSecret('SUPABASE_ANON_KEY')}`)

2. Generate Pre-signed URL to GET a Storage Object (retrieve)

The getSupabaseObject function below retrieves an object’s pre-signed URL from Supabase Storage using the REST API. It generates a signed URL that allows you to access the file securely for a limited time.

src/storage/supabase.ts
// ...Existing Code...
export async function getSupabaseObject(objectPath: string) {
try {
const objectCall = await fetch(`${SUPABASE_BASE_STORAGE_API}/object/sign/${getSecret('SUPABASE_BUCKET_NAME')}/${objectPath}`, {
body,
headers,
method: 'POST',
})
const objectResp = await objectCall.json()
if (!objectCall.ok) throw new Error(JSON.stringify(objectResp))
return [SUPABASE_BASE_STORAGE_API, objectResp.signedURL].join('')
} catch (e: any) {
const tmp = e.message || e.toString()
console.log(tmp)
return
}
}

3. Generate Pre-signed URL to PUT a Storage Object (upload)

The uploadSupabaseObject function below is responsible for generating a pre-signed URL for uploading a file to Supabase Storage using the REST API. It creates a signed URL that allows you to upload files securely.

src/storage/supabase.ts
// ...Existing Code...
export async function uploadSupabaseObject(file: { name: string; type: string }) {
try {
const objectCall = await fetch(`${SUPABASE_BASE_STORAGE_API}/object/upload/sign/${getSecret('SUPABASE_BUCKET_NAME')}/${file.name}`, {
body,
headers,
method: 'POST',
})
const objectResp = await objectCall.json()
if (!objectCall.ok) throw new Error(JSON.stringify(objectResp))
return [SUPABASE_BASE_STORAGE_API, objectResp.url].join('')
} catch (e: any) {
const tmp = e.message || e.toString()
console.log(tmp)
return
}
}

4. Create a Server Endpoint (an API Route) in Astro

src/pages/api/storage.ts
import type { APIContext } from 'astro'
import { getSupabaseObject, uploadSupabaseObject } from '../../storage/supabase'
// Define an asynchronous function named GET that accepts a request object.
export async function GET({ request }: APIContext) {
// Extract the 'file' parameter from the request URL.
const url = new URL(request.url)
const file = url.searchParams.get('file')
// Check if the 'file' parameter exists in the URL.
if (file) {
try {
const filePublicURL = await getSupabaseObject(file)
// Return a response with the image's public URL and a 200 status code.
return new Response(filePublicURL)
} catch (error: any) {
// If an error occurs, log the error message and return a response with a 500 status code.
const message = error.message || error.toString()
console.log(message)
return new Response(message, { status: 500 })
}
}
// If the 'file' parameter is not found in the URL, return a response with a 400 status code.
return new Response('Invalid Request.', { status: 400 })
}
export async function POST({ request }: APIContext) {
// Extract the parameters from the request URL.
const url = new URL(request.url)
const name = url.searchParams.get('name')
const type = url.searchParams.get('type')
if (!name || !type) {
return new Response('Invalid Request. Missing name or type.', { status: 400 })
}
try {
// Generate an upload URL for the file
const uploadUrl = await uploadSupabaseObject({ name, type })
// Return a success response with the upload URL
return new Response(uploadUrl)
} catch (error: any) {
// If there was an error during the upload process, return a 500 response with the error message
const message = error.message || error.toString()
console.log(message)
return new Response(message, { status: 500 })
}
}

Deploy to Cloudflare Workers

To make your application deployable to Cloudflare Workers, create a file named .assetsignore in the public directory with the following content:

_routes.json
_worker.js

Next, you will need to use the Wrangler CLI to deploy your application to Cloudflare Workers. Run the following command to deploy:

Terminal window
npm run build && npx wrangler@latest deploy

Conclusion

In this blog post, you learned how to integrate Supabase Storage with Astro and Cloudflare Workers for file uploads and retrieval. By following the implementation steps, you can securely upload and retrieve files from Supabase Storage, ensuring that your web application has a robust and flexible storage solution.

If you would like to explore specific sections in more detail, expand on certain concepts, or cover additional related topics, please let me know, and I’ll be happy to assist!

Learn More How to Implement Basic Authorization in Astro
How to Implement Basic Authorization in Astro July 16, 2025
Cloudflare D1 Database Support Now Available in LaunchFast Starter Kits
Cloudflare D1 Database Support Now Available in LaunchFast Starter Kits July 3, 2025
Mailgun Email Support Now Available in LaunchFast Starter Kits
Mailgun Email Support Now Available in LaunchFast Starter Kits June 7, 2025