Skip to content
Go back

Using AWS S3 Signed URLs for Secure File Uploads

Using AWS S3 Signed URLs for Secure File Uploads

Introduction

AWS S3 presigned URLs allow clients to upload files directly to S3 without exposing AWS credentials or granting broad permissions.

Prerequisites

Step 1: Configure AWS SDK

npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

Create lib/s3.ts:

import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

const s3 = new S3Client({ region: process.env.AWS_REGION });
const bucketName = process.env.S3_BUCKET_NAME!;

export async function generateUploadURL(key: string): Promise<string> {
  const command = new PutObjectCommand({
    Bucket: bucketName,
    Key: key,
    ContentType: 'application/octet-stream',
  });

  const signedUrl = await getSignedUrl(s3, command, { expiresIn: 3600 }); // 1 hour
  return signedUrl;
}

Step 2: Create API Route

Create pages/api/upload-url.ts (or app/api/upload-url/route.ts):

import { NextRequest, NextResponse } from 'next/server';
import { generateUploadURL } from '@/lib/s3';

export async function GET(req: NextRequest) {
  const { searchParams } = new URL(req.url);
  const filename = searchParams.get('filename');

  if (!filename) {
    return NextResponse.json({ error: 'Filename is required' }, { status: 400 });
  }

  const key = `uploads/${Date.now()}-${filename}`;
  const url = await generateUploadURL(key);

  return NextResponse.json({ url, key });
}

Step 3: Client-Side Upload

async function uploadFile(file) {
  // Get signed URL
  const res = await fetch(`/api/upload-url?filename=${file.name}`);
  const { url, key } = await res.json();

  // Upload file
  await fetch(url, {
    method: 'PUT',
    headers: { 'Content-Type': file.type },
    body: file,
  });

  return key;
}

Step 4: Configure S3 CORS

<CORSConfiguration>
  <CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
  </CORSRule>
</CORSConfiguration>

Summary

S3 presigned URLs provide a secure method for direct client uploads, minimizing backend load and eliminating the need to expose AWS credentials.


Share this post on:

Previous Post
Using PostgreSQL JSONB for Flexible Schemas
Next Post
Implementing Row-Level Security (RLS) in PostgreSQL