<?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: Ramya Hegde</title>
    <description>The latest articles on DEV Community by Ramya Hegde (@ramya_hegde_6a771225097d3).</description>
    <link>https://dev.to/ramya_hegde_6a771225097d3</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%2F4115503%2Fdb37cd6f-c4fd-4e8b-881a-786c76c7cb2a.jpg</url>
      <title>DEV Community: Ramya Hegde</title>
      <link>https://dev.to/ramya_hegde_6a771225097d3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ramya_hegde_6a771225097d3"/>
    <language>en</language>
    <item>
      <title>"CloudFront Access DEnied After a Clean S3 Deploy? Check Your Bucket Root Structure First"</title>
      <dc:creator>Ramya Hegde</dc:creator>
      <pubDate>Tue, 08 Sep 2026 11:02:20 +0000</pubDate>
      <link>https://dev.to/ramya_hegde_6a771225097d3/cloudfront-access-denied-after-a-clean-s3-deploy-check-your-bucket-root-structure-first-3amf</link>
      <guid>https://dev.to/ramya_hegde_6a771225097d3/cloudfront-access-denied-after-a-clean-s3-deploy-check-your-bucket-root-structure-first-3amf</guid>
      <description>&lt;p&gt;If you've ever pushed a perfectly successful build to S3, wired it up to CloudFront, hit the URL - and gotten a flat "403 Access Denied" - this post is for you. It's one of those errors that looks like a permission problem, send you down an IAM rabbit role and turns out to be something much simpler.&lt;/p&gt;

&lt;p&gt;Here's the exact debugging path I took recently while setting up a Bamboo CI/CD pipeline deploying an Angular front-end to S3 + CloudFront.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The setup:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The pipeline was straightforward on paper:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bamboo Specs build the Angular app&lt;/li&gt;
&lt;li&gt;Build artifacts sync to S3 bucket&lt;/li&gt;
&lt;li&gt;CloudFront serves the bucket as a static site via an Origin Access      Control (OAC)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The build succeeded.The S3 sync succeeded. &lt;em&gt;aws s3 ls&lt;/em&gt; showed the files sitting right there in the bucket. And yet, hitting the CloudFront distribution URL returned:&lt;br&gt;
&lt;code&gt;&amp;lt;Error&amp;gt;&lt;br&gt;
  &amp;lt;Code&amp;gt;AccessDenied&amp;lt;/Code&amp;gt;&lt;br&gt;
  &amp;lt;Message&amp;gt;Access Denied &amp;lt;/Message&amp;gt;&lt;br&gt;
&amp;lt;/Error&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where I looked first(why was it a dead end)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The instinct with AccessDenied is almost always IAM: bucket policy,OAC permissions, CloudFront's ability to read from the origin. I checked all three:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Bucket policy explicitly allowed s3:GetObject for the CloudFront OAC principal&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The OAC was correctly attached to the distribution's origin&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The distribution's origin path was pointing at the bucket&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything looked correct. This is the trap - When the permission are actually fine, you can burn hours re-verifying policies that were never the problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The actual root cause: bucket root structure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The real issue was upstream of permissions entirely: &lt;strong&gt;where the files landed inside the bucket didn't match where CloudFront was told to look&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The build tool (in this case, an Angular esbuild-based builder) had output the production bundle into a nested folder- not directly at the bucket root, but one level down. CloudFront's origin path, meanwhile was configured to serve from the bucket root. So when a request came in for &lt;em&gt;index.html&lt;/em&gt;, CloudFront looked in a location where nothing existed.&lt;/p&gt;

&lt;p&gt;S3 and CloudFront don't distinguish that scenario from a genuine permissions failure - a missing object under an OAC-protected origin returns the same generic Acces Denied, not a 404. Tha's what made this so easy to misdiagnose:the error message pointed at IAM, but the actual fault was a path mismatch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to confirm it's a structure issue , not a permission issue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before you touch a single IAM policy, run this quick check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;ls &lt;/span&gt;s3://your-bucket-name/ &lt;span class="nt"&gt;--recursive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look closely at there your index.html and asset files actually sit. If your CloudFront origin path is set to the bucket root (empty or /) but your files are sitting under a subfolder like /browser/ or /dist/, you've found yur answer - no policy debugging required.&lt;/p&gt;

&lt;p&gt;You can also confirm from the CloudFront side: check the &lt;strong&gt;Origin Path&lt;/strong&gt; field on the distribution's origin settings and compare it directly against the actual object keys in the bucket listing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Two options, depending on what you control:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A - Fix the build output path.&lt;/strong&gt; If your build tools is dumping files into a nested folder, adjust the build config so files land exactly where the origin expects them - typically the bucket root.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B - Fix the CloudFront origin path.&lt;/strong&gt; If restructuring the build isn't practical, point the origin path at the correct subfolder instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Origin path: /browser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Either way, the goal is the same: make the object key CloudFront requests match an object key that actually exists in the bucket.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Access DEnied on a statuc site behind CloudFRont + OAC is not always a permission bug - a missing object under a protected origin returns the same error as a genuinely blocked one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Before touching IAM, verify with &lt;em&gt;aws s3 ls --recursive&lt;/em&gt; that your files are actually where your origin path expects them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build tool changes (a new bundler , an updated framework version, a build config tweak) can silently shift output paths - Worth chcking first any time this error shows up right after a build tooling change.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Document your bucket's expected root structure somewhere visible in the pipeline - it saves the next person (possibly future you) from re-walking this same IAM detour.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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%2Fvw35dfu7y428q5uz40n4.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%2Fvw35dfu7y428q5uz40n4.png" alt=" " width="764" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Have you hit a CloudFront/S3 error that turned out to be something completely different from what it looked like? I'd love to hear about it - drop a comment below&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>aws</category>
      <category>cicd</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
