Index: apache-tomcat-8.0.53-src/java/org/apache/catalina/servlets/DefaultServlet.java
===================================================================
--- apache-tomcat-8.0.53-src.orig/java/org/apache/catalina/servlets/DefaultServlet.java
+++ apache-tomcat-8.0.53-src/java/org/apache/catalina/servlets/DefaultServlet.java
@@ -1704,7 +1704,7 @@ public class DefaultServlet extends Http
 
         // First check that the resulting path is under the provided base
         try {
-            if (!candidate.getCanonicalPath().startsWith(base.getCanonicalPath())) {
+            if (!candidate.getCanonicalFile().toPath().startsWith(base.getCanonicalFile().toPath())) {
                 return null;
             }
         } catch (IOException ioe) {
Index: apache-tomcat-8.0.53-src/java/org/apache/catalina/startup/ContextConfig.java
===================================================================
--- apache-tomcat-8.0.53-src.orig/java/org/apache/catalina/startup/ContextConfig.java
+++ apache-tomcat-8.0.53-src/java/org/apache/catalina/startup/ContextConfig.java
@@ -571,29 +571,32 @@ public class ContextConfig implements Li
         Host host = (Host) context.getParent();
         File appBase = host.getAppBaseFile();
 
-        String docBase = context.getDocBase();
-        if (docBase == null) {
+        // This could be blank, relative, absolute or canonical
+        String docBaseConfigured = context.getDocBase();
+        // If there is no explicit docBase, derive it from the path and version
+        if (docBaseConfigured == null) {
             // Trying to guess the docBase according to the path
             String path = context.getPath();
             if (path == null) {
                 return;
             }
             ContextName cn = new ContextName(path, context.getWebappVersion());
-            docBase = cn.getBaseName();
+            docBaseConfigured = cn.getBaseName();
         }
 
-        File file = new File(docBase);
-        if (!file.isAbsolute()) {
-            docBase = (new File(appBase, docBase)).getPath();
+        // Obtain the absolute docBase in String and File form
+        String docBaseAbsolute;
+        File docBaseConfiguredFile = new File(docBaseConfigured);
+        if (!docBaseConfiguredFile.isAbsolute()) {
+            docBaseAbsolute = (new File(appBase, docBaseConfigured)).getAbsolutePath();
         } else {
-            docBase = file.getCanonicalPath();
+            docBaseAbsolute = docBaseConfiguredFile.getAbsolutePath();
         }
-        file = new File(docBase);
-        String origDocBase = docBase;
+        File docBaseAbsoluteFile = new File(docBaseAbsolute);
+        String originalDocBase = docBaseAbsolute;
 
         ContextName cn = new ContextName(context.getPath(), context.getWebappVersion());
         String pathName = cn.getBaseName();
-
         boolean unpackWARs = true;
         if (host instanceof StandardHost) {
             unpackWARs = ((StandardHost) host).isUnpackWARs();
@@ -602,28 +605,29 @@ public class ContextConfig implements Li
             }
         }
 
-        boolean docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
-
-        if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory()) {
-            URL war = UriUtil.buildJarUrl(new File(docBase));
+        // At this point we need to determine if we have a WAR file in the
+        // appBase that needs to be expanded. Therefore we consider the absolute
+        // docBase NOT the canonical docBase. This is because some users symlink
+        // WAR files into the appBase and we want this to work correctly.
+        boolean docBaseAbsoluteInAppBase = docBaseAbsolute.startsWith(appBase.getPath() + File.separatorChar);
+        if (docBaseAbsolute.toLowerCase(Locale.ENGLISH).endsWith(".war") && !docBaseAbsoluteFile.isDirectory()) {
+            URL war = UriUtil.buildJarUrl(docBaseAbsoluteFile);
             if (unpackWARs) {
-                docBase = ExpandWar.expand(host, war, pathName);
-                file = new File(docBase);
-                docBase = file.getCanonicalPath();
+                docBaseAbsolute = ExpandWar.expand(host, war, pathName);
+                docBaseAbsoluteFile = new File(docBaseAbsolute);
                 if (context instanceof StandardContext) {
-                    ((StandardContext) context).setOriginalDocBase(origDocBase);
+                    ((StandardContext) context).setOriginalDocBase(originalDocBase);
                 }
             } else {
                 ExpandWar.validate(host, war, pathName);
             }
         } else {
-            File docDir = new File(docBase);
-            File warFile = new File(docBase + ".war");
+            File docBaseAbsoluteFileWar = new File(docBaseAbsolute + ".war");
             URL war = null;
-            if (warFile.exists() && docBaseInAppBase) {
-                war = UriUtil.buildJarUrl(warFile);
+            if (docBaseAbsoluteFileWar.exists() && docBaseAbsoluteInAppBase) {
+                war = UriUtil.buildJarUrl(docBaseAbsoluteFileWar);
             }
-            if (docDir.exists()) {
+            if (docBaseAbsoluteFile.exists()) {
                 if (war != null && unpackWARs) {
                     // Check if WAR needs to be re-expanded (e.g. if it has
                     // changed). Note: HostConfig.deployWar() takes care of
@@ -634,31 +638,33 @@ public class ContextConfig implements Li
             } else {
                 if (war != null) {
                     if (unpackWARs) {
-                        docBase = ExpandWar.expand(host, war, pathName);
-                        file = new File(docBase);
-                        docBase = file.getCanonicalPath();
+                        docBaseAbsolute = ExpandWar.expand(host, war, pathName);
+                        docBaseAbsoluteFile = new File(docBaseAbsolute);
                     } else {
-                        docBase = warFile.getCanonicalPath();
+                        docBaseAbsolute = docBaseAbsoluteFileWar.getAbsolutePath();
+                        docBaseAbsoluteFile = docBaseAbsoluteFileWar;
                         ExpandWar.validate(host, war, pathName);
                     }
                 }
                 if (context instanceof StandardContext) {
-                    ((StandardContext) context).setOriginalDocBase(origDocBase);
+                    ((StandardContext) context).setOriginalDocBase(originalDocBase);
                 }
             }
         }
 
-        // Re-calculate now docBase is a canonical path
-        docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
+        String docBaseCanonical = docBaseAbsoluteFile.getCanonicalPath();
 
-        if (docBaseInAppBase) {
-            docBase = docBase.substring(appBase.getPath().length());
+        // Re-calculate now docBase is a canonical path
+        boolean docBaseCanonicalInAppBase = docBaseAbsoluteFile.getCanonicalFile().toPath().startsWith(appBase.toPath());
+        String docBase;
+        if (docBaseCanonicalInAppBase) {
+            docBase = docBaseCanonical.substring(appBase.getPath().length());
             docBase = docBase.replace(File.separatorChar, '/');
             if (docBase.startsWith("/")) {
                 docBase = docBase.substring(1);
             }
         } else {
-            docBase = docBase.replace(File.separatorChar, '/');
+            docBase = docBaseCanonical.replace(File.separatorChar, '/');
         }
 
         context.setDocBase(docBase);
Index: apache-tomcat-8.0.53-src/java/org/apache/catalina/startup/ExpandWar.java
===================================================================
--- apache-tomcat-8.0.53-src.orig/java/org/apache/catalina/startup/ExpandWar.java
+++ apache-tomcat-8.0.53-src/java/org/apache/catalina/startup/ExpandWar.java
@@ -26,6 +26,7 @@ import java.net.JarURLConnection;
 import java.net.URL;
 import java.net.URLConnection;
 import java.nio.channels.FileChannel;
+import java.nio.file.Path;
 import java.util.Enumeration;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
@@ -116,10 +117,7 @@ public class ExpandWar {
         }
 
         // Expand the WAR into the new document base directory
-        String canonicalDocBasePrefix = docBase.getCanonicalPath();
-        if (!canonicalDocBasePrefix.endsWith(File.separator)) {
-            canonicalDocBasePrefix += File.separator;
-        }
+        Path canonicalDocBasePath = docBase.getCanonicalFile().toPath();
 
         // Creating war tracker parent (normally META-INF)
         File warTrackerParent = warTracker.getParentFile();
@@ -134,14 +132,13 @@ public class ExpandWar {
                 JarEntry jarEntry = jarEntries.nextElement();
                 String name = jarEntry.getName();
                 File expandedFile = new File(docBase, name);
-                if (!expandedFile.getCanonicalPath().startsWith(
-                        canonicalDocBasePrefix)) {
+                if (!expandedFile.getCanonicalFile().toPath().startsWith(canonicalDocBasePath)) {
                     // Trying to expand outside the docBase
                     // Throw an exception to stop the deployment
                     throw new IllegalArgumentException(
                             sm.getString("expandWar.illegalPath",war, name,
                                     expandedFile.getCanonicalPath(),
-                                    canonicalDocBasePrefix));
+                                    canonicalDocBasePath));
                 }
                 int last = name.lastIndexOf('/');
                 if (last >= 0) {
@@ -210,10 +207,7 @@ public class ExpandWar {
         File docBase = new File(host.getAppBaseFile(), pathname);
 
         // Calculate the document base directory
-        String canonicalDocBasePrefix = docBase.getCanonicalPath();
-        if (!canonicalDocBasePrefix.endsWith(File.separator)) {
-            canonicalDocBasePrefix += File.separator;
-        }
+        Path canonicalDocBasePath = docBase.getCanonicalFile().toPath();
         JarURLConnection juc = (JarURLConnection) war.openConnection();
         juc.setUseCaches(false);
         try (JarFile jarFile = juc.getJarFile()) {
@@ -222,14 +216,13 @@ public class ExpandWar {
                 JarEntry jarEntry = jarEntries.nextElement();
                 String name = jarEntry.getName();
                 File expandedFile = new File(docBase, name);
-                if (!expandedFile.getCanonicalPath().startsWith(
-                        canonicalDocBasePrefix)) {
+                if (!expandedFile.getCanonicalFile().toPath().startsWith(canonicalDocBasePath)) {
                     // Entry located outside the docBase
                     // Throw an exception to stop the deployment
                     throw new IllegalArgumentException(
                             sm.getString("expandWar.illegalPath",war, name,
                                     expandedFile.getCanonicalPath(),
-                                    canonicalDocBasePrefix));
+                                    canonicalDocBasePath));
                 }
             }
         } catch (IOException e) {
Index: apache-tomcat-8.0.53-src/java/org/apache/catalina/startup/HostConfig.java
===================================================================
--- apache-tomcat-8.0.53-src.orig/java/org/apache/catalina/startup/HostConfig.java
+++ apache-tomcat-8.0.53-src/java/org/apache/catalina/startup/HostConfig.java
@@ -598,8 +598,7 @@ public class HostConfig
                     docBase = new File(host.getAppBaseFile(), context.getDocBase());
                 }
                 // If external docBase, register .xml as redeploy first
-                if (!docBase.getCanonicalPath().startsWith(
-                        host.getAppBaseFile().getAbsolutePath() + File.separator)) {
+                if (!docBase.getCanonicalFile().toPath().startsWith(host.getAppBaseFile().toPath())) {
                     isExternal = true;
                     deployedApp.redeployResources.put(
                             contextXml.getAbsolutePath(),
Index: apache-tomcat-8.0.53-src/webapps/docs/changelog.xml
===================================================================
--- apache-tomcat-8.0.53-src.orig/webapps/docs/changelog.xml
+++ apache-tomcat-8.0.53-src/webapps/docs/changelog.xml
@@ -141,6 +141,11 @@
       <add>
         Improve validation of storage location when using FileStore. (markt)
       </add>
+      <fix>
+        <bug>63872</bug>: Fix some edge cases where the docBase was not being
+        set using a canonical path which in turn meant resource URLs were not
+        being constructed as expected. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">
@@ -280,6 +285,10 @@
         Implement checksum checks when downloading dependencies that are used
         to build Tomcat. (kkolinko)
       </add>
+      <scode>
+        Use <code>java.nio.file.Path</code> to test for one directory being a
+        sub-directory of another in a consistent way. (markt)
+      </scode>
     </changelog>
   </subsection>
 </section>

