Generic Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

This article explores how to serve static resources with Spring – using both XML and Java configuration.

Further reading:

Cachable Static Assets with Spring MVC

This article shows how to cache your static assets such as Javascript and CSS files when serving them with Spring MVC.

Introduction to WebJars

A quick and practical guide to using WebJars with Spring.

Minification of JS and CSS Assets with Maven

A quick guide to using Maven to minify Javascript and CSS files in a Java web project.

2. Using Spring Boot

Spring Boot comes with a pre-configured implementation of ResourceHttpRequestHandler to facilitate serving static resources.

By default, this handler serves static content from any of /static, /public, /resources, and /META-INF/resources directories that are on the classpath. Since src/main/resources is typically on the classpath by default, we can place any of these directories there.

For example, if we put an about.html file inside the /static directory in our classpath, then we can access that file via http://localhost:8080/about.html. Similarly, we can achieve the same result by adding that file in other mentioned directories.

2.1. Custom Path Patterns

By default, Spring Boot serves all static content under the root part of the request, that is, /**Even though it seems to be a good default configuration, we can change it via the spring.mvc.static-path-pattern configuration property. 

For example, if we want to access the same file via http://localhost:8080/content/about.html, we can say so in our application.properties:

spring.mvc.static-path-pattern=/content/**

In WebFlux environments, we should use the spring.webflux.static-path-pattern property.

2.2. Custom Directories

Similar to path patterns, it's also possible to change the default resource locations via the spring.resources.static-locations configuration property. This property can accept multiple comma-separated resource locations:

spring.resources.static-locations=classpath:/files/,classpath:/static-files

Here, we're serving static contents from the /files and /static-files directories inside the classpath. Moreover, Spring Boot can serve static files from outside of the classpath:

spring.resources.static-locations=file:/opt/files

Here we're using the file resource signature, file:/, to serve files from our local disk.

3. XML Configuration

If you need to go the old fashion way with XML-based configuration, you can make good use of the mvc:resources element to point to the location of resources with a specific public URL pattern.

For example – the following line will serve all requests for resources coming in with a public URL pattern like “/resources/**” by searching in the “/resources/” directory under the root folder in our application.

<mvc:resources mapping="/resources/**" location="/resources/" />

Now, we can access a CSS file as in the following HTML page:

Example 3.1.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">
    <title>Home</title>
</head>
<body>
    <h1>Hello world!</h1>
</body>
</html>

4. The ResourceHttpRequestHandler

Spring 3.1. introduced the ResourceHandlerRegistry to configure ResourceHttpRequestHandlers for serving static resources from the classpath, the WAR, or the file system. We can configure the ResourceHandlerRegistry programmatically inside our web context configuration class.

4.1. Serving a Resource Stored in the WAR

To illustrate this, we'll use the same URL as before to point to myCss.css, but now the actual file will be located in the WAR's webapp/resources folder, which is where static resources should be placed when deploying Spring 3.1+ applications:

Example 4.1.1.

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
          .addResourceHandler("/resources/**")
          .addResourceLocations("/resources/");	
    }
}

Let's analyze the example bit. First, we configure the external-facing URI path by adding defining a resource handler. Then, we map that external-facing URI path internally to the physical path where the resources are actually located.

We can, of course, define multiple resource handlers using this simple yet flexible API.

Now – the following line in an html page would get us the myCss.css resource inside the webapp/resources directory:

<link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">

4.2. Serving a Resource Stored in the File System

Let's say we want to serve a resource stored in the /opt/files/ directory whenever a request comes in for the public URL matching the pattern: /files/**. We simply configure the URL pattern and map it to that particular location on disk:

Example 4.2.1.

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
      .addResourceHandler("/files/**")
      .addResourceLocations("file:/opt/files/");
 }

*(For Windows users: The argument passed to addResourceLocations for this example would be “file:///C:/opt/files/“).

Once we configure the resource location, we can use the mapped URL pattern in our home.html to load an image stored in the file system as follows:

Example 4.2.2.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">
    <title>Home</title>
</head>
<body>
    <h1>Hello world!</h1>
    <img alt="image"  src="<c:url value="files/myImage.png" />">
</body>
</html>

4.3. Configuring Multiple Locations for a Resource

What if we want to look for a resource in more than one location?

We can include multiple locations with the addResourceLocations method. The list of locations will be searched in order until the resource is found. Let's take a look at Example 3.3.1.

Example 4.3.1

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
      .addResourceHandler("/resources/**")
      .addResourceLocations("/resources/","classpath:/other-resources/");
}

The following curl request will display the Hello.html page stored in either the application's webappp/resources or the other-resources folder in the classpath.

curl -i http://localhost:8080/handling-spring-static-resources/resources/Hello.html

5. The New ResourceResolvers

Spring 4.1. provides – with the new ResourcesResolvers – different types of resource resolvers that can be used to optimize browser performance when loading static resources. These resolvers can be chained and cached in the browser to optimize request handling.

5.1. The PathResourceResolver

This is the simplest resolver and its purpose is to find a resource given a public URL pattern. In fact, if no ResourceResolver is added to the ResourceChainRegistration, this is the default resolver.

Let's see an example:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
      .addResourceHandler("/resources/**")
      .addResourceLocations("/resources/","/other-resources/")
      .setCachePeriod(3600)
      .resourceChain(true)
      .addResolver(new PathResourceResolver());
}

Things to notice:

  • We are registering the PathResourceResolver in the resource chain as the sole ResourceResolver in it. See section 4.3. to check how to chain more than one ResourceResolver.
  • The resources served will be cached in the browser for 3600 seconds.
  • The chain is finally configured with the method resourceChain(true).

Now – the HTML code that, in conjunction with the PathResourceResolver, locates the foo.js script in either the webapp/resources of the webapp/other-resources folder:

<script type="text/javascript" src="<c:url value="/resources/foo.js" />">

5.2. The EncodedResourceResolver

This resolver attempts to find an encoded resource based on the Accept-Encoding request header value.

For example, we may need to optimize bandwidth by serving the compressed version of a static resource using gzip content coding.

To configure an EncodedResourceResolver, we just need to configure it in the ResourceChain just as we configured the PathResourceResolver, as in the following line of code:

registry
  .addResourceHandler("/other-files/**")
  .addResourceLocations("file:/Users/Me/")
  .setCachePeriod(3600)
  .resourceChain(true)
  .addResolver(new EncodedResourceResolver());

By default, the EncodedResourceResolver is configured to support br and gzip codings.

So, the following curl request will get the zipped version of the Home.html file located in the file system in the Users/Me/ directory:

curl -H  "Accept-Encoding:gzip" 
  http://localhost:8080/handling-spring-static-resources/other-files/Hello.html

Notice how we are setting the header's “Accept-Encoding” value to gzip – this is important because this particular resolver will only kick in if the gzip content is valid for the response.

Finally, note that, same as before, the compressed version will remain available for the period of time it is cached in the browser – which in this case is 3600 seconds.

5.3. Chaining ResourceResolvers

To optimize resource lookup, ResourceResolvers can delegate the handling of resources to other resolvers. The only resolver that can't delegate to the chain is the PathResourceResolver which should be added at the end of the chain.

In fact, if the resourceChain is not set to true, then by default only a PathResourceResolver will be used to serve resources. In Example 4.3.1. we're chaining the PathResourceResolver to resolve the resource if the GzipResourceResolver is unsuccessful.

Example 5.3.1.

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
      .addResourceHandler("/js/**")
      .addResourceLocations("/js/")
      .setCachePeriod(3600)
      .resourceChain(true)
      .addResolver(new GzipResourceResolver())
      .addResolver(new PathResourceResolver());
}

Now that we have added the /js/** pattern to the ResourceHandler, let's include the foo.js resource located in the webapp/js/ directory in our home.html page as in Example 4.3.2.

Example 5.3.2.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <link href="<c:url value="/resources/bootstrap.css" />" rel="stylesheet" />
    <script type="text/javascript"  src="<c:url value="/js/foo.js" />"></script>
    <title>Home</title>
</head>
<body>
    <h1>This is Home!</h1>
    <img alt="bunny hop image"  src="<c:url value="files/myImage.png" />" />
    <input type = "button" value="Click to Test Js File" onclick = "testing();" />
</body>
</html>

It's worth mentioning that, as of Spring Framework 5.1, the GzipResourceResolver has been deprecated in favor of the EncodedResourceResolver. Therefore, we should avoid using it in the future.

6. Additional Security Configuration

If using Spring Security – it's important to allow access to static resources. We'll need to add the corresponding permissions for accessing the resource URL:

<intercept-url pattern="/files/**" access="permitAll" />
<intercept-url pattern="/other-files/**/" access="permitAll" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/js/**" access="permitAll" />

7. Conclusion

In this article, we have illustrated various ways in which a Spring application can serve static resources.

The XML-based resource configuration is a “legacy” option that we can use if we can't go down the Java configuration route yet.

Spring 3.1. came out with a basic programmatic alternative through its ResourceHandlerRegistry object.

And finally – the new out of the box ResourceResolvers and ResourceChainRegistration object shipped with Spring 4.1. offer resource loading optimization features like caching and resource handler chaining to improve efficiency in serving static resources.

As always, the full example is available over on Github. Additionally, Spring Boot related source codes are also available in this project.

Generic bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
29 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Will
Will
5 years ago

Thanks for this helpful article.
However, I have one question. What will be the easiest and most efficient way to map the url “{variable}/resources/**” request to the location “/{variable}/resources/” folder?

Eugen Paraschiv
5 years ago
Reply to  Will

Hey Will – so, that depends on a couple of things – the location of the folder (file system, classpath, etc), and what {variable} is. What I’d recommend is start simple and look at example 3.3.1 – usually that’s enough to map a folder containing static assets. Cheers,
Eugen.

Adam
Adam
5 years ago

Is there a possibility to pass all the requests to main application folder except /rest/* requests.
In main application folder I have static JS application, but this application need to send requests to oneself but to /rest/* endpoint that I want to be served by Spring MVC.

Eugen Paraschiv
5 years ago
Reply to  Adam

Hey Adam – sure, it’s definitely possible. Keep in mind that all of this configuration starts from the servlet that’s actually handling these requests. So, one way to go is to simply have the static files in a separate servlet and map /rest/* to another servlet. Another way to go, if you do need to share the servlet, is to allow Spring the resolve your requests. As long as you carefully define the mappings to your static artifacts, and of course your /rest/* is mapped to your Controller layer – Spring should be fully capable of resolving both with no… Read more »

Fabrice
Fabrice
4 years ago

Hello, how to access to a static sitemap.xml file or a robort.txt file?

Eugen Paraschiv
4 years ago
Reply to  Fabrice

Hey Fabrice – from the POV of Spring, these are simply 2 files, so the are no special semantics to work with them. Hope that helps. Cheers,
Eugen.

djangofan
djangofan
4 years ago

Is there a way to serve resources in the static folder location in such a way that it is browseable? I am noticing that with the above config, it is not browseable. It would be useful to me during development, but for obvious reasons, not as useful in production deployment.

Eugen Paraschiv
4 years ago
Reply to  djangofan

That’s definitely an interesting question. I’m sure you could configure your app to also server all files out of your static artifacts repo. I see this as a separate, and mostly unrelated thing though – meaning – you could probably configure it but it would be a separate configuration that you need to do.

Kingsley
Kingsley
4 years ago

Please I need your help. I am developing an application where I have to upload images to my file system and also display these images on a web page. I have the images stored in “C:plazaimages” and when I tried to access it using: <img src="” /> it gives a 404 error code. I had followed your instrcution especially example 3.2.1 but to no avail.

Eugen Paraschiv
4 years ago
Reply to  Kingsley

Hey Kingsley, That sounds like a moderately complex scenario, where you have to accommodate both of those usecases in a single location on disk. Now – while it’s hard to say exactly what you’re doing wrong without looking at a working example of the code, I would suggest to debug through the request and the file retrieval process from disk and figure out what’s going on there. That’s going to show you if you’re either using the wrong URL, or if Spring isn’t configured to go to the right directory, or if there’s something else going on. Basically – this… Read more »

Kingsley
Kingsley
4 years ago

Thank you so much for your reply. I had to follow your suggestion and so I found out I wasn’t using the right URL on my page to access the image files. Everything worked perfectly as soon as I used the right URL. Thank you once again.

Eugen Paraschiv
4 years ago
Reply to  Kingsley

Cool, glad everything worked out and happy to help. Cheers,
Eugen.

Comments are closed on this article!