Fix relative paths in HTML served by java servlet (for HTML5 pushstate)

I'm trying to modify a Jetty server which has a servlet ( it somehow magically picks up and uses:

public class RootResource {
    private ServletContext servletContext;

    @Path("react-client/{path: .*\\..+$}")
    public Response serveReactClientContent(@Context HttpServletRequest httpServletRequest) {
        // This doesn't work, a resolved relative resource is not relative
        // to the /react-client base. See description of problem.
        final String pathToResource = httpServletRequest.getRequestURI();
        return serveStaticContent(pathToResource);

    @Path("react-client/{path: .*$}")
    public Response serveReactClientIndexPage(@Context HttpServletRequest httpServletRequest) {
        return serveStaticContent("/react-client/index.html");

    private Response serveStaticContent(String pathToResource) {
        final String type = this.servletContext.getMimeType(pathToResource);
        final Response.ResponseBuilder response = Response.ok(servletContext.getResourceAsStream(pathToResource)).type(type);

The idea is to take a GET request to react-client/some/path and return the contents of react-client/index.html. Essentially making Jetty behave like a webapp server which uses client side routing.

The issue I'm having is that the relative paths in the index.html only work if the path is one level deep e.g. react-client/products.

<script src="./webapp.js"></script>

In that case the javascript file above in index.html is found because webapp.js is a file which exists at react-client/webapp.js.

As soon as I try a deeper link e.g. react-client/products/97357361 that fails as the servlet tries to find webapp.js in react-client/products/webapp.js which doesn't exist.

How can I make it request the resource always as if it's from /react-client? Thanks


2 Answers Fix relative paths in HTML served by java servlet (for HTML5 pushstate)

I tried a lot of things such as filters and rewrite handlers but at no point did Jetty ever provide the original unresolved relative import (e.g. ./webapp) so I never had an opportunity of rewriting relative resources.

In the end I had to just detect them with a hack in the knowledge that they were nearly all requested from a /static/ sub directory (I had to add a hard coded exception for favicon.ico and manifest.json.

@Path("react-client/{path: .*\\..+$}")
public Response serveReactClientContent(@Context HttpServletRequest httpServletRequest) {
    final String pathToResource = httpServletRequest.getRequestURI();

    Pattern p = Pattern.compile("/react-client/(.+?(?=static/|favicon.ico))", Pattern.CASE_INSENSITIVE);
    Matcher m = p.matcher(pathToResource);
    String resolveResourcePath = m.replaceAll("/react-client/");
    return serveStaticContent(resolveResourcePath);

Not happy with it but it works.

4 months ago

Since your index.html could get access by multiple urls such as

  • /react-client/products
  • /react-client/products/97357361
  • /react-client/some
  • /react-client/some/path

It might be wise to add a <base> tag at the header of your HTML page.

    <base href="" />
    <script src="./script.js"></script>

This will tell the browser to resolve any relative path found in the page as relative to the

So, according to the example above, <script src="./script.js"></script> would be requested to the server as "" regardless of the current url being use to access this page.

Then, you can revert back those settings on Jetty and keep it simple.

4 months ago