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
- server.xml and Connectors
- Tomcat Directory Layout
- Basic idea of Servlets and HTTP sessions
Three Layers of Configuration
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| File | Scope | Typical edits |
|---|---|---|
conf/server.xml | Whole Tomcat | Ports, virtual hosts |
conf/web.xml | All apps inherit | Rare—global default servlet behavior |
conf/context.xml | All apps inherit | Shared JNDI resources (careful) |
WEB-INF/web.xml | One app | Servlets, filters, session timeout |
META-INF/context.xml | One app | App-specific Context (path, reload) |
conf/Catalina/localhost/*.xml | One app by name | External docBase, production deploy |
Code explanation:
- More specific or later-loaded app config can override inherited defaults
- Spring Boot executable JARs often skip
web.xmlentirely—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:
myapp.war
├── index.html → /myapp/index.html (default servlet)
├── assets/app.js → /myapp/assets/app.js
└── WEB-INF/ → not directly accessible via URLCode explanation:
WEB-INF/is private—browsers must not fetch classes orlib/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:
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet { /* ... */ }Code explanation:
metadata-complete="true"inweb.xmldisables annotation scanning—useful for strict XML-only deploys- Without it, Tomcat scans
WEB-INF/classesfor annotations at deploy time
Application web.xml (WEB-INF/web.xml)
Minimal example for a Servlet 4 / Jakarta app:
| Element | Purpose |
|---|---|
welcome-file-list | Resource served for directory URLs (/myapp/ → index.html) |
session-config/session-timeout | Idle session expiry in minutes |
error-page | Custom page for HTTP status or exception type |
servlet / servlet-mapping | Explicit servlet registration (if not using annotations) |
filter / filter-mapping | Encoding, auth, logging filters |
Register a servlet in XML (classic style)
<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:
http://localhost:8080/myapp/helloFilters 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:
<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 onreloadable—see below)- Global
context.xmlapplies 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
<Context path="/hello" reloadable="true">
<!-- app-specific resources -->
</Context>| Attribute | Meaning |
|---|---|
path | Context path (often inferred from WAR name—explicit only when needed) |
reloadable | If true, Tomcat watches WEB-INF/classes and WEB-INF/lib for changes and redeploys |
docBase | Usually omitted inside WAR—Tomcat sets it automatically |
Outside the WAR: Context fragment
conf/Catalina/localhost/hello.xml (for app at context /hello):
<Context docBase="/opt/apps/hello.war"
reloadable="false"
unpackWARs="true" />Code explanation:
docBasecan point to a WAR or exploded directory anywhere on disk- File name
hello.xml→ context path/hello(exceptROOT.xml→/) - Preferred for production—upgrade WAR without touching Tomcat home
Details in Deploying WAR and webapps.
reloadable: development vs production
| Setting | Development | Production |
|---|---|---|
reloadable | Sometimes true for quick class tests | false — redeploy causes memory leaks and downtime under load |
| Deploy flow | IDE or copy class files | CI 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/
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):
<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&serverTimezone=UTC"/>
</Context>Code explanation:
name="jdbc/MyDB"— JNDI pathjava:comp/env/jdbc/MyDBauth="Container"— Tomcat creates and pools connections (Tomcat DBCP pool)&in XML — escaped&in JDBC URL query string
3. Link in web.xml (resource reference)
<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)
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:
- Same
DataSourceabstraction as connection pooling in JDBC - Spring Boot usually configures HikariCP in
application.ymlinstead of Tomcat JNDI—both end at a pool, different wiring - See Spring Boot JDBC DataSource for the Boot-native path
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 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>| Role | Purpose |
|---|---|
manager-gui | HTML Manager at /manager/html |
manager-script | Text API for scripted deploy |
admin-gui | Host Manager (virtual hosts)—use sparingly |
Generate a hashed password (Tomcat 10+):
cd $CATALINA_HOME
./bin/digest.sh -a SHA-512 your_passwordPaste 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)
Request handling order (conceptual):
server.xml Host appBase
→ Context (auto, localhost/*.xml, or META-INF/context.xml)
→ WEB-INF/web.xml + annotations
→ Filters → Servlets| Question | Look 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
| Style | Configuration |
|---|---|
| Spring Boot JAR | application.yml, auto-config, embedded Tomcat |
| Spring Boot WAR on external Tomcat | SpringBootServletInitializer, minimal or empty web.xml, Boot still owns MVC and DataSource |
| Classic Servlet WAR | WEB-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
- Backup:
cp conf/context.xml conf/context.xml.bak - For app-only changes, rebuild WAR—avoid editing exploded
webapps/myapp/long term - For localhost fragments, edit
conf/Catalina/localhost/myapp.xml, then reload or restart - Check logs:
tail -30 logs/localhost.$(date +%Y-%m-%d).log- Hit a test URL:
curl -I http://127.0.0.1:8080/myapp/Common Mistakes
| Mistake | Symptom | Fix |
|---|---|---|
JDBC driver only in WEB-INF/lib but JNDI in Tomcat context.xml | NameNotFoundException or driver not found | Driver in lib/ for container-managed pool, or use app-managed pool in WAR only |
Unescaped & in JDBC URL in XML | Parse error on deploy | Use & |
reloadable="true" under load | Slow leaks, odd class errors | false in prod; restart to deploy |
Edited web.xml in exploded WAR, rebuilt WAR overwrites | Changes lost | Edit source project, mvn package, redeploy |
Expect tomcat-users.xml to log into your app | Confusion | That file is for Manager only |
Post-Chapter Checklist
- Distinguish
conf/web.xmlvsWEB-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.xmlis for Manager, not application login
What Comes Next
| Chapter | Topic |
|---|---|
| 07 — Deploying WAR and webapps | Copy WAR, Manager deploy, external docBase |
| 08 — First Servlet application | Maven WAR end-to-end |
| 11 — Tomcat security basics | Harden 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.