<?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: Muhammad Usman Tahir</title>
    <description>The latest articles on DEV Community by Muhammad Usman Tahir (@usman_tahir).</description>
    <link>https://dev.to/usman_tahir</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3722492%2F82d9ea4b-d8e5-4b64-bff8-78f97bb33655.jpg</url>
      <title>DEV Community: Muhammad Usman Tahir</title>
      <link>https://dev.to/usman_tahir</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/usman_tahir"/>
    <language>en</language>
    <item>
      <title>Designing a Scalable AI-Powered Serverless Text-to-Speech System with AWS Polly</title>
      <dc:creator>Muhammad Usman Tahir</dc:creator>
      <pubDate>Mon, 02 Nov 2026 11:46:21 +0000</pubDate>
      <link>https://dev.to/usman_tahir/designing-a-scalable-ai-powered-serverless-text-to-speech-system-with-aws-polly-299d</link>
      <guid>https://dev.to/usman_tahir/designing-a-scalable-ai-powered-serverless-text-to-speech-system-with-aws-polly-299d</guid>
      <description>&lt;p&gt;Recently, I engineered a fully serverless AI-powered Text-to-Speech application using AWS services, focused on scalability, intelligent voice handling, low operational cost, and production-level reliability.&lt;/p&gt;

&lt;p&gt;What looked like a simple “text → audio” feature actually involved solving multiple real-world engineering challenges around AI voice generation, architecture design, fallback handling, latency, and cost optimization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project Goal&lt;/strong&gt;&lt;br&gt;
The objective was to create a system where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users can enter text&lt;/li&gt;
&lt;li&gt;Select different AI voices&lt;/li&gt;
&lt;li&gt;Generate natural human-like speech instantly&lt;/li&gt;
&lt;li&gt;Receive a playable MP3 URL in real-time&lt;/li&gt;
&lt;li&gt;All without managing traditional backend servers or GPUs&lt;/li&gt;
&lt;li&gt;The complete system was designed around a fully Serverless Architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why I Chose AWS Polly Instead of Open-Source Models&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the biggest decisions in this project was choosing the right AI speech engine.&lt;/p&gt;

&lt;p&gt;Initially, open-source TTS models looked attractive, but after deeper evaluation, several practical issues appeared:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPU dependency&lt;/li&gt;
&lt;li&gt;High inference cost&lt;/li&gt;
&lt;li&gt;Heavy deployment requirements&lt;/li&gt;
&lt;li&gt;Long processing time&lt;/li&gt;
&lt;li&gt;Complex scaling&lt;/li&gt;
&lt;li&gt;Infrastructure maintenance overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of spending resources managing ML infrastructure, I focused on solving the actual business problem efficiently.&lt;/p&gt;

&lt;p&gt;That is where Amazon Polly became the ideal choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Intelligent Voice Engine Handling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the most interesting engineering parts of this project was how voice generation was handled dynamically.&lt;/p&gt;

&lt;p&gt;AWS Polly provides multiple voice engine types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generative&lt;/li&gt;
&lt;li&gt;Neural&lt;/li&gt;
&lt;li&gt;Standard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the challenge was: ⚠ Not every voice supports every engine type. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some voices support Generative AI voices&lt;/li&gt;
&lt;li&gt;Some only support Neural&lt;/li&gt;
&lt;li&gt;Others fallback to Standard synthesis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I designed a smart fallback mechanism inside the Lambda layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Smart AI Voice Fallback Logic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Whenever a request comes from frontend:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;System first tries the highest-quality engine (Generative)&lt;/li&gt;
&lt;li&gt;If the selected voice does not support Generative, the system automatically switches to Neural&lt;/li&gt;
&lt;li&gt;If Neural is also unavailable, the system falls back to Standard voice synthesis.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This solved multiple production problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevented request failures&lt;/li&gt;
&lt;li&gt;Improved reliability&lt;/li&gt;
&lt;li&gt;Maintained uninterrupted audio generation&lt;/li&gt;
&lt;li&gt;Allowed dynamic voice compatibility handling&lt;/li&gt;
&lt;li&gt;Delivered best possible voice quality automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This made the system more intelligent instead of relying on hardcoded voice-engine combinations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full Architecture Flow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Frontend Layer (React.js)&lt;/strong&gt;&lt;br&gt;
The user enters text and selects a voice.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7hr67l354h5qi4o3hl1p.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7hr67l354h5qi4o3hl1p.png" alt=" " width="799" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. API Gateway — Entry Point&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;API Gateway acts as the secure frontend-facing layer.&lt;br&gt;
It handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Routing&lt;/li&gt;
&lt;li&gt;CORS&lt;/li&gt;
&lt;li&gt;Request validation&lt;/li&gt;
&lt;li&gt;Lambda triggering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This removed the need for maintaining any servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. AWS Lambda — AI Processing Layer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lambda became the brain of the system.&lt;br&gt;
Responsibilities included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parsing requests&lt;/li&gt;
&lt;li&gt;Selecting optimal engine type&lt;/li&gt;
&lt;li&gt;Calling Amazon Polly SDK&lt;/li&gt;
&lt;li&gt;Handling fallback logic&lt;/li&gt;
&lt;li&gt;Generating audio streams&lt;/li&gt;
&lt;li&gt;Uploading files to S3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since Lambda runs only when triggered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No idle servers&lt;/li&gt;
&lt;li&gt;No infrastructure management&lt;/li&gt;
&lt;li&gt;Automatic scaling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Amazon Polly — AI Speech Synthesis&lt;/strong&gt;&lt;br&gt;
Amazon Polly processed the text using advanced speech synthesis models and generated highly natural MP3 audio.&lt;/p&gt;

&lt;p&gt;Depending on compatibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generative voices were prioritized&lt;/li&gt;
&lt;li&gt;Neural voices were used as secondary fallback&lt;/li&gt;
&lt;li&gt;Standard synthesis ensured universal compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. AWS S3 — Audio Storage Layer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Generated MP3 files were uploaded to S3 buckets.&lt;br&gt;
Lambda returned the public audio URL back to frontend instantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend Audio Playback&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On frontend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Audio URL was stored in React state&lt;/li&gt;
&lt;li&gt;HTML5 audio player handled playback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;audio controls src={audioUrl} /&amp;gt;&lt;/code&gt;&lt;br&gt;
This created a smooth real-time AI audio experience for users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Engineering Decisions That Improved the System&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Serverless-First Architecture&lt;/strong&gt;&lt;br&gt;
No server deployment or DevOps overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost Optimization&lt;/strong&gt;&lt;br&gt;
No GPU required. No always-running backend. Pure pay-per-use model.&lt;/p&gt;

&lt;p&gt;Scalability&lt;br&gt;
The architecture can automatically handle massive concurrent requests using AWS Lambda scaling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reliability Through Fallback Logic&lt;/strong&gt;&lt;br&gt;
Voice compatibility problems were solved dynamically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Faster Development Cycle&lt;/strong&gt;&lt;br&gt;
Cloud-native services allowed focusing on product logic instead of infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technologies Used&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Lambda&lt;/li&gt;
&lt;li&gt;Amazon Polly&lt;/li&gt;
&lt;li&gt;API Gateway&lt;/li&gt;
&lt;li&gt;AWS S3&lt;/li&gt;
&lt;li&gt;Axios&lt;/li&gt;
&lt;li&gt;Serverless Architecture&lt;/li&gt;
&lt;li&gt;React.js&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Learning&lt;/strong&gt;&lt;br&gt;
This project taught me that building AI systems is not only about training models.&lt;/p&gt;

&lt;p&gt;Real engineering value comes from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Designing scalable systems&lt;/li&gt;
&lt;li&gt;Solving production edge cases&lt;/li&gt;
&lt;li&gt;Optimizing cost&lt;/li&gt;
&lt;li&gt;Improving reliability&lt;/li&gt;
&lt;li&gt;Creating intelligent fallback mechanisms&lt;/li&gt;
&lt;li&gt;Delivering real-world usable experiences&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI + Cloud Architecture together can create extremely powerful solutions when designed correctly.&lt;/p&gt;

&lt;p&gt;Live Demo:&lt;br&gt;
&lt;a href="https://tts-flame.vercel.app/" rel="noopener noreferrer"&gt;https://tts-flame.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The application demonstrates a fully serverless AI-powered Text-to-Speech workflow using AWS Polly, Lambda, API Gateway, and S3.&lt;/p&gt;

&lt;h1&gt;
  
  
  AWS #ArtificialIntelligence #Serverless #CloudComputing #AWSLambda #AmazonPolly #ReactJS #GenerativeAI #SoftwareEngineering #WebDevelopment #AIEngineering #FrontendDevelopment #CloudArchitecture #S3 #APIGateway @AWS @AWS-builders
&lt;/h1&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>cloud</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Why My EC2 Instance Was Running but Still Not Accessible</title>
      <dc:creator>Muhammad Usman Tahir</dc:creator>
      <pubDate>Wed, 21 Jan 2026 09:44:23 +0000</pubDate>
      <link>https://dev.to/usman_tahir/why-my-ec2-instance-was-running-but-still-not-accessible-17aa</link>
      <guid>https://dev.to/usman_tahir/why-my-ec2-instance-was-running-but-still-not-accessible-17aa</guid>
      <description>&lt;p&gt;When I launched my first Amazon EC2 instance, everything looked fine in the AWS Console.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instance state: Running&lt;/li&gt;
&lt;li&gt;System status checks: Passed&lt;/li&gt;
&lt;li&gt;Public IP assigned: Yes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But when I tried to connect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH → Timeout&lt;/li&gt;
&lt;li&gt;Browser → Connection refused&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first, this felt confusing. If the instance is running, why can’t I access it?&lt;br&gt;
This post explains &lt;strong&gt;why this happens&lt;/strong&gt;, what I misunderstood, and how I fixed it.&lt;br&gt;
&lt;strong&gt;The Problem&lt;/strong&gt;&lt;br&gt;
I faced two common symptoms:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; &lt;strong&gt;SSH Access Failed&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh ec2-user@192.168.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connection timed out&lt;/li&gt;
&lt;li&gt;Or connection refused&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; &lt;strong&gt;Web App Not Opening&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser showed "This site can’t be reached"
Despite the EC2 instance being healthy, &lt;strong&gt;network access was blocked&lt;/strong&gt;.
&lt;strong&gt;The Key Realization&lt;/strong&gt;
&lt;strong&gt;Running EC2 ≠ Accessible EC2&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS separates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compute (EC2 instance)&lt;/li&gt;
&lt;li&gt;Network security (Security Groups + NACLs)&lt;/li&gt;
&lt;li&gt;Networking (Public vs Private IP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If &lt;strong&gt;any one&lt;/strong&gt; of these is misconfigured, access will fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Groups vs NACLs (Most Common Confusion)&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Security Groups&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Act as a &lt;strong&gt;virtual firewall&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Work at the &lt;strong&gt;instance level&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Are &lt;strong&gt;stateful&lt;/strong&gt; (responses are automatically allowed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If inbound traffic is not explicitly allowed →  access denied.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Network ACLs (NACLs)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wOrk at the &lt;strong&gt;subnet level&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Are &lt;strong&gt;stateless&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Require both inbound &lt;strong&gt;and outbound&lt;/strong&gt; rules.
Even if your Security Group is correct, a restrictive NACL can still block traffic.
&lt;strong&gt;Required Ports Explained&lt;/strong&gt;
Depending on your use case, these ports must be open:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Port&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Protocol&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SSH&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTPS&lt;/td&gt;
&lt;td&gt;443&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Missing even one required port = &lt;strong&gt;connection failure&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Public IP vs Private IP (Critical Detail)&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Public IP&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Required to access EC2 from the internet&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Changes when instance is stopped/started (unless Elastic IP is used)&lt;br&gt;
&lt;strong&gt;Private IP&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Works &lt;strong&gt;only inside the VPC&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not accessible from your local machine&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you use a &lt;strong&gt;private IP&lt;/strong&gt; from your laptop, the connection will never work.&lt;br&gt;
&lt;strong&gt;The Fix: Security Group Configuration&lt;/strong&gt;&lt;br&gt;
My issue was missing inbound rules in the Security Group.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: Allow SSH Access (Port 22)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "IpProtocol": "tcp",
  "FromPort": 22,
  "ToPort": 22,
  "IpRanges": [
    {
      "CidrIp": "0.0.0.0/0",
      "Description": "Allow SSH access"
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example: Allow HTTP Access (Port 80)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "IpProtocol": "tcp",
  "FromPort": 80,
  "ToPort": 80,
  "IpRanges": [
    {
      "CidrIp": "0.0.0.0/0",
      "Description": "Allow HTTP traffic"
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For production, never keep SSH open to 0.0.0.0/0. Restrict it to your IP.&lt;br&gt;
&lt;strong&gt;What I Checked Step-by-Step&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;EC2 instance state → &lt;strong&gt;Running&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Public IP assigned → &lt;strong&gt;Yes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Security Group inbound rules → &lt;strong&gt;Missing&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;NACL rules → &lt;strong&gt;Allowed&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Correct username used (ec2-user, ubuntu, etc.)&lt;/li&gt;
&lt;li&gt;Correct key permissions (chmod 400)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After fixing the Security Group, SSH worked instantly.&lt;br&gt;
&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 running status only confirms compute availability&lt;/li&gt;
&lt;li&gt;Network access depends on &lt;strong&gt;Security Groups + NACLs&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Security Groups must explicitly allow required ports&lt;/li&gt;
&lt;li&gt;Public IP is mandatory for internet access&lt;/li&gt;
&lt;li&gt;AWS prioritizes &lt;strong&gt;security over convenience&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;br&gt;
This issue taught me how &lt;strong&gt;layered AWS security&lt;/strong&gt; really is.&lt;br&gt;
Instead of assuming something is broken, it’s better to trace the request path:&lt;br&gt;
&lt;strong&gt;Client → Internet → Security Group → NACL → EC2&lt;/strong&gt;&lt;br&gt;
Once I understood this flow, debugging became much easier.&lt;br&gt;
If you’re learning AWS, trust me everyone hits this problem at least once.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ec2</category>
      <category>cloud</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Why My S3 Object URL Still Gave Access Denied (And How I Fixed It)</title>
      <dc:creator>Muhammad Usman Tahir</dc:creator>
      <pubDate>Wed, 21 Jan 2026 09:22:36 +0000</pubDate>
      <link>https://dev.to/usman_tahir/why-my-s3-object-url-still-gave-access-denied-and-how-i-fixed-it-54p9</link>
      <guid>https://dev.to/usman_tahir/why-my-s3-object-url-still-gave-access-denied-and-how-i-fixed-it-54p9</guid>
      <description>&lt;p&gt;When you’re new to AWS S3, one of the most confusing things is public access.&lt;br&gt;
Recently, I faced an issue where I uploaded a file to S3, disabled Block all public access, but still got Access Denied when opening the Object URL.&lt;br&gt;
Here’s what happened, why it happened, and how I fixed it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;&lt;br&gt;
I uploaded a file to an S3 bucket and wanted to access it using the &lt;strong&gt;Object URL&lt;/strong&gt;.&lt;br&gt;
Steps I had already taken:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uploaded the file successfully&lt;/li&gt;
&lt;li&gt;Disabled &lt;strong&gt;Block all public access&lt;/strong&gt; at the bucket level&lt;/li&gt;
&lt;li&gt;Tried opening the Object URL in the browser&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result: &lt;strong&gt;AccessDenied&lt;/strong&gt;&lt;br&gt;
At this point, I thought:&lt;br&gt;
"Public access is disabled, so why is it still denying access?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Important Concept I Missed&lt;/strong&gt;&lt;br&gt;
Disabling &lt;strong&gt;Block all public access&lt;/strong&gt; does &lt;strong&gt;NOT&lt;/strong&gt; automatically make your objects public.&lt;br&gt;
It only means:&lt;br&gt;
AWS will now allow you to create public permissions — but you still have to define them.&lt;br&gt;
So unless you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a &lt;strong&gt;bucket policy&lt;/strong&gt;, or&lt;/li&gt;
&lt;li&gt;Make the object public via ACL (not recommended anymore)
Your objects will remain private.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Solution That Worked&lt;/strong&gt;&lt;br&gt;
I added a &lt;strong&gt;bucket policy&lt;/strong&gt; to explicitly allow public read access for objects.&lt;br&gt;
Here is the policy I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::usman-bucket-v1/*"
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What This Policy Does&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allows &lt;strong&gt;anyone&lt;/strong&gt; (Principal: "*")&lt;/li&gt;
&lt;li&gt;To &lt;strong&gt;read objects&lt;/strong&gt; (s3:GetObject)&lt;/li&gt;
&lt;li&gt;Inside my bucket (usman-bucket-v1/*)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After adding this policy, I refreshed the Object URL and it worked.&lt;br&gt;
&lt;strong&gt;Why AWS Does This (Security Reason)&lt;/strong&gt;&lt;br&gt;
AWS follows &lt;strong&gt;secure-by-default&lt;/strong&gt; principles.&lt;br&gt;
Even if public access blocking is disabled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Objects are still private&lt;/li&gt;
&lt;li&gt;You must explicitly allow public access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This prevents accidental data leaks, especially in production environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important Warning&lt;/strong&gt;&lt;br&gt;
This policy makes &lt;strong&gt;all objects in the bucket public&lt;/strong&gt;.&lt;br&gt;
Do &lt;strong&gt;NOT&lt;/strong&gt; use this for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sensitive files&lt;/li&gt;
&lt;li&gt;User data&lt;/li&gt;
&lt;li&gt;Production buckets without proper review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Safer Alternatives&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;CloudFront + OAC&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;pre-signed URLs&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Restrict access to specific IPs or principals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disabling Block all public access ≠ public files&lt;/li&gt;
&lt;li&gt;You still need a bucket policy&lt;/li&gt;
&lt;li&gt;AWS forces explicit permission for security&lt;/li&gt;
&lt;li&gt;Bucket policies are powerful — use carefully&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;br&gt;
This small issue helped me better understand how S3 security actually works.&lt;br&gt;
If you're learning AWS (like me), these "Access Denied" errors are not failures — they’re lessons.&lt;br&gt;
If this post helped you, feel free to share or drop a comment on dev.to.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>s3</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
