How to serve the index.html file using Spring inside a directory without typing index.html in the URL

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 export with trailingSlash: true outputs:
    • /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.html files

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.html files
  • 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 ResourceResolver that 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 ResourceHttpRequestHandler to 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.

Leave a Comment