Skip to main content
RapidDev - Software Development Agency
firebase-tutorial

How to Get the Public URL of a Firebase Storage File

To get the public URL of a Firebase Storage file, use getDownloadURL() from the modular SDK with a storage reference. This returns a long-lived HTTPS URL with an access token, making the file accessible to anyone with the link. For truly public URLs without tokens, make the bucket public using Google Cloud IAM. For temporary access, generate signed URLs with the Firebase Admin SDK. Always call getDownloadURL after the upload completes.

What you'll learn

  • How to get a download URL with getDownloadURL from the client SDK
  • How download URL tokens work and when they expire
  • How to make files truly public using Google Cloud IAM settings
  • How to generate time-limited signed URLs with the Admin SDK
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate8 min read10-15 minFirebase JS SDK v9+, Cloud Storage for Firebase (all plans)March 2026RapidDev Engineering Team
TL;DR

To get the public URL of a Firebase Storage file, use getDownloadURL() from the modular SDK with a storage reference. This returns a long-lived HTTPS URL with an access token, making the file accessible to anyone with the link. For truly public URLs without tokens, make the bucket public using Google Cloud IAM. For temporary access, generate signed URLs with the Firebase Admin SDK. Always call getDownloadURL after the upload completes.

Getting the Public URL of a Firebase Storage File

Firebase Storage provides multiple ways to generate URLs for accessing files. The getDownloadURL() method returns a token-based URL that bypasses security rules. For permanent public access, you can set IAM permissions on the bucket. For temporary sharing, signed URLs with expiration times offer the most control. This tutorial covers all three approaches and when to use each.

Prerequisites

  • A Firebase project with Cloud Storage enabled
  • Firebase JS SDK v9+ installed
  • At least one file uploaded to Firebase Storage
  • Admin SDK configured for signed URL generation (optional)

Step-by-step guide

1

Get a download URL with getDownloadURL

The most common method is getDownloadURL(), which returns an HTTPS URL with an embedded access token. This URL bypasses storage security rules — anyone with the URL can access the file, even without authentication. The token is generated when the file is uploaded or when you explicitly request a new one. Import ref and getDownloadURL from firebase/storage.

typescript
1import { getStorage, ref, getDownloadURL } from 'firebase/storage';
2
3const storage = getStorage();
4
5async function getFileUrl(filePath: string): Promise<string> {
6 const fileRef = ref(storage, filePath);
7 const url = await getDownloadURL(fileRef);
8 console.log('Download URL:', url);
9 return url;
10}
11
12// Example: get URL for an uploaded image
13const url = await getFileUrl('users/abc123/avatar.jpg');
14// Returns: https://firebasestorage.googleapis.com/v0/b/BUCKET/o/users%2Fabc123%2Favatar.jpg?alt=media&token=TOKEN

Expected result: A long-lived HTTPS URL is returned that can be used in img tags, fetch calls, or shared directly.

2

Get the download URL immediately after uploading

When uploading a file, chain getDownloadURL after the upload completes to get the URL for the newly uploaded file. This is the standard pattern for displaying uploaded images or providing download links. Use the snapshot reference from uploadBytes or listen for the complete event on uploadBytesResumable.

typescript
1import { getStorage, ref, uploadBytes, getDownloadURL } from 'firebase/storage';
2
3const storage = getStorage();
4
5async function uploadAndGetUrl(file: File, path: string): Promise<string> {
6 const fileRef = ref(storage, path);
7
8 // Upload the file
9 await uploadBytes(fileRef, file);
10
11 // Get the download URL
12 const url = await getDownloadURL(fileRef);
13 return url;
14}
15
16// With resumable upload and progress
17import { uploadBytesResumable } from 'firebase/storage';
18
19function uploadWithProgress(file: File, path: string): Promise<string> {
20 return new Promise((resolve, reject) => {
21 const fileRef = ref(storage, path);
22 const task = uploadBytesResumable(fileRef, file);
23
24 task.on('state_changed',
25 (snapshot) => {
26 const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
27 console.log(`Upload: ${progress.toFixed(0)}%`);
28 },
29 reject,
30 async () => {
31 const url = await getDownloadURL(task.snapshot.ref);
32 resolve(url);
33 }
34 );
35 });
36}

Expected result: The download URL is available immediately after the upload completes and can be stored in Firestore or displayed in the UI.

3

Store the download URL in Firestore for later use

A common pattern is to store the download URL in a Firestore document right after uploading. This way, you do not need to call getDownloadURL again when displaying the file — just read the URL from Firestore. This is more efficient because Firestore reads are cheaper than Storage API calls at scale.

typescript
1import { getStorage, ref, uploadBytes, getDownloadURL } from 'firebase/storage';
2import { getFirestore, doc, updateDoc } from 'firebase/firestore';
3
4const storage = getStorage();
5const db = getFirestore();
6
7async function uploadUserAvatar(
8 userId: string,
9 file: File
10): Promise<string> {
11 // Upload to a user-specific path
12 const filePath = `users/${userId}/avatar.jpg`;
13 const fileRef = ref(storage, filePath);
14 await uploadBytes(fileRef, file);
15
16 // Get the download URL
17 const url = await getDownloadURL(fileRef);
18
19 // Store the URL in the user's Firestore document
20 await updateDoc(doc(db, 'users', userId), {
21 avatarUrl: url,
22 avatarPath: filePath
23 });
24
25 return url;
26}

Expected result: The download URL is saved in Firestore alongside the storage path, enabling efficient file display without repeated getDownloadURL calls.

4

Make files truly public with Google Cloud IAM

Download URLs with tokens still require the token in the URL. For truly public files (like public website assets), you can make the bucket or a specific path publicly readable through Google Cloud IAM. Go to the Google Cloud Console > Cloud Storage > Your bucket > Permissions. Add the allUsers principal with the Storage Object Viewer role. This makes every file in the bucket readable via a simple public URL pattern.

typescript
1// After setting allUsers as Storage Object Viewer on your bucket,
2// files are accessible via this public URL pattern:
3// https://storage.googleapis.com/YOUR-BUCKET/path/to/file.jpg
4
5// No token needed — the URL is permanent and public
6function getPublicUrl(bucket: string, filePath: string): string {
7 const encodedPath = encodeURIComponent(filePath)
8 .replace(/%2F/g, '/');
9 return `https://storage.googleapis.com/${bucket}/${encodedPath}`;
10}
11
12// Example:
13const publicUrl = getPublicUrl(
14 'myproject.appspot.com',
15 'public/hero-image.jpg'
16);

Expected result: Files are accessible via a clean public URL without any access token, suitable for CDN caching and public sharing.

5

Generate time-limited signed URLs with the Admin SDK

For temporary file sharing, use the Firebase Admin SDK (server-side) to generate signed URLs that expire after a specified duration. Signed URLs are ideal for sharing files that should only be accessible for a limited time, like download links in emails or temporary previews.

typescript
1// Server-side: Node.js with Firebase Admin SDK
2import { initializeApp, cert } from 'firebase-admin/app';
3import { getStorage } from 'firebase-admin/storage';
4
5const app = initializeApp({
6 credential: cert('./service-account.json'),
7 storageBucket: 'YOUR-PROJECT.appspot.com'
8});
9
10const bucket = getStorage(app).bucket();
11
12async function getSignedUrl(
13 filePath: string,
14 expiresInMinutes: number = 60
15): Promise<string> {
16 const file = bucket.file(filePath);
17 const [url] = await file.getSignedUrl({
18 action: 'read',
19 expires: Date.now() + expiresInMinutes * 60 * 1000
20 });
21 return url;
22}
23
24// URL expires in 24 hours
25const tempUrl = await getSignedUrl('users/abc123/report.pdf', 1440);

Expected result: A signed URL is returned that provides read access to the file for the specified duration, after which it returns a 403 error.

Complete working example

storage-urls.ts
1import { initializeApp } from 'firebase/app';
2import {
3 getStorage,
4 ref,
5 uploadBytes,
6 getDownloadURL
7} from 'firebase/storage';
8import { getFirestore, doc, updateDoc } from 'firebase/firestore';
9
10const app = initializeApp({
11 // Your Firebase config
12});
13const storage = getStorage(app);
14const db = getFirestore(app);
15
16// Get download URL for an existing file
17export async function getFileUrl(filePath: string): Promise<string> {
18 return getDownloadURL(ref(storage, filePath));
19}
20
21// Upload file and get URL in one step
22export async function uploadAndGetUrl(
23 file: File,
24 path: string
25): Promise<string> {
26 const fileRef = ref(storage, path);
27 await uploadBytes(fileRef, file);
28 return getDownloadURL(fileRef);
29}
30
31// Upload avatar and save URL to Firestore
32export async function uploadUserAvatar(
33 userId: string,
34 file: File
35): Promise<string> {
36 const path = `users/${userId}/avatar.jpg`;
37 const fileRef = ref(storage, path);
38 await uploadBytes(fileRef, file);
39
40 const url = await getDownloadURL(fileRef);
41 await updateDoc(doc(db, 'users', userId), {
42 avatarUrl: url,
43 avatarPath: path
44 });
45
46 return url;
47}
48
49// Construct a public URL (bucket must have allUsers IAM)
50export function getPublicUrl(
51 bucket: string,
52 filePath: string
53): string {
54 const encoded = encodeURIComponent(filePath).replace(/%2F/g, '/');
55 return `https://storage.googleapis.com/${bucket}/${encoded}`;
56}

Common mistakes when getting the Public URL of a Firebase Storage File

Why it's a problem: Calling getDownloadURL before the upload finishes

How to avoid: Always await the uploadBytes call before calling getDownloadURL. For resumable uploads, get the URL in the complete callback, not before.

Why it's a problem: Assuming download URL tokens expire like signed URLs

How to avoid: Download URL tokens do not expire automatically. They remain valid until the file is deleted or re-uploaded (which generates a new token). If you need expiring URLs, use signed URLs from the Admin SDK.

Why it's a problem: Making the entire storage bucket public when only specific files need public access

How to avoid: Use a separate bucket or folder for public files. Set allUsers IAM only on that specific path, or use download URLs with tokens for authenticated access.

Best practices

  • Store download URLs in Firestore after upload to avoid repeated getDownloadURL calls
  • Store both the download URL and the storage path — URL for display, path for management
  • Use a separate public bucket for truly public assets to avoid accidentally exposing user files
  • Generate signed URLs server-side for temporary file sharing with expiration
  • Use getDownloadURL for authenticated app features and public IAM URLs for public website assets
  • Validate file uploads with security rules before generating download URLs
  • Cache download URLs on the client to reduce network requests

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

Show me how to get the public download URL of a Firebase Storage file using the modular SDK v9+. Include examples for getting the URL of an existing file, uploading and getting the URL in one step, storing the URL in Firestore, making files truly public with Google Cloud IAM, and generating time-limited signed URLs with the Admin SDK.

Firebase Prompt

Create Firebase Storage URL utilities: getDownloadURL for existing files, upload-then-get-url workflow, save URL to Firestore alongside storage path, public URL construction for IAM-public buckets, and a server-side signed URL generator using the Admin SDK. Use TypeScript and modular SDK v9+.

Frequently asked questions

Do Firebase Storage download URLs expire?

No. Download URLs generated by getDownloadURL contain a token that does not expire. The URL remains valid until the file is deleted, re-uploaded, or you manually revoke the token in the Firebase Console.

Can anyone access a file using the download URL?

Yes. Download URLs with tokens bypass security rules. Anyone with the URL can download the file, even without a Firebase account. Treat download URLs as semi-public links.

How do I revoke access to a download URL?

You can revoke the token by deleting and re-uploading the file, which generates a new token and invalidates the old URL. There is also a 'Revoke' option in the Firebase Console Storage browser for each file.

What is the difference between getDownloadURL and a signed URL?

getDownloadURL returns a permanent URL with an access token. Signed URLs (generated server-side with the Admin SDK) have a configurable expiration time, after which they stop working.

Can I customize the download URL format?

No. Firebase download URLs follow a fixed format with the token parameter. For clean URLs, use a CDN in front of your storage bucket or configure a custom domain for Cloud Storage.

Can RapidDev help set up a file management system with Firebase Storage?

Yes, RapidDev can build a complete file management system including uploads, URL generation, Firestore metadata storage, access control, and CDN integration for your Firebase project.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.