Summary
This incident centers on Spring Boot failing to serve nested index.html files generated by a Next.js static export using trailingSlash: true. Although Next.js correctly outputs /page/index.html, Spring Boot does not automatically map /page to /page/index.html unless explicitly configured. The result is a mismatch between how Next.js expects static pages to be resolved and how Spring Boot resolves static resources.
Root Cause
The root cause is that Spring Boot’s static resource resolver does not automatically treat directory requests as index.html lookups, except for the root path /.
Key contributing factors:
- Spring Boot only auto-resolves
/→/index.html - It does not recursively apply this rule to subdirectories
- Next.js
exportwithtrailingSlash: trueoutputs:/auth/sign-in/index.html- But Spring Boot looks for
/auth/sign-in(a file), not/auth/sign-in/index.html
- No Spring configuration exists by default to map directory requests to nested
index.htmlfiles
Why This Happens in Real Systems
This pattern is extremely common when combining frontend static exports with backend static hosting:
- Frameworks like Next.js, React, Vue, SvelteKit export nested
index.htmlfiles - Backends like Spring Boot, Django, Rails do not automatically map directory paths to nested index files
- Developers assume the backend behaves like Nginx or Apache, which do support directory index resolution by default
Real-World Impact
This mismatch causes:
- 404 errors for valid static pages
- Broken navigation when users click links expecting directory-style URLs
- Unexpected API route conflicts if workarounds are implemented incorrectly
- Maintenance overhead when adding new pages
Example or Code (if necessary and relevant)
A minimal Spring Boot configuration that correctly resolves nested index.html files:
@Configuration
public class StaticResourceConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/{path:^(?!api$).*$}")
.setViewName("forward:/index.html");
registry.addViewController("/**/{path:^(?!api$).*$}")
.setViewName("forward:/index.html");
}
}
How Senior Engineers Fix It
Senior engineers solve this by explicitly configuring Spring Boot to treat directory requests as index lookups, while avoiding interference with API endpoints.
Typical solutions include:
- Adding a custom
ResourceResolverthat maps/path→/path/index.html - Using a WebMvcConfigurer to forward unknown paths to the static index
- Ensuring API routes are excluded (e.g.,
/api/**) - Using Spring’s
ResourceHttpRequestHandlerto support directory index resolution
Key principles:
- Never break API routing
- Never hardcode page paths
- Ensure the solution scales as new pages are added
Why Juniors Miss It
Junior engineers often miss this issue because:
- They assume Spring Boot behaves like Nginx/Apache regarding directory indexes
- They expect Next.js export conventions to be universally supported
- They do not understand Spring’s static resource resolution pipeline
- They try to fix the issue by modifying Next.js instead of Spring
- They overlook the need to protect API routes from static forwarding
The core misunderstanding is believing that static hosting is standardized, when in reality each backend framework has its own resolution rules.