Edit on GitHub << back to Core Developers

# Static Content

Struts can serve a static content like CSS and JavaScript files using a predefined path. By default, these resources are served using `/static` path defined using a constant `struts.ui.staticContentPath` - see below for more details.

Please remember to include this path in your filter mapping if you use a custom mapping, see web.xml example config.

## Disabling static content

You can disable this feature by setting the following constant to `false`. Once disabled you must provided the required CSS & JavaScript files on your own, which can be a good thing when you want to use a CDN.

    <constant name="struts.serve.static" value="false"/>

> If you disable this feature, but you use the `xhtml`, or `css_xhtml` theme, make sure the JavasScript and CSS files shipped inside the core jar are extracted to your web application directory or served in some other way.

## Custom Static Content Loaders

Static content is served by an implementation of `org.apache.struts2.dispatcher.StaticContentLoader`. To write your own `StaticContentLoader`, implement `StaticContentLoader` and define a bean for the class:

    <bean type="org.apache.struts2.dispatcher.StaticContentLoader" class="MyStaticContentLoader" name="myLoader"/>
    <constant name="struts.staticContentLoader" value="myLoader"/>

## Default Content Loader

The Apache Struts provides a default implementation of `StaticContentLoader` which is `org.apache.struts2.dispatcher.DefaultStaticContentLoader`. This loader will handle urls that start with “/static/” by default.

This content loader can serve static content from the classpath, so when writing a plugin, you can put a file inside your plugin’s jar like “/static/image/banner.jpg” and it will be served when the url “/static/image/banner.jpg” is requested.

> This loader is not optimized to handle static content, and to improve performance, it is recommended that you extract your static content to the web application directory, and let the container handle them.

## Default path

If needed you can change the default path at which static content is served. Just define a new `constant` in your `struts.xml` with a path as below:

    <constant name="struts.ui.staticContentPath" value="/my-static-content"/>

This value is also used by the Default Content Loader.

## WebJars support

Available since Struts 7.3.0.

WebJars package client-side libraries (Bootstrap, jQuery, …) as JARs, shipping their assets under `META-INF/resources/webjars/<name>/<version>/…`. Instead of vendoring these files into your web application and re-committing them on every upgrade, you add the WebJar as a regular dependency and let Struts resolve and serve it.

Struts serves WebJar assets through the same static content pipeline described above, under `<staticContentPath>/webjars/**` (by default `/static/webjars/**`). Resolution is version-less: you reference a resource by its logical path and Struts resolves the version present on the classpath. For example, a request for:

    /static/webjars/bootstrap/css/bootstrap.min.css

is served from `META-INF/resources/webjars/bootstrap/5.3.8/css/bootstrap.min.css` (whichever version is on the classpath). To emit these URLs from a template without hardcoding the version, use the webjar tag:

    <link rel="stylesheet" href="<s:webjar path="bootstrap/css/bootstrap.min.css"/>"/>

Struts resolves versions using webjars-locator-lite, which is bundled with `struts2-core`.

### Configuration

Constant | Default | Description
--- | --- | ---
`struts.webjars.enabled` | `true` | Master switch for resolving and serving WebJar assets under `<staticContentPath>/webjars/**`.
`struts.webjars.allowlist` | (empty) | Optional comma-separated allowlist of WebJar names. When empty, all WebJars on the classpath are allowed.

To restrict serving to specific WebJars:

    <constant name="struts.webjars.allowlist" value="bootstrap,jquery"/>

To disable the feature entirely:

    <constant name="struts.webjars.enabled" value="false"/>

### Security

Resolution is hard-constrained to the `META-INF/resources/webjars/` root: path traversal attempts (`..`, `.`, backslashes) are rejected before resolution, the resolved path is re-checked for containment, and only paths that the locator can resolve to a real WebJar are served. Disabled, unresolved, traversal, and allowlist-blocked requests all fail closed - a `404` when serving and empty output when building URLs.

### Customizing resolution

URL resolution is provided by the `org.apache.struts2.webjars.WebJarUrlProvider` container bean (default implementation `DefaultWebJarUrlProvider`), which exposes `resolveResourcePath(String)` and `resolveUrl(String, HttpServletRequest)`. Plugins and applications can replace it by declaring their own bean:

    <bean type="org.apache.struts2.webjars.WebJarUrlProvider" class="com.example.MyWebJarUrlProvider" name="myProvider"/>
    <constant name="struts.webjars.urlProvider" value="myProvider"/>

## Preventing Struts from handling a request

If there is a request that Struts is handling as an action, and you wish to make Struts ignore it, you can do so by specifying a comma separated list of regular expressions like:

    <constant name="struts.action.excludePattern" value="/some/content/.*,/other/content/.*"/>

These regular expression will be evaluated against the request’s URI (`HttpServletRequest.getRequestURI()`), and if any of them matches, then Struts will not handle the request.

To evaluate each pattern the `Pattern` class from JDK will be used, you can find more about what kind of pattern you can use in the Pattern class JavaDoc.

Since Struts 6.1.0 you can use a custom separator. By default, the provided patterns are split using comma `,`, but it can happen that you want to use comma in your patterns as well, e.g.: `/static/[a-z]{1,10}.json`. In such case you can define a custom separator to be used to split the patterns, use `struts.action.excludePattern.separator` constant:

    <constant name="struts.action.excludePattern.separator" value="//"/>
    <constant name="struts.action.excludePattern" value="/some/[a-zA-Z]{1,10}.json///other/content/.*"/>

Follow @x