<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Burhan Rampura</title>
    <description>The latest articles on DEV Community by Burhan Rampura (@burhanuddin_rampura).</description>
    <link>https://dev.to/burhanuddin_rampura</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2906234%2Fa2a6b765-b377-4a03-88a0-d7228e95753b.jpg</url>
      <title>DEV Community: Burhan Rampura</title>
      <link>https://dev.to/burhanuddin_rampura</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/burhanuddin_rampura"/>
    <language>en</language>
    <item>
      <title>Uploading Images to S3 Using Pre-Signed URLs and Accessing via CloudFront</title>
      <dc:creator>Burhan Rampura</dc:creator>
      <pubDate>Sun, 02 Mar 2025 12:02:26 +0000</pubDate>
      <link>https://dev.to/burhanuddin_rampura/uploading-images-to-s3-using-pre-signed-urls-and-accessing-via-cloudfront-2chi</link>
      <guid>https://dev.to/burhanuddin_rampura/uploading-images-to-s3-using-pre-signed-urls-and-accessing-via-cloudfront-2chi</guid>
      <description>&lt;p&gt;This guide will walk you through uploading images to an AWS S3 bucket using pre-signed URLs and retrieving them securely via CloudFront signed URLs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feep5ime9rtcijs7tu2c0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feep5ime9rtcijs7tu2c0.png" alt="Image description" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Create an S3 Bucket&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to the AWS Console.&lt;/li&gt;
&lt;li&gt;Search for S3 and navigate to the S3 Dashboard.&lt;/li&gt;
&lt;li&gt;Click on Create Bucket.&lt;/li&gt;
&lt;li&gt;An S3 bucket name must be globally unique&lt;/li&gt;
&lt;li&gt;Block all public access (important for security).&lt;/li&gt;
&lt;li&gt;Keep all other settings as default and click Create Bucket.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Generate Public and Private Keys&lt;/strong&gt;&lt;br&gt;
Since CloudFront requires a key pair for secure access, generate a public-private key pair using OpenSSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl genrsa -out private_key.pem 2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Upload the Public Key to CloudFront&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to CloudFront in the AWS Console.&lt;/li&gt;
&lt;li&gt;In the sidebar, navigate to Key Management &amp;gt; Public Keys.&lt;/li&gt;
&lt;li&gt;Click Add Public Key and upload the public key generated earlier.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsb6zdow97lgb0itxoym7.png" alt="Image description" width="800" height="376"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Create a Key Group in CloudFront&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In Key Management, go to Key Groups.&lt;/li&gt;
&lt;li&gt;Click Create Key Group and add the public key you just uploaded.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12iezgk4vcdy41c69ev1.png" alt="Image description" width="800" height="390"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note: Select the public key from the dropdown that we created in Step 3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Set Up a CloudFront Distribution&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In CloudFront, click Create Distribution.&lt;/li&gt;
&lt;li&gt;Choose your S3 bucket as the origin.&lt;/li&gt;
&lt;li&gt;In Origin Access, select the recommended control setting, then create a new OAC (Origin Access Control).
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi4gmmvxc2ny6dhcrc4ou.png" alt="Image description" width="800" height="392"&gt;
&lt;/li&gt;
&lt;li&gt;Change the Viewer Protocol Policy from 'HTTP and HTTPS' to 'Redirect HTTP to HTTPS'.&lt;/li&gt;
&lt;li&gt;Restrict viewer access: Yes, and select the Key Group created earlier.&lt;/li&gt;
&lt;li&gt;Keep other settings default and click Create Distribution.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Update S3 Bucket Policy and CORS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to S3 &lt;strong&gt;Bucket &amp;gt; Permissions&lt;/strong&gt; and add the following Bucket Policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "AllowCloudFrontServicePrincipal",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::your-bucket-name/*",
            "Condition": {
                "StringEquals": {
                    "AWS:SourceArn": "arn:aws:cloudfront::your-account-id:distribution/your-distribution-id"
                }
            }
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, configure CORS Policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
    {
        "AllowedHeaders": ["*"],
        "AllowedMethods": ["GET", "PUT", "POST"],
        "AllowedOrigins": ["http://yourlocalhost", "https://yourdomain.com"],
        "ExposeHeaders": ["ETag"],
        "MaxAgeSeconds": 3000
    }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 7: Create a Node.js Backend to Generate Pre-Signed URLs&lt;/strong&gt;&lt;br&gt;
Initialize a Node.js project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init -y
npm i express dotenv @aws-sdk/client-s3 @aws-sdk/s3-request-presigner @aws-sdk/cloudfront-signer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express from "express";
import dotenv from "dotenv";
import cors from "cors";

import { S3Client } from "@aws-sdk/client-s3";

import { PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl as uploads3Object } from "@aws-sdk/s3-request-presigner";
import { getSignedUrl as readCDNImage } from "@aws-sdk/cloudfront-signer";

dotenv.config();

const port = 7000;

// Configure the S3 client with credentials and region
export const s3Client = new S3Client({
  region: "YOUR_REGION",
  credentials: {
    accessKeyId: "YOUR_ACCESS_KEY", // Replace with your AWS access key
    secretAccessKey: "YOUR_SECRET_KEY", // Replace with your AWS secret key
  },
});

const app = express();
app.use(
  cors({
    origin: "http://your_localhost:", // Allow requests from frontend
  })
);

app.use(express.json());

// This private key is used for signing CloudFront URLs
let key = `-----BEGIN PRIVATE KEY-----
YOUR_PRIVATE_KEY_HERE
-----END PRIVATE KEY-----`;

// API route to generate a pre-signed URL for uploading images to S3
app.get("/get-presigned-url", async (req, res) =&amp;gt; {
  const { fileName, fileType } = req.query; // Extract filename and file type from request

  const params = {
    Bucket: "YOUR_S3_BUCKET_NAME", // Your S3 bucket name
    Key: `uploads/${fileName}`, // File path inside the bucket
    ContentType: fileType, // The type of file being uploaded
  };

  try {
    const command = new PutObjectCommand(params); // Create an S3 upload command

    // Generate a pre-signed URL valid for 60 seconds to upload image to S3
    const uploadUrl = await uploads3Object(s3Client, command, {
      expiresIn: 60,
    });

    res.status(200).json({
      uploadUrl, // Pre-signed S3 URL for uploading the image
    });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "Error generating pre-signed URL" });
  }
});

app.get("/get-cdn-url", async (req, res) =&amp;gt; {
  const { fileName } = req.query;

 // Generate a signed URL for accessing the image via CloudFront, valid for 1 hour
  try {
     const readImage = readCDNImage({
      url: `https://${process.env.CLOUDFRONT_DOMAIN}/uploads/${fileName}`,
      keyPairId: process.env.CLOUDFRONT_KEY_PAIR_ID, // CloudFront Key Pair ID from .env
      privateKey: key, // Private key for signing the URL
      dateLessThan: new Date(Date.now() + 60 * 60 * 1000), // URL expires in 1 hour
    });

    res.status(200).json({ readImage });
  } catch (error) {
    res
      .status(500)
      .json({ error: "Error generating signed URL" });
  }

});

// Start the Express server
app.listen(port, () =&amp;gt; {
  console.log(`Your application running on port ${port}`);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 8: Create a React Frontend to Upload Images&lt;/strong&gt;&lt;br&gt;
Initialize a React project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite@latest
cd project-name
npm install
npm install axios
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState } from "react";
import axios from "axios";

const BASE_URL = "http://localhost:7000";

const app = () =&amp;gt; {
  const [file, setFile] = useState("");
  const [imageUrl, setImageUrl] = useState("");
  console.log(file);

  const handleFileChange = (e) =&amp;gt; {
    setFile(e.target.files[0]);
  };

  const handleUpload = async () =&amp;gt; {
    const { data } = await axios.get(BASE_URL + "/get-presigned-url", {
      params: { fileName: file.name, fileType: file.type },
    });

    await axios.put(data.uploadUrl, file, {
      headers: { "Content-Type": file.type },
    });
  };

  const getImageFromCdn = async () =&amp;gt; {
    const { data } = await axios.get(BASE_URL + "/get-cdn-url", {
      params: { fileName: file.name },
    });

    console.log(data.readImage);

    setImageUrl(data.readImage);
  };

  return (
    &amp;lt;&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;input type="file" onChange={handleFileChange} /&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;button onClick={handleUpload}&amp;gt;Upload Image to S3&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;br /&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;input type="text" value={file.name} /&amp;gt;
        &amp;lt;button onClick={getImageFromCdn}&amp;gt;Get Image from CDN&amp;lt;/button&amp;gt;
        &amp;lt;div&amp;gt;
          {imageUrl &amp;amp;&amp;amp; &amp;lt;img src={imageUrl} alt="Uploaded" width="200" /&amp;gt;}
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/&amp;gt;
  );
};

export default app;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
You have successfully set up a secure system to upload images to S3 using pre-signed URLs and retrieve them via CloudFront signed URLs. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Use This Approach?&lt;/strong&gt;&lt;br&gt;
✅ Security: No direct public access to S3.&lt;br&gt;
✅ Performance: CloudFront caches images, reducing load times.&lt;br&gt;
✅ Cost-Effective: CloudFront reduces the number of direct S3 requests.&lt;/p&gt;

&lt;p&gt;Now, your app can securely upload and serve images efficiently!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>react</category>
      <category>node</category>
    </item>
  </channel>
</rss>
