Skip to content

Why Does Spring Boot H2 Console Return 404 with WebFlux?

Problem

When I configured the H2 console in my Spring Boot application, I got a 404 error trying to access it:

Browser response
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
There was an unexpected error (type=Not Found, status=404).

My application.yml looked correct:

application.yml
spring:
h2:
console:
enabled: true
path: /h2
settings:
web-allow-others: true

But when I opened http://localhost:8080/h2, the console just returned 404.

Environment

  • Spring Boot 3.x
  • Spring WebFlux (spring-boot-starter-webflux)
  • H2 Database 2.x
  • Embedded server: Netty

What happened?

I was building a reactive application using Spring WebFlux. I added H2 as my development database and configured the console properties in application.yml.

My pom.xml dependency:

pom.xml
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>

When I started the application, I noticed this in the logs:

Startup log
Netty started on port(s): 8080
Started Application in 2.5 seconds

The key detail here: Netty started, not Tomcat. This indicates a reactive/WebFlux stack.

I assumed spring.h2.console.enabled=true would work, but when I accessed /h2, I got 404.

How to solve it?

I first tried adding more configuration options:

application.yml (failed attempt)
spring:
h2:
console:
enabled: true
path: /h2
settings:
web-allow-others: true
datasource:
url: jdbc:h2:mem:testdb
driverClassName: org.h2.Driver

Still 404. I then searched Stack Overflow and found the root cause.

The solution is to manually configure an H2 server using org.h2.tools.Server:

H2ServerManual.java
package com.example.config;
import org.h2.tools.Server;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
@Component
public class H2ServerManual {
private Server webServer;
@Value("${h2-server.port:8081}")
Integer h2ConsolePort;
@EventListener(ContextRefreshedEvent.class)
public void start() throws SQLException {
this.webServer = Server.createWebServer(
"-webPort", h2ConsolePort.toString(),
"-tcpAllowOthers"
).start();
System.out.println("H2 Console available at: " + webServer.getURL());
}
@EventListener(ContextClosedEvent.class)
public void stop() {
if (this.webServer != null) {
this.webServer.stop();
}
}
}

And update application.yml:

application.yml
h2-server:
port: 8081
spring:
datasource:
url: jdbc:h2:mem:testdb
driverClassName: org.h2.Driver
username: sa
password:
server:
port: 8080

I also needed to remove the runtime scope from H2 dependency:

pom.xml (corrected)
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<!-- No scope specified = compile scope, allows Server class -->
</dependency>

Now when I start the application:

Startup output
Netty started on port(s): 8080
H2 Console available at: http://localhost:8081
Started Application in 2.8 seconds

I can access the H2 console at http://localhost:8081 (not port 8080).

The reason

The key reason for the 404 error is that Spring Boot’s H2ConsoleAutoConfiguration only executes for servlet-based applications.

Here’s the difference:

Servlet vs Reactive stacks
┌─────────────────────────────────────────────────────────────────┐
│ Servlet Stack (Tomcat) │
├─────────────────────────────────────────────────────────────────┤
│ spring.h2.console.enabled=true │
│ ↓ │
│ H2ConsoleAutoConfiguration executes │
│ ↓ │
│ Servlet container (Tomcat) registers /h2 endpoint │
│ ↓ │
│ H2 console accessible at /h2 │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Reactive Stack (Netty) │
├─────────────────────────────────────────────────────────────────┤
│ spring.h2.console.enabled=true │
│ ↓ │
│ H2ConsoleAutoConfiguration SKIPPED (requires servlet) │
│ ↓ │
│ No /h2 endpoint registered │
│ ↓ │
│ 404 when accessing /h2 │
└─────────────────────────────────────────────────────────────────┘

When you use spring-boot-starter-webflux, the embedded server is Netty, not Tomcat. Netty is a reactive, non-blocking server that doesn’t support servlets. The H2 console servlet cannot be registered, so the auto-configuration is skipped.

Common mistakes I made:

  1. Assuming properties work universally - spring.h2.console.enabled only works with servlet stacks
  2. Using runtime scope for H2 - This prevents using the Server class for manual configuration
  3. Not checking logs - The “Netty started” message was the key clue

Summary

In this post, I explained why the H2 console returns 404 in Spring Boot WebFlux applications. The key point is that H2 console auto-configuration requires a servlet environment, which Netty doesn’t provide. For reactive applications, manually configure the H2 server using org.h2.tools.Server with Spring event listeners.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments