Decorative background gradient
Back to Blog
Next.js S3 UploadDigitalOcean Spaces NodejsPresigned URLs Upload

Next.js S3 Upload to DigitalOcean Spaces with Presigned URLs

Build a secure Next.js file upload pipeline to DigitalOcean Spaces using the AWS SDK S3 client, presigned URLs, and CDN delivery — no server relay.

Next.js S3 Upload to DigitalOcean Spaces with Presigned URLs

Uploading files from a Next.js app to object storage sounds simple until you consider bandwidth cost, server load, and security. Relaying every upload through your API routes means your server pays for both the inbound and outbound bandwidth of every file, twice. Presigned URLs solve this by letting the browser upload directly to storage while your server only issues a short-lived, scoped permission slip.

This guide builds that pipeline against DigitalOcean Spaces, which is S3-compatible.

Step 1: Configure the S3 Client for DigitalOcean Spaces

bash
ts

The only difference from configuring AWS S3 directly is the endpoint — Spaces implements the same S3 API, so the rest of the SDK behaves identically.

Step 2: Generate a Presigned Upload URL on the Server

Validate the request before signing anything — this is your only checkpoint before the file leaves the client.

ts

Two things matter here: the URL expires in 120 seconds (short enough to limit misuse if it leaks, long enough for a real upload to complete), and validation happens before signing — never after.

Step 3: Upload Directly from the Browser

tsx
tsx

The file bytes go straight from the browser to Spaces via the PUT request — your Next.js server never sees the file body, only the small JSON signing request.

Step 4: Serve Files Through the CDN Endpoint

DigitalOcean Spaces has two endpoints for the same bucket: the raw origin (nyc3.digitaloceanspaces.com) and a CDN-fronted one (nyc3.cdn.digitaloceanspaces.com). Always serve public files through the CDN endpoint — it caches content at edge locations globally, which is meaningfully faster than hitting the origin region directly on every request.

ts

If you're already using next/image, add the CDN hostname to remotePatterns so Next.js can optimize these images too:

ts

Step 5: Handle Private Files with Presigned Download URLs

For files that shouldn't be publicly readable, skip ACL: "public-read" on upload and generate a presigned GET URL on demand instead:

ts

This keeps the object private in storage while still letting an authorized user access it for a limited window, without your server proxying the file bytes.

Key Takeaways

DigitalOcean Spaces' S3 compatibility means the standard AWS SDK works unmodified — only the endpoint config changes. Route uploads through short-lived presigned URLs so files go directly from browser to storage instead of through your server, validate type and size before signing (not after), and always serve public assets through the CDN endpoint for fast global delivery.

Frequently Asked Questions

Do I need a different SDK for DigitalOcean Spaces than for AWS S3?

No. DigitalOcean Spaces implements the S3 API, so the same @aws-sdk/client-s3 package works — you just point the endpoint config at your Spaces region (e.g. https://nyc3.digitaloceanspaces.com) instead of an AWS endpoint.

Why use a presigned URL instead of uploading through my API route?

Routing file bytes through your Next.js server doubles the bandwidth cost and ties up server resources for the duration of the upload. A presigned URL lets the browser upload directly to object storage — your server only ever handles a small signing request, not the file itself.

How long should a presigned upload URL be valid for?

Keep it short — a few minutes is usually enough for a real upload to start and finish. Anyone with the URL can upload to that specific key until it expires, so a long expiry unnecessarily widens the window for misuse if the URL leaks.

How do I validate file type and size before generating a presigned URL?

Check the requested content type and declared size in the Server Action or Route Handler before calling PutObjectCommand, and reject anything outside your allowed types/limits there. You can also enforce a max size via a Content-Length range condition when generating the presigned POST policy instead of a simple PUT URL.

Working on something similar? Take a look at my services and case studies, or book a free call to talk about your idea.

Related Articles

Silent Tool Failures in AI Agents — Why Your Agent Lies About Success
Ai Agent ReliabilityTool Call FailuresLlm Error Handling+1 more

Silent Tool Failures in AI Agents — Why Your Agent Lies About Success

Most AI agent bugs never throw an error — the tool call "succeeds" with empty, truncated, or stale data and the agent confidently reports the task as done. Here's how to catch failures that don't look like failures.

September 22, 2026Read more →
AI Coding Agents Compared in 2026 — Claude Code vs Cursor vs GitHub Copilot
Claude Code Vs CursorAI Coding AgentsGithub Copilot

AI Coding Agents Compared in 2026 — Claude Code vs Cursor vs GitHub Copilot

Compare Claude Code, Cursor, and GitHub Copilot for real development work — agentic workflows, codebase understanding, pricing, and which fits your team.

September 18, 2026Read more →
Best Authentication Libraries for Next.js in 2026 — Clerk vs Auth.js vs Better Auth
Nextjs AuthenticationClerk Vs NextauthBetter Auth

Best Authentication Libraries for Next.js in 2026 — Clerk vs Auth.js vs Better Auth

Compare Clerk, Auth.js (NextAuth), and Better Auth for Next.js — setup effort, hosted UI, pricing, and which fits your app.

September 18, 2026Read more →
Best Error Monitoring & Analytics Tools for Next.js in 2026 — Sentry vs PostHog vs Vercel Analytics
Nextjs Error MonitoringSentry Vs PosthogWeb Analytics

Best Error Monitoring & Analytics Tools for Next.js in 2026 — Sentry vs PostHog vs Vercel Analytics

Compare Sentry, PostHog, and Vercel Analytics for a Next.js app — error tracking, product analytics, session replay, and pricing.

September 18, 2026Read more →
How to Add AI Features to an Existing Web App (Without a Rebuild)
Add AI To Existing AppAI SaaS FeaturesOpenAI API Integration

How to Add AI Features to an Existing Web App (Without a Rebuild)

A practical guide to adding real AI features, like matching, extraction, and automation, to a web app or SaaS product you already have, without rebuilding it from scratch.

September 16, 2026Read more →
AI Agent Observability and Tracing in Production
Ai Agent ObservabilityLlm TracingAgent Monitoring+2 more

AI Agent Observability and Tracing in Production

Why logging inputs and outputs isn't enough for AI agents in production — and how trace-level observability catches failures evals and logs miss.

September 14, 2026Read more →

Trending Topics