SpringBoot
Spring-MVC
Maven
WEB-INF
WebApp Development

WEB-INF not included in WebApp using SpringBoot, Spring-MVC and Maven

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

If you have worked with traditional Java web applications, you are accustomed to the WEB-INF directory sitting at the root of your WAR file and holding web.xml, JSP files, and library JARs. When you create a new Spring Boot project with Spring MVC and Maven, that directory is nowhere to be found. This is not a bug; it is a deliberate design choice that reflects how Spring Boot packages and runs applications.

Why WEB-INF Exists in Traditional WAR Projects

In a classic Servlet-based application deployed to an external server like Tomcat or Jetty, WEB-INF serves several purposes.

 
1my-webapp/
2  src/
3    main/
4      webapp/
5        WEB-INF/
6          web.xml
7          views/
8            index.jsp
9          lib/
10          classes/

The Servlet specification states that files under WEB-INF are not directly accessible by a client browser, which makes it a safe place to store JSPs and configuration. The web.xml deployment descriptor defines servlets, filters, and listener mappings. The lib folder holds third-party JARs, and classes holds compiled bytecode.

How Spring Boot Changes the Picture

Spring Boot defaults to packaging your application as an executable JAR (not a WAR). An executable JAR uses an embedded Tomcat, Jetty, or Undertow server, so there is no external Servlet container to deploy into. Because there is no external container, there is no need for WEB-INF or web.xml. Spring Boot replaces web.xml with auto-configuration and Java-based configuration classes.

java
1@SpringBootApplication
2public class MyApp {
3    public static void main(String[] args) {
4        // Embedded Tomcat starts here; no web.xml needed
5        SpringApplication.run(MyApp.class, args);
6    }
7}

Maven's spring-boot-maven-plugin repackages the JAR with the embedded server and all dependencies inside it. The resulting artifact is self-contained.

xml
1<build>
2    <plugins>
3        <plugin>
4            <groupId>org.springframework.boot</groupId>
5            <artifactId>spring-boot-maven-plugin</artifactId>
6        </plugin>
7    </plugins>
8</build>

Adding JSP Support with WEB-INF

If you still need JSPs, perhaps because you are migrating a legacy application, you can bring WEB-INF back. Create the directory manually under src/main/webapp.

 
1src/
2  main/
3    webapp/
4      WEB-INF/
5        jsp/
6          home.jsp

Then configure the view resolver in application.properties so Spring MVC knows where to find JSP files.

properties
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

You also need to add the Tomcat JSP dependency and switch your packaging from JAR to WAR in pom.xml.

xml
1<packaging>war</packaging>
2
3<dependencies>
4    <dependency>
5        <groupId>org.apache.tomcat.embed</groupId>
6        <artifactId>tomcat-embed-jasper</artifactId>
7    </dependency>
8    <dependency>
9        <groupId>javax.servlet</groupId>
10        <artifactId>jstl</artifactId>
11    </dependency>
12</dependencies>

Finally, make your main class extend SpringBootServletInitializer so it works both as a standalone JAR (with embedded Tomcat) and as a WAR deployed to an external server.

java
1@SpringBootApplication
2public class MyApp extends SpringBootServletInitializer {
3
4    @Override
5    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
6        return builder.sources(MyApp.class);
7    }
8
9    public static void main(String[] args) {
10        SpringApplication.run(MyApp.class, args);
11    }
12}

When to Use Templates Instead

Spring Boot strongly encourages server-side template engines like Thymeleaf, FreeMarker, or Mustache over JSPs. Templates are loaded from the classpath (src/main/resources/templates/), which means they work with JAR packaging out of the box and do not require WEB-INF at all.

java
1@Controller
2public class HomeController {
3
4    @GetMapping("/")
5    public String home(Model model) {
6        model.addAttribute("message", "Hello from Thymeleaf");
7        return "home"; // resolves to templates/home.html
8    }
9}
html
1<!-- src/main/resources/templates/home.html -->
2<!DOCTYPE html>
3<html xmlns:th="http://www.thymeleaf.org">
4<body>
5    <h1 th:text="${message}">Placeholder</h1>
6</body>
7</html>

This approach is simpler, faster to develop with (templates support hot-reload), and fully compatible with executable JAR packaging.

Common Pitfalls

  • Placing JSPs under src/main/resources: JSPs must live under src/main/webapp/WEB-INF, not on the classpath. The embedded Tomcat JSP engine cannot find them from resources/.
  • Forgetting to change packaging to WAR: If you add src/main/webapp/WEB-INF but leave \<packaging>jar\</packaging> in your POM, Maven ignores the webapp directory entirely during the build.
  • Missing tomcat-embed-jasper dependency: Without this dependency, the embedded Tomcat has no JSP compiler. Requests to JSP-backed endpoints return a 404 or a blank page with no helpful error message.
  • Not extending SpringBootServletInitializer: If you package as WAR and deploy to an external Tomcat without this class, the container does not know how to bootstrap your Spring context.
  • Using WEB-INF out of habit: Many developers add WEB-INF to a Spring Boot project simply because they are used to it. Unless you have a concrete need for JSPs, prefer Thymeleaf or another template engine, which avoids the complexity of WAR packaging altogether.

Summary

  • Spring Boot packages applications as executable JARs with an embedded server, eliminating the need for WEB-INF and web.xml.
  • If you need JSPs, create src/main/webapp/WEB-INF, switch Maven packaging to WAR, and add the tomcat-embed-jasper dependency.
  • Extend SpringBootServletInitializer to support deployment to both embedded and external Servlet containers.
  • For new projects, prefer Thymeleaf or FreeMarker templates stored in src/main/resources/templates/, which work seamlessly with JAR packaging.
  • The absence of WEB-INF is intentional and reflects Spring Boot's philosophy of convention over configuration and self-contained deployable artifacts.

Course illustration
Course illustration

All Rights Reserved.