Tomcat 404 After Deploying WAR Context Path Mismatch: Fix It
A practical guide to diagnosing and fixing Tomcat 404 errors after WAR deployment by verifying the effective context path using log and configuration inspection, with manual steps and AI-assisted log reading.
Rao Aadil, India 5 min read
Confirm the 404 is from Tomcat (not a proxy or frontend)
Send a request straight to the Tomcat connector and look at the response headers. Linux, use curl with the -I flag to see the headers without the body:
curl -I http://your-tomcat-host:8080/myapp
Windows PowerShell, use Invoke-WebRequest and print the headers:
$response = Invoke-WebRequest -Uri http://your-tomcat-host:8080/myapp -Method Head -ErrorAction SilentlyContinue
$response.Headers
A Tomcat-generated 404 includes a Server header of Apache-Coyote/1.1 (or a similar Coyote version). If you see nginx, Microsoft-IIS, or another frontend server, the proxy generated the 404 before the request reached Tomcat. The problem is frontend routing, not the WAR deployment.
When you're behind nginx or IIS, compare the frontend access log with Tomcat's access log. Tail Tomcat's access log while repeating the request. Linux:
tail -f $CATALINA_HOME/logs/localhost_access_log.*.txt
Windows PowerShell:
Get-Content "$env:CATALINA_HOME\logs\localhost_access_log.*.txt" -Wait -Tail 100
If the request shows up in the frontend log but not in Tomcat's, the frontend is not forwarding correctly. If it shows up in Tomcat's log with a 404 status, the issue is inside Tomcat.
“The WAR deploys with no error and Manager lists the application as running, but the URL you expect returns 404.”
It checks which context path Tomcat actually assigned - the WAR file name, and any context file under conf/Catalina/localhost that overrides it - and tails the log covering that request, so you find out whether the app is live at a different path or not serving the request at all.
It reads the real state of the server before it says anything, shows you the exact command, and waits for your approval. Windows and Linux, over the SSH access you already have.
Find the actual context path Tomcat assigned to the WAR
Tomcat derives the default context path from the name of the file or directory in webapps. The one exception: ROOT maps to /. List the directory to see what Tomcat sees.
Linux:
ls -l $CATALINA_HOME/webapps
Windows PowerShell:
Get-ChildItem "$env:CATALINA_HOME\webapps"
If you deployed myapp.war, the context path is /myapp. If you deployed myapp-1.0.war, it's /myapp-1.0 — even if you always access /myapp.
The deployment log confirms it. Look for a line like Deploying web application archive myapp.war in catalina.out or localhost.log. Linux:
grep -i "Deploying web application" $CATALINA_HOME/logs/catalina.out | tail -20
Windows PowerShell:
Select-String -Path "$env:CATALINA_HOME\logs\catalina.out" -Pattern "Deploying web application" | Select-Object -Last 20
If nothing shows up, check today's localhost log: $CATALINA_HOME/logs/localhost.$(date +%Y-%m-%d).log on Linux or $env:CATALINA_HOME\logs\localhost.$(Get-Date -Format yyyy-MM-dd).log on Windows.
Explicit Context elements can override the default. Check server.xml in $CATALINA_HOME/conf (Linux) or $env:CATALINA_HOME\conf (Windows) for <Context path="/something" docBase="..."> entries. Also look at per-host context files in $CATALINA_HOME/conf/Catalina/localhost/ (or %CATALINA_HOME%\conf\Catalina\localhost\). A file named myapp.xml with a docBase pointing somewhere else makes Tomcat serve that application at /myapp no matter what the WAR file is called.
If you have a tool that can read Tomcat logs and configuration, this is the moment it earns its keep. Ask it to read the Tomcat configuration and show me the effective context path for this WAR, and it returns the relevant lines from catalina.out and any overriding Context entry without you grepping through multiple files manually.
Diagnose common causes of context path mismatch
These patterns produce a deployment that looks fine but 404s on the URL you expect.
WAR file name differs from desired path. myapp.war maps to /myapp; myapp-1.0.war maps to /myapp-1.0. If your build artifact has a version number, Tomcat uses that version number as the context path.
Exploded directory already exists with a different name. Tomcat prefers an exploded directory over a WAR file. An old myapp-old/ directory still sitting in webapps gets deployed instead, and its name becomes the context path. The new WAR is ignored.
A META-INF/context.xml inside the WAR specifies a path attribute. In modern Tomcat (8.0+), the path attribute in an embedded context.xml is ignored unless the Host's copyXML is true. Tomcat logs a warning; the app still deploys under the WAR file name.
A Context element in server.xml or conf/Catalina/[host]/myapp.xml overrides the default naming. In server.xml, <Context path="/foo" docBase="/path/to/war" /> can force a custom path, though it's discouraged. In a per-host file like conf/Catalina/localhost/myapp.xml, the file name myapp is the context path; the path attribute is ignored.
Missing or failed deployment due to exceptions. Tomcat can fail to fully start the web application and keep running, leaving the context unavailable. The manager UI may show the application as deployed, but its servlets never registered. Check catalina.out and localhost.[date].log for stack traces. Linux, tail the latest localhost log:
tail -n 100 $CATALINA_HOME/logs/localhost.$(date +%Y-%m-%d).log
Windows PowerShell:
Get-Content "$env:CATALINA_HOME\logs\localhost.$(Get-Date -Format yyyy-MM-dd).log" -Tail 100
Look for ClassNotFoundException, NoClassDefFoundError, or missing dependency errors.
Fix the context path
Fix the cause, not the symptom.
If the WAR file name is wrong, rename it to match the context path you want. Linux:
mv $CATALINA_HOME/webapps/myapp-1.0.war $CATALINA_HOME/webapps/myapp.war
Windows PowerShell:
Rename-Item "$env:CATALINA_HOME\webapps\myapp-1.0.war" "$env:CATALINA_HOME\webapps\myapp.war"
If an exploded directory is in the way, rename it the same way.
When you need a custom context path that can't be expressed by the file name, create a context file in conf/Catalina/[host]/. Linux:
cat > $CATALINA_HOME/conf/Catalina/localhost/myapp.xml <<'EOF'
<Context docBase="/path/to/your/myapp.war" />
EOF
Windows PowerShell:
Set-Content -Path "$env:CATALINA_HOME\conf\Catalina\localhost\myapp.xml" -Value '<Context docBase="C:\path\to\your\myapp.war" />'
The file name myapp becomes the context path /myapp. The path attribute is neither needed nor honored in this location. Do not put a path attribute in META-INF/context.xml inside the WAR; it will not set the context path in modern Tomcat.
After changing the file name, directory name, or context file, restart Tomcat. Linux with systemd:
sudo systemctl restart tomcat
Or manually:
$CATALINA_HOME/bin/shutdown.sh
$CATALINA_HOME/bin/startup.sh
Windows, restart the service in PowerShell:
Restart-Service Tomcat9
Or manually:
%CATALINA_HOME%\bin\shutdown.bat
%CATALINA_HOME%\bin\startup.bat
Then request the context path again and check the status code. Linux:
curl -I http://your-tomcat-host:8080/myapp
Windows PowerShell:
$response = Invoke-WebRequest -Uri http://your-tomcat-host:8080/myapp -Method Head -ErrorAction SilentlyContinue
$response.StatusCode
A 200 OK means the app is reachable at the path you wanted. If you still get 404, go back through the context path discovery steps and check the access log for that request.
Prevent future context path surprises
Name final WAR artifacts exactly after the context path you want, no version numbers. Do the rename in your build or release pipeline before deployment.
Tomcat Manager or a scripted deploy can set the context path explicitly. The manager HTTP API takes a custom path parameter and returns a response that says whether the deployment succeeded.
Automate a post-deployment check. A simple script hits the expected context path and verifies the response isn't 404. Linux, combine curl and grep:
curl -s -o /dev/null -w "%{http_code}" http://your-tomcat-host:8080/myapp | grep -q 200 && echo "OK" || echo "Not OK"
Windows PowerShell:
$code = (Invoke-WebRequest -Uri http://your-tomcat-host:8080/myapp -Method Head -ErrorAction SilentlyContinue).StatusCode
if ($code -eq 200) { "OK" } else { "Not OK" }
If your deployment pipeline already calls a log-reading assistant, add a step that asks it to read the Tomcat access log after deploy and report any 404 or error entries for the new context path. That surfaces context path mismatches before a user sees them.
Document the actual context path for each application in a central config repository. The next person shouldn't have to guess.