# What's the best way to handle uploads to IPFS from a web app?

**URL:** <https://discuss.ipfs.tech/t/whats-the-best-way-to-handle-uploads-to-ipfs-from-a-web-app/18029>\
**Category:** Help\
**Tags:** files, upload\
**Created:** [May 23, 2024, 6:39pm UTC](https://discuss.ipfs.tech/t/whats-the-best-way-to-handle-uploads-to-ipfs-from-a-web-app/18029 "2024-05-23T18:39:35Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![eee](https://avatars.discourse-cdn.com/v4/letter/e/87869e/32.png) [@eee](https://discuss.ipfs.tech/u/eee)\
**Post date:** [May 23, 2024, 6:39pm UTC](https://discuss.ipfs.tech/t/whats-the-best-way-to-handle-uploads-to-ipfs-from-a-web-app/18029/1 "2024-05-23T18:39:35Z")

</div>

Helia does not actually commit to IPFS so it’s not an option. It (by default) stores in memory:

> <https://github.com/ipfs/helia/blob/011fa92c05bf42fb20666b1df4c86fb47889a07e/packages/helia/src/index.ts#L91-L93>

The datastore can be configured with ones from `js-stores`, however none of them actually store on the IPFS network: [GitHub - ipfs/js-stores: TypeScript interfaces used by IPFS internals](https://github.com/ipfs/js-stores).

Helia 201 - storage also has no mentions of actually storing anything on the IPFS network: [helia-examples/examples/helia-101/201-storage.js at main · ipfs-examples/helia-examples · GitHub](https://github.com/ipfs-examples/helia-examples/blob/main/examples/helia-101/201-storage.js)

The question arises: how to actually upload the content? Is it exposing (only /add) a custom Kubo node that the app pins to? If so, then having one server for pins makes us centralized and can be a bottleneck. How should that be dealt with? As far as I can tell, the files are distributed to the other nodes on the network when they’re queried (first request for CID uploaded to own Kubo instance takes an eternity to respond).

---

<div class="post-metadata">

**Author:** ![danieln](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ipfs.tech/danieln/32/6408_2.png) [@danieln](https://discuss.ipfs.tech/u/danieln)\
**Post date:** [May 24, 2024, 10:12am UTC](https://discuss.ipfs.tech/t/whats-the-best-way-to-handle-uploads-to-ipfs-from-a-web-app/18029/2 "2024-05-24T10:12:19Z")

</div>

> [@eee](#):
>
> how to actually upload the content?

A couple of options:

- Run Kubo and expose the [Kubo RPC API | IPFS Docs](https://docs.ipfs.tech/reference/kubo/rpc/#api-v0-add) endpoint as you suggested. You probably want to limit origins that can upload to prevent attacks.
- Use a pinning service [GitHub - ipfs/awesome-ipfs: Community list of awesome projects, apps, tools, pinning services and more related to IPFS.](https://github.com/ipfs/awesome-ipfs?tab=readme-ov-file#pinning-services)
- Run a long-running node.js server with Helia and a custom upload endpoint that adds to Helia.

Generally speaking, once other IPFS peers in the network can retrieve a CID, the data can get replicated by multiple peers.

---

<div class="post-metadata">

**Author:** ![eee](https://avatars.discourse-cdn.com/v4/letter/e/87869e/32.png) [@eee](https://discuss.ipfs.tech/u/eee)\
**Post date:** [May 24, 2024, 4:25pm UTC](https://discuss.ipfs.tech/t/whats-the-best-way-to-handle-uploads-to-ipfs-from-a-web-app/18029/3 "2024-05-24T16:25:12Z")

</div>

> [@danieln](#):
>
> Run Kubo and expose the [Kubo RPC API | IPFS Docs](https://docs.ipfs.tech/reference/kubo/rpc/#api-v0-add) endpoint as you suggested. You probably want to limit origins that can upload to prevent attacks.

That, as I mentioned, provides a single choke point for incoming traffic. It also means pins are centralized unless someone queries the CID from another node (which I don’t expect any of our users doing). Is there a solution for this?

There’s another question regarding this: if we have staging/production environments for our app, does it make sense to have two Kubo instances for each environment?

> [@danieln](#):
>
> Run a long-running node.js server with Helia and a custom upload endpoint that adds to Helia.

I’m not quite sure I follow you here. Do you mean using Helia on a server as our own node (similar to Kubo)? This can also have the same problem of centralization: there’s one (our own) server that holds the data and pinning.

At this point, how is IPFS any more decentralized than AWS S3 or GCP Cloud Stoage? There’s one server that handles the uploads and the only practical way of retrieving data is using the gateway of that server. We don’t know when (or even if) some other node might have the file. As far as I can see (please correct me if I’m wrong), if it’s not queried, no other node will have the file

> [@danieln](#):
>
> Use a pinning service [GitHub - ipfs/awesome-ipfs: Community list of awesome projects, apps, tools, pinning services and more related to IPFS.](https://github.com/ipfs/awesome-ipfs?tab=readme-ov-file#pinning-services)

Thanks for that suggestion. I’ll explore those. Do you know how propagation happens with these services? Is it the same as running own Kubo node (the content is available on one node, unless someone asks another node for it)?

---

<div class="post-metadata">

**Author:** ![danieln](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ipfs.tech/danieln/32/6408_2.png) [@danieln](https://discuss.ipfs.tech/u/danieln)\
**Post date:** [May 28, 2024, 9:42am UTC](https://discuss.ipfs.tech/t/whats-the-best-way-to-handle-uploads-to-ipfs-from-a-web-app/18029/4 "2024-05-28T09:42:00Z")

</div>

> [@eee](#):
>
> That, as I mentioned, provides a single choke point for incoming traffic. It also means pins are centralized unless someone queries the CID from another node (which I don’t expect any of our users doing). Is there a solution for this?

Indeed. The meaning of decentralisation is context dependent and can be best thought of as a spectrum.

For example, would you consider the pins sufficiently decentralised if you pinned them to 3 IPFS nodes that you control? If so, you may want to look into IPFS Cluster which helps you coordinate pin sets across multiple Kubo nodes.

If you want others to pin the CIDs, you could rely on pinning services to pin the CIDs once uploaded. However, for the pinning services to keep pinning they rely on you paying for their services. Would that make it sufficiently decentralised?

Then of course, there are crypto native approaches to this, that include [Saturn](https://saturn.tech/), but I don’t have enough experience with those to say how well they work.

> [@eee](#):
>
> I’m not quite sure I follow you here. Do you mean using Helia on a server as our own node (similar to Kubo)? This can also have the same problem of centralization: there’s one (our own) server that holds the data and pinning.

Exactly. That was just an alternative suggestion to Kubo.

> [@eee](#):
>
> At this point, how is IPFS any more decentralized than AWS S3 or GCP Cloud Stoage? There’s one server that handles the uploads

IPFS enshrines the ability for other peers to replicate CIDs and become providers for those CIDs. However, you are right about the choke point problem of your Kubo node.

> [@eee](#):
>
> and the only practical way of retrieving data is using the gateway of that server.

That’s not entirely correct. Part of the magic of IPFS is the DHT, which allows other IPFS nodes to find all the nodes that provide a given CID and retrieve it over Bitswap. In other words, data can be fetched from your Kubo node using either the gateway or bitswap.

> [@eee](#):
>
> As far as I can see (please correct me if I’m wrong), if it’s not queried, no other node will have the file

That’s correct. If you add a file to your Kubo node, no other peer in the network will “magically” replicate it.

> [@eee](#):
>
> There’s another question regarding this: if we have staging/production environments for our app, does it make sense to have two Kubo instances for each environment?

Depends on your use-case and amount of data, but generally it’s a good idea to keep environments separate.  
If you decide to share a node across envs, you probably keep track of CIDs in your app so that you can routinely and safely unpin CIDs associated with your staging environment (assuming that unpinning won’t break your staging environment).

---

<div class="post-metadata">

**Author:** ![veermetri05](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ipfs.tech/veermetri05/32/7854_2.png) [@veermetri05](https://discuss.ipfs.tech/u/veermetri05)\
**Post date:** [June 5, 2024, 3:45pm UTC](https://discuss.ipfs.tech/t/whats-the-best-way-to-handle-uploads-to-ipfs-from-a-web-app/18029/6 "2024-06-05T15:45:44Z")

</div>

> [@danieln](#):
>
> Use a pinning service

A better thing to do is use a decetralized Pinning service like [Crust Network](https://crust.network/). Perks of using Crust Network is pinning is cheap (as of June 2024, [price](https://crust.subscan.io/tools/storage_calculator) is $0.00014/GB/month) and more decentralized than a centralized pinning service, a file pinned on Crust Network is [replicated](https://crust.subscan.io/storage) on average of ~33 nodes (as of June 2024). (more data at [Crust Subscan](https://crust.subscan.io/))

Crust is very decentralized in nature, and IPFS is at it’s core. To pin a file on Crust Network, you first need to make your file available on the IPFS network. Any publicly accessible IPFS hash can be pinned on Crust Network.

So, to answer your initial question.  
You need a centralized solution (Host your own Kubo node and expose it’s API or use any services which would enable you to receive file from the cilent and make it available on the IPFS network), but this centralized solution is just a temporary storage for your files.  
Once you receive the file from your web app to the your own IPFS Node and it is accessible publicly, then you can place a storage order on Crust Network ([docs](https://docs.crust.network/)).

And thus you will have a more decentralized and long term sustainable IPFS storage.

---

<div class="post-metadata">

**Author:** ![veermetri05](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ipfs.tech/veermetri05/32/7854_2.png) [@veermetri05](https://discuss.ipfs.tech/u/veermetri05)\
**Post date:** [June 5, 2024, 3:50pm UTC](https://discuss.ipfs.tech/t/whats-the-best-way-to-handle-uploads-to-ipfs-from-a-web-app/18029/7 "2024-06-05T15:50:48Z")

</div>

> [@danieln](#):
>
> Then of course, there are crypto native approaches to this, that include [Saturn](https://saturn.tech/), but I don’t have enough experience with those to say how well they work.

Saturn is only a retrieval network. Saturn provides services for retrieving a file that is already available on IPFS network or Filecoin. It could be thought of as a decentralized gateway or more preciously a decentralized CDN that works on top of IPFS and Filecoin.

> [@danieln](#):
>
> That’s correct. If you add a file to your Kubo node, no other peer in the network will “magically” replicate it.

To make that magic happen you just have say 2 magic words, “Crust Network” 🪄✨😅

---

<div class="post-metadata">

**Author:** ![NachoColl](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ipfs.tech/nachocoll/32/9765_2.png) [@NachoColl](https://discuss.ipfs.tech/u/NachoColl)\
**Post date:** [April 7, 2026, 7:50pm UTC](https://discuss.ipfs.tech/t/whats-the-best-way-to-handle-uploads-to-ipfs-from-a-web-app/18029/8 "2026-04-07T19:50:58Z")

</div>

For the “let users upload from the frontend without exposing your API key” pattern, [IPFS.NINJA](https://ipfs.ninja) has this built in: **signed upload tokens**.

You generate a short-lived, scoped token server-side and send it to the client — they upload directly to IPFS without ever seeing your master API key. The token supports expiry time, max file size, and file type restrictions.

This eliminates the backend proxy pattern entirely. Standard REST API, straightforward integration with any frontend stack. Free tier available to try it out.
