How do I serve index.html in subfolders with S3/Cloudfront?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
When you put a static site behind CloudFront, the root URL can be easy to handle, but subfolders are where people get surprised. Default Root Object only helps for /, not for paths such as /docs/ or /blog/, so serving index.html in subfolders usually requires a URL rewrite strategy.
Understand the S3 and CloudFront Difference
S3 static website hosting and CloudFront do not behave identically.
If you use the S3 website endpoint directly, S3 has website-style behaviors such as serving index documents for directory-style requests. Once CloudFront is in front, especially with an S3 REST origin plus origin access control, you should not assume those website semantics still solve every path automatically.
That is why /index.html works but /docs/ may return a 403 or 404 unless you rewrite the request to /docs/index.html.
Default Root Object Only Solves the Root
In CloudFront, this setting:
helps only when the request URI is /.
It does not automatically map:
- '
/docs/to/docs/index.html' - '
/blog/to/blog/index.html'
That distinction is the source of most confusion.
Rewrite Directory Requests with a CloudFront Function
The clean modern fix is a viewer-request rewrite. A CloudFront Function can append index.html to directory-style paths before the request goes to S3.
This handles:
- '
/as/index.html' - '
/docs/as/docs/index.html' - '
/docsas/docs/index.html'
while leaving asset requests such as /app.js or /images/logo.png alone.
That is usually the simplest answer for folder-based static sites.
SPA Fallback Is a Different Problem
Be careful not to mix two different requirements:
- serve
/section/from/section/index.html - serve unknown app routes from one root
index.html
The first is a directory-index problem. The second is a single-page application fallback problem.
For SPA fallback, the rewrite often becomes:
- if the path has no file extension, send
/index.html
That is useful for React Router and similar client-side routers, but it is not the same thing as preserving per-folder index.html files.
So define the routing model clearly before implementing a rewrite.
Keep the S3 Layout Consistent
If you want /docs/ to work, the objects should actually exist as:
and not only as:
The rewrite cannot invent a directory structure that is not in the bucket. It only changes which key CloudFront requests.
This also means your build pipeline should upload files in the exact folder structure your URLs imply.
Error Responses Are a Fallback, Not the Best Primary Mechanism
Some teams use custom error responses so that 403 or 404 errors return index.html. That can work for SPA fallback, but it is a poor primary solution for subfolder index handling because:
- true missing files can be masked
- debugging becomes harder
- every miss turns into an error-based control flow
A request rewrite is usually cleaner and faster because CloudFront asks for the right object the first time.
Test the Important URLs Explicitly
After you add the rewrite, test:
- '
/' - '
/docs/' - '
/docs' - '
/docs/index.html' - one real asset such as
/docs/app.css
This matters because a rewrite that is too broad can accidentally rewrite asset URLs too, which breaks the site in a more confusing way than the original 404.
Common Pitfalls
- Assuming
Default Root Objectapplies to every subfolder path. - Using a root-level SPA fallback when the site actually has folder-specific
index.htmlfiles. - Rewriting every extensionless path without checking whether that matches the site structure.
- Forgetting that the requested S3 keys must still exist in the bucket.
- Relying on custom error responses when a viewer-request rewrite would be cleaner.
Summary
- CloudFront
Default Root Objectonly covers the root path. - To serve
index.htmlin subfolders, rewrite directory-style URLs to/folder/index.html. - A CloudFront Function is usually the cleanest way to do that.
- SPA fallback and subfolder index handling are related but different routing problems.
- Always verify that the S3 object layout matches the rewritten URLs.
Related reading
- How do I set Content-Type when uploading to S3 with AWS CLI?
- How do I set the name of the default profile in AWS CLI?
- How do I set up cloud-init on custom AMIs in AWS? CentOS
- How do I set up TensorFlow in the Google cloud?
- How do I set distance between flexbox items?
- How do I set/unset a cookie with jQuery?
- How do I set up TensorFlow in the Google cloud?
- How do I specify template parameters when running AWS SAM Local?

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.