code: public static @NotNull String externalizeUrlWithoutMapping(@NotNull String url, @Nullable SlingHttpServletRequest request) {
  
      // apply externalization only path part
      String path = url;
  
      // split off query string or fragment that may be appended to the URL
      String urlRemainder = null;
      int urlRemainderPos = StringUtils.indexOfAny(path, '?', '#');
      if (urlRemainderPos >= 0) {
        urlRemainder = path.substring(urlRemainderPos);
        path = path.substring(0, urlRemainderPos);
      }
  
      // apply namespace mangling (e.g. replace jcr: with _jcr_)
      path = mangleNamespaces(path);
  
      // add webapp context path
      if (request != null) {
        path = StringUtils.defaultString(request.getContextPath()) + path; //NOPMD
      }
  
      // url-encode path
      path = Escape.urlEncode(path);
      path = StringUtils.replace(path, "+", "%20");
      // replace %2F back to / for better readability
      path = StringUtils.replace(path, "%2F", "/");
  
      // build full URL again
      return path + (urlRemainder != null ? urlRemainder : "");
    }
docs: Externalizes an URL without applying Sling Mapping. Instead the servlet context path is added and sling namespace
  mangling is applied manually.
  Hostname and scheme are not added because they are added by the link handler depending on site URL configuration
  and secure/non-secure mode. URLs that are already externalized remain untouched.
  @param url Unexternalized URL (without scheme or hostname)
  @param request Request
  @return Exernalized URL without scheme or hostname, the path is URL-encoded if it contains special chars.