A Spring Boot application can compile cleanly, start up without errors, connect to the database, and still return a frustrating 404 Not Found response when you hit an endpoint. This disconnect happens because 404 errors in Spring MVC aren't always caused by broken code. Often, they stem from configuration mismatches, component scanning gaps, or subtle routing errors that trick the dispatcher servlet into looking in the wrong place.
Fixing these routing issues requires checking how Spring registers beans and maps web requests. Let's look at the most common reasons your endpoints return 404 responses and how to resolve them systematically.
Controller Component Scanning Issues
The most frequent cause of an unexpected 404 error is that Spring Boot simply doesn't know your controller exists. When you annotate a class with @RestController or @Controller, you expect Spring to pick it up automatically. However, component scanning relies strictly on your project's package structure.
By default, Spring Boot starts its component scan from the package containing your main application class (the one annotated with @SpringBootApplication) and all of its sub-packages. If your controller sits outside this directory tree, Spring ignores it completely during startup.
For example, if your main class is in com.example.app, but your user controller is placed in com.example.api, Spring Boot initializes successfully without registering your endpoints. Because no bean is created for that controller, every request to it yields a 404.
To fix package scanning problems, you can:
- Move your controller classes inside a sub-package of your main application class.
- Relocate your main application class to a common parent package that covers all module namespaces.
- Explicitly configure component scanning on your main configuration class using the
basePackagesattribute, like@ComponentScan(basePackages = {"com.example.app", "com.example.api"}).
Context Paths and Server Path Mappings
When developing locally, you might test an endpoint at http://localhost:8080/users. But if your deployment environment or local configuration introduces a servlet context path, that URL changes immediately.
Check your application.properties or application.yml file for global prefix configurations:
server:
servlet:
context-path: /api
Adding a context path shifts every endpoint in your application. The actual address for your user endpoint becomes http://localhost:8080/api/users. Hitting the old URL returns a 404 even though your controller mapping is correct.
Similar path mismatches happen when deploying behind an API gateway, reverse proxy, or load balancer that strips or appends path segments before forwarding traffic to your container. Always inspect the raw URL reaching your embedded Tomcat or Jetty container via console logging or an access filter rather than assuming browser paths match internal routing rules.
HTTP Method Mismatches
Sometimes the endpoint URL is correct, but the HTTP verb is wrong. If a controller method uses @PostMapping("/submit"), sending a GET request to that same path will trigger a 404 or a 405 Method Not Allowed depending on how DispatcherServlet handles the fallback chain.
In modern Spring versions, if a resource path isn't found for a specific HTTP method, the framework might attempt to treat it as a static resource request. This often surfaces in logs as an unexpected NoResourceFoundException, confusing developers into searching for missing JavaScript files or static folder configurations when the real issue is an incorrect HTTP method or an unmatched request mapping.
Verify your mapping annotations match the client request type:
- Use
@GetMappingfor retrieval operations. - Use
@PostMappingfor resource creation. - Use
@PutMappingor@PatchMappingfor updates. - Use
@DeleteMappingfor removal.
Request Mapping Typos and Parameter Conflicts
A single misplaced slash or a typo in a request mapping annotation breaks route resolution. Spring treats /users and /users/ differently depending on trailing slash matching configurations, and path variables must align precisely between the annotation and the method signature.
Review your path variables for syntax errors:
@GetMapping("/users/{id}")
public User getUser(@PathVariable("id") Long userId) {
return userService.findById(userId);
}
If a client requests /users/abc while your parameter expects a Long, Spring throws a 400 Bad Request. However, if the mismatch involves optional path segments or conflicting mapping hierarchies in larger controllers, it can fall through to a 404. Keep your path templates explicit and avoid overlapping URI patterns across different controller classes.
Single Page Application Routing Conflicts
If you are building a modern web application that pairs Spring Boot with a frontend framework like React, Angular, or Vue, client-side routing introduces unique 404 challenges.
When a user visits your app's root URL, Spring serves index.html successfully. But if they refresh the page while visiting a client-side route like /dashboard/settings, the browser sends a request for that path directly to the Spring Boot server. Because Spring finds no backend controller mapped to /dashboard/settings, it returns a 404 error page.
To prevent your backend from intercepting frontend routes, you can implement a catch-all controller that forwards unmapped paths back to the root index.html file, letting your frontend router handle the navigation state:
@Controller
public class SpaForwardController {
@RequestMapping(value = "/{path:[^\\.]*}/**")
public String forward() {
return "forward:/";
}
}
This configuration ensures that requests with file extensions (like .js or .css) are handled normally, while clean URLs fall back to your single-page application entry point.
Debugging Spring Boot Routing Step-by-Step
When an elusive 404 error stalls your development, use this systematic checklist to find the root cause:
- Enable debug-level logging for web components in your
application.propertiesfile (logging.level.org.springframework.web=DEBUG) to see all registered request mappings during application startup. - Inspect the application startup console output to verify that your controller class and its specific endpoints appear in the list of mapped URLs.
- Test the endpoint using a command-line tool like cURL or Postman to rule out browser caching or client-side proxy interference.
- Verify that your request headers, content types, and HTTP verbs match the method expectations precisely.
Consult the official Spring Framework Documentation for deep dives into web request handling and dispatcher servlet configuration patterns.
Frequently Asked Questions
Why does Spring Boot return a 404 instead of a 500 when my database lookup fails?
A 404 error indicates that a resource URI could not be found by the server router. If a database query yields no records for a valid ID, returning a 404 is often considered RESTful best practice because the endpoint path exists, but the specific entity does not. Alternatively, some teams prefer returning 204 No Content or an empty payload depending on API design standards.
How can I create a custom error page for 404 responses?
Spring Boot uses convention-based error handling. If you use template engines like Thymeleaf, place an 404.html file inside your src/main/resources/templates/error/ directory. For static HTML applications, place the file inside src/main/resources/public/error/. Spring automatically renders this page whenever a 404 status occurs.
Can security configurations cause 404 errors?
Yes. If Spring Security is misconfigured or a security filter chain blocks an unauthenticated request without a proper redirect or login challenge, it can manifest as a 404 or a 403 Forbidden error depending on the custom access rules defined in your security filter chain bean.