#CDN integration
1 messages · Page 1 of 1 (latest)
Hi @viral plover. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
Convex doesn’t yet have first‑class CDN configuration for file storage in the docs, but there are two supported patterns you can build on.
1. Put a CDN in front of HTTP actions
You can serve files via HTTP actions and then point a CDN (e.g. Cloudflare, Fastly) at that HTTP endpoint:
- Create an HTTP action that serves a file from storage:
// convex/http.ts
import { httpRouter } from "convex/server";
import { httpAction } from "./_generated/server";
import { Id } from "./_generated/dataModel";
const http = httpRouter();
http.route({
path: "/getImage",
method: "GET",
handler: httpAction(async (ctx, request) => {
const { searchParams } = new URL(request.url);
const storageId = searchParams.get("storageId")! as Id<"_storage">;
const blob = await ctx.storage.get(storageId);
if (blob === null) {
return new Response("Image not found", { status: 404 });
}
return new Response(blob);
}),
});
export default http;
-
Expose that action on a custom domain (e.g.
files.example.com) using Convex’s custom domain support. [Custom domains] -
Configure your CDN to use that custom domain as the origin and cache responses from
/getImage?....
This gives you:
- CDN caching and edge delivery
- Full control over access (auth, headers) in the HTTP action
- A stable origin URL for the CDN
Note: HTTP action responses are currently limited to 20 MB, so this pattern is only suitable for smaller files. Larger files must be served via storage.getUrl URLs instead. [Serve files]
2. Use an external object store + CDN via a Convex Component
If you want a more “native” CDN + object storage setup, you can offload files to a provider that has built‑in CDN / image delivery and use a Convex Component to integrate:
- Cloudflare R2 Component: “Store and serve files in Cloudflare's R2 storage.” [Integrations]
With R2 you can:
- Store files from Convex actions into R2.
- Serve them via Cloudflare’s edge network and image transforms.
- Optionally still keep metadata / permissions in Convex.
This is currently the most direct way to get a full CDN + image‑processing stack in front of your files while staying within documented patterns. [Are image transforms supported?]
If you share which CDN you’re using (Cloudflare, Fastly, etc.) and whether your files are >20 MB, I can narrow down which of these two approaches fits better.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@viral plover Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Let me check the knowledge base for you again. One moment! :books:
Convex’s docs and support threads don’t describe a CloudFront‑specific integration, so the guidance below is based only on what is documented and an S3‑style pattern that Convex staff have confirmed works in the Convex runtime.
1. Generate signed URLs inside Convex (CloudFront/S3‑style)
A Convex support reply confirms that S3‑style presigned URLs can be generated directly inside Convex functions using @aws-sdk/s3-request-presigner, and that this works even in queries (no Node‑only APIs required). [Discord S3 thread]
For CloudFront, you’d follow the same pattern conceptually:
- Store only the object key / identifier in Convex.
- In a Convex query or mutation, generate a signed URL for that key using AWS’ signing library (CloudFront or S3, depending on how you front your content).
- Return that URL to the client along with the rest of your data.
The R2 component shows the pattern of “generate a URL per object in a query and return it with your data”; you’d do the same but with CloudFront signing instead of r2.getUrl:
// convex/listMessages.ts
import { query } from "./_generated/server";
// import your CloudFront/S3 signing helper here
export const list = query({
args: {},
handler: async (ctx) => {
const messages = await ctx.db.query("messages").collect();
return Promise.all(
messages.map(async (message) => ({
...message,
// Pseudocode: replace with your CloudFront/S3 signing call
avatarUrl: await generateSignedCloudfrontUrl(message.avatarKey),
})),
);
},
});
This is analogous to how the R2 component generates per‑object URLs in a query. [R2 serving files]
2. Use Convex storage + CloudFront as a pure CDN cache (not documented)
Convex’s own file URLs (ctx.storage.getUrl(id)) are generated by Convex and currently don’t expose any CDN configuration knobs in the docs. [Storage getUrl; Serving files]
The docs do say:
- You can serve files via:
- Direct file URLs from
ctx.storage.getUrl(id)(no size limit mentioned there). [Serving file URLs] - HTTP actions that stream blobs from storage, but those are limited to 20 MB per response. [Serving from HTTP actions; HTTP action limits]
- Direct file URLs from
They do not describe putting CloudFront directly in front of those URLs, nor how cache headers are set, so I can’t give a documented, CloudFront‑specific recipe for “CDN in front of Convex storage URLs.”
3. When to use which pattern
From the docs and support threads:
- For full control over access at serve time and small files (≤ 20 MB), use an HTTP action that reads from storage and returns a
Response, then you could put a CDN in front of that HTTP endpoint. [Serving from HTTP actions; HTTP action limits] - For larger files or when you want signed URLs that don’t go through Convex on every request, use the S3‑style presigned URL pattern from inside Convex (queries/mutations) and let CloudFront/S3 handle the actual file delivery. [Discord S3 thread]
Because there’s no CloudFront‑specific example in the knowledge sources, I can’t provide exact AWS config steps, but the documented Convex side is:
- Generate URLs (S3/CloudFront‑style) inside queries/mutations/actions.
- Or serve blobs via HTTP actions (≤ 20 MB) and optionally front those with a CDN.