web.xml, context.xml, and Configuration Layers

Introduction

server.xml defines ports and hosts; web.xml and context.xml define how each web application behaves—default servlets, session timeouts, error pages, and optional JNDI resources like a database pool. Tomcat merges settings from global files under conf/, per-application descriptors inside the WAR, and Context fragments on disk. This chapter explains those layers, what to edit where, and how they connect to JDBC DataSource concepts used in Spring Boot.

Prerequisites

Three Layers of Configuration

text
Container (Tomcat)
├── conf/server.xml           ← Connectors, Engine, Host
├── conf/web.xml              ← defaults for EVERY app
├── conf/context.xml          ← default Context for EVERY app
└── conf/tomcat-users.xml     ← Manager / Host Manager users
 
Web application (WAR)
├── META-INF/context.xml      ← optional Context overrides
└── WEB-INF/web.xml           ← servlets, filters, welcome files, errors
FileScopeTypical edits
conf/server.xmlWhole TomcatPorts, virtual hosts
conf/web.xmlAll apps inheritRare—global default servlet behavior
conf/context.xmlAll apps inheritShared JNDI resources (careful)
WEB-INF/web.xmlOne appServlets, filters, session timeout
META-INF/context.xmlOne appApp-specific Context (path, reload)
conf/Catalina/localhost/*.xmlOne app by nameExternal docBase, production deploy

Code explanation:

  • More specific or later-loaded app config can override inherited defaults
  • Spring Boot executable JARs often skip web.xml entirely—standalone Tomcat WARs still use these files frequently

Global web.xml (conf/web.xml)

Tomcat ships a large default conf/web.xml. It registers container-wide components every application inherits, including:

  • Default servlet — serves static files (HTML, CSS, JS, images)
  • JSP servlet — compiles and runs JSP pages
  • Default MIME mappings, welcome-file behavior baseline, session defaults

You rarely edit global web.xml when learning. Instead, override in WEB-INF/web.xml inside your WAR.

Default servlet and static files

The default servlet maps to / and serves files from the WAR root when no other servlet matches:

text
myapp.war
├── index.html          →  /myapp/index.html  (default servlet)
├── assets/app.js       →  /myapp/assets/app.js
└── WEB-INF/            →  not directly accessible via URL

Code explanation:

  • WEB-INF/ is private—browsers must not fetch classes or lib/ JARs over HTTP
  • Static frontends (React/Vite dist/ copied into WAR root) rely on the default servlet

Servlet 3+ annotations vs web.xml

Modern apps can declare servlets with @WebServlet instead of XML:

java
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
 
@WebServlet("/hello")
public class HelloServlet extends HttpServlet { /* ... */ }

Code explanation:

  • metadata-complete="true" in web.xml disables annotation scanning—useful for strict XML-only deploys
  • Without it, Tomcat scans WEB-INF/classes for annotations at deploy time

Application web.xml (WEB-INF/web.xml)

Minimal example for a Servlet 4 / Jakarta app:

ElementPurpose
welcome-file-listResource served for directory URLs (/myapp/index.html)
session-config/session-timeoutIdle session expiry in minutes
error-pageCustom page for HTTP status or exception type
servlet / servlet-mappingExplicit servlet registration (if not using annotations)
filter / filter-mappingEncoding, auth, logging filters

Register a servlet in XML (classic style)

xml
<servlet>
  <servlet-name>Hello</servlet-name>
  <servlet-class>com.example.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>Hello</servlet-name>
  <url-pattern>/hello</url-pattern>
</servlet-mapping>

Full URL with context path /myapp:

text
http://localhost:8080/myapp/hello

Filters follow the same pattern with filter and filter-mapping—order matters for the filter chain.

Global context.xml (conf/context.xml)

Default Tomcat conf/context.xml often enables a WatchedResource and may define shared resources:

xml
<Context>
  <WatchedResource>WEB-INF/web.xml</WatchedResource>
  <WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
  <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
</Context>

Code explanation:

  • WatchedResource — Tomcat can reload the Context when these files change (behavior depends on reloadable—see below)
  • Global context.xml applies to every deployed application unless overridden

Warning

Shared JNDI in Global context.xml

Putting a DataSource in global context.xml exposes it to all apps on the instance—usually prefer per-app META-INF/context.xml or Spring Boot application.yml instead.

Per-Application context.xml

Inside the WAR: META-INF/context.xml

xml
<Context path="/hello" reloadable="true">
  <!-- app-specific resources -->
</Context>
AttributeMeaning
pathContext path (often inferred from WAR name—explicit only when needed)
reloadableIf true, Tomcat watches WEB-INF/classes and WEB-INF/lib for changes and redeploys
docBaseUsually omitted inside WAR—Tomcat sets it automatically

Outside the WAR: Context fragment

conf/Catalina/localhost/hello.xml (for app at context /hello):

xml
<Context docBase="/opt/apps/hello.war"
         reloadable="false"
         unpackWARs="true" />

Code explanation:

  • docBase can point to a WAR or exploded directory anywhere on disk
  • File name hello.xml → context path /hello (except ROOT.xml/)
  • Preferred for production—upgrade WAR without touching Tomcat home

Details in Deploying WAR and webapps.

reloadable: development vs production

SettingDevelopmentProduction
reloadableSometimes true for quick class testsfalse — redeploy causes memory leaks and downtime under load
Deploy flowIDE or copy class filesCI replaces WAR, controlled restart

Warning

reloadable on Production

Frequent reloads leak PermGen/metaspace on older JVMs and still stress class loaders on modern JDKs. Use full redeploy or blue/green instead.

JNDI DataSource in context.xml (Intro)

JNDI (Java Naming and Directory Interface) lets Tomcat expose a javax.sql.DataSource that application code looks up by name—common in traditional Java EE style apps.

1. Put JDBC driver in Tomcat lib/

bash
cp mysql-connector-j-8.x.x.jar $CATALINA_HOME/lib/

Restart Tomcat after adding JARs to lib/.

2. Define Resource in Context

META-INF/context.xml inside your WAR (or localhost fragment):

xml
<Context>
  <Resource name="jdbc/MyDB"
            auth="Container"
            type="javax.sql.DataSource"
            maxTotal="20"
            maxIdle="10"
            maxWaitMillis="10000"
            username="demo"
            password="demo_pass"
            driverClassName="com.mysql.cj.jdbc.Driver"
            url="jdbc:mysql://127.0.0.1:3306/demo?useSSL=false&amp;serverTimezone=UTC"/>
</Context>

Code explanation:

  • name="jdbc/MyDB" — JNDI path java:comp/env/jdbc/MyDB
  • auth="Container" — Tomcat creates and pools connections (Tomcat DBCP pool)
  • &amp; in XML — escaped & in JDBC URL query string
xml
<resource-ref>
  <description>MySQL DB</description>
  <res-ref-name>jdbc/MyDB</res-ref-name>
  <res-type>javax.sql.DataSource</res-type>
  <res-auth>Container</res-auth>
</resource-ref>

4. Look up in Servlet code (illustrative)

java
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource) envCtx.lookup("jdbc/MyDB");
try (Connection conn = ds.getConnection()) {
    // use connection
}

Code explanation:

tomcat-users.xml (Manager Access)

conf/tomcat-users.xml defines users for Tomcat’s Manager and Host Manager web apps—not your business application users.

Example (Tomcat 10):

xml
<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users xmlns="http://tomcat.apache.org/xml"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://tomcat.apache.org/xml
              http://tomcat.apache.org/xml/tomcat-users.xsd"
              version="1.0">
 
  <role rolename="manager-gui"/>
  <role rolename="manager-script"/>
  <user username="admin" password="change_me_strong"
        roles="manager-gui,manager-script"/>
</tomcat-users>
RolePurpose
manager-guiHTML Manager at /manager/html
manager-scriptText API for scripted deploy
admin-guiHost Manager (virtual hosts)—use sparingly

Generate a hashed password (Tomcat 10+):

bash
cd $CATALINA_HOME
./bin/digest.sh -a SHA-512 your_password

Paste the digest into password="…" with {SHA-512}… prefix per tool output.

Code explanation:

  • Restart Tomcat after editing tomcat-users.xml
  • Restrict Manager to localhost or VPN in production—see Tomcat Security Basics
  • Your Spring Security / session login is configured inside the WAR, not here

How Configuration Merges (Precedence)

text
Request handling order (conceptual):
  server.xml Host appBase
       → Context (auto, localhost/*.xml, or META-INF/context.xml)
            → WEB-INF/web.xml + annotations
                 → Filters → Servlets
QuestionLook here first
Which port?conf/server.xml Connector
Where is the WAR on disk?docBase in localhost fragment or webapps/
Session timeout?WEB-INF/web.xml session-config
DB pool for legacy WAR?META-INF/context.xml Resource
Static index.html?WAR root + welcome-file-list
Manager password?conf/tomcat-users.xml

When both global and app web.xml define behavior, the application descriptor adds to or overrides defaults according to Servlet spec merge rules—when unsure, test after deploy and read logs/localhost.*.log.

Spring Boot WAR vs Classic web.xml

StyleConfiguration
Spring Boot JARapplication.yml, auto-config, embedded Tomcat
Spring Boot WAR on external TomcatSpringBootServletInitializer, minimal or empty web.xml, Boot still owns MVC and DataSource
Classic Servlet WARWEB-INF/web.xml and/or @WebServlet

Boot apps rarely use Tomcat JNDI DataSources— they use HikariCP from properties. Standalone Tomcat tutorials and older enterprise WARs still use context.xml resources.

Edit and Deploy Workflow

  1. Backup: cp conf/context.xml conf/context.xml.bak
  2. For app-only changes, rebuild WAR—avoid editing exploded webapps/myapp/ long term
  3. For localhost fragments, edit conf/Catalina/localhost/myapp.xml, then reload or restart
  4. Check logs:
bash
tail -30 logs/localhost.$(date +%Y-%m-%d).log
  1. Hit a test URL:
bash
curl -I http://127.0.0.1:8080/myapp/

Common Mistakes

MistakeSymptomFix
JDBC driver only in WEB-INF/lib but JNDI in Tomcat context.xmlNameNotFoundException or driver not foundDriver in lib/ for container-managed pool, or use app-managed pool in WAR only
Unescaped & in JDBC URL in XMLParse error on deployUse &amp;
reloadable="true" under loadSlow leaks, odd class errorsfalse in prod; restart to deploy
Edited web.xml in exploded WAR, rebuilt WAR overwritesChanges lostEdit source project, mvn package, redeploy
Expect tomcat-users.xml to log into your appConfusionThat file is for Manager only

Post-Chapter Checklist

  • Distinguish conf/web.xml vs WEB-INF/web.xml
  • Know three Context locations: global, META-INF/, conf/Catalina/localhost/
  • Explain welcome-file-list, session-timeout, error-page
  • Describe JNDI DataSource at a high level and when Spring Boot makes it unnecessary
  • Know tomcat-users.xml is for Manager, not application login

What Comes Next

ChapterTopic
07 — Deploying WAR and webappsCopy WAR, Manager deploy, external docBase
08 — First Servlet applicationMaven WAR end-to-end
11 — Tomcat security basicsHarden Manager, remove samples

FAQ

Is WEB-INF/web.xml required?

Not for Servlet 3+ if you use annotations and no required XML-only features—but many teams keep a minimal web.xml for session and error settings.

Can I delete conf/web.xml?

No—Tomcat needs the global defaults. Back it up; do not remove.

context.xml in WAR vs localhost fragment?

WAR-embedded travels with the app; localhost fragment keeps secrets and paths on the server (different docBase per environment).

Same as Spring application.yml?

Conceptually similar environment-specific binding, different mechanism. Boot spring.datasource.url replaces Tomcat JNDI for most new projects.

Why 404 on welcome page?

Missing index.html, wrong welcome-file-list, or wrong context path—not necessarily web.xml on the server.

Does editing global context.xml require restart?

Yes for global conf/context.xml. Per-app redeploy may pick up META-INF/context.xml changes depending on autoDeploy—do not rely on it in production.

Tomcat 9 javax vs Tomcat 10 jakarta web.xml namespace?

Tomcat 10 uses https://jakarta.ee/xml/ns/jakartaee and jakarta.* classes. Tomcat 9 uses Java EE javax.* namespace URLs—match your Tomcat major version.