OWASP Java Encoder picks the escaping for the context you name
Contextual output encoding for Java 8+: HTML, JavaScript, CSS, XML and URI components. Zero-dependency core with optional JSP and Jakarta adapters.
At a glance
- What is it?
- The OWASP Java Encoder is a dependency-free library of contextual output encoders for HTML, JavaScript, CSS, XML, URI components and Java string literals, with separate JSP taglib adapters for javax and Jakarta, and a security fix in 1.5.0 that older versions lack.
- Who is it for?
- The OWASP Java Encoder fits any Java 8 or newer service that writes untrusted text into HTML, JavaScript, CSS, XML or a URL component and wants the context chosen explicitly rather than by a template engine default. Do not adopt it below 1.5.0, since the parser-boundary security fixes in 1.5.0 are absent from versions through 1.4.1, and do not expect it to sanitise markup, validate URLs, serialise JSON or parameterise SQL.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The API is organised by the parser that will read your output
Contextual output encoding for Java 8 and newer is the whole idea, and the library's shape follows from it. You choose an encoder for the parser context that receives untrusted text: HTML, JavaScript, CSS, XML or a URL component. The README frames this as one part of XSS prevention, alongside safe templates, URL validation and other application controls, which is the correct framing and worth repeating because it stops the library from being treated as a complete defence.
The context table is the reference you will actually use. HTML text and textarea content use forHtmlContent, and forHtml also covers quoted ordinary text attributes, while a quoted HTML text attribute has its own forHtmlAttribute, with the explicit limit that it is not an event-handler expression and not a URL validator. A single raw URL component uses forUriComponent, where you assemble the URL with trusted delimiters, validate the URL separately, then encode for the enclosing HTML attribute. JavaScript strings use forJavaScript with quotes supplied by the caller, JSON string content uses forJson, CSS strings and url(...) values use forCssString and forCssUrl, and Java source string literals use forJava.
Every listed facade method has both a String and a Writer overload, and the context guide covers nesting, null and Unicode behaviour, JavaScript variants, template boundaries, JSON and the contexts that are unsafe.
The core artifact pulls in nothing at runtime
Three artifacts are published under the group ID org.owasp.encoder, and the split is by target rather than by feature. The core encoder is a String and Writer API with no runtime dependencies, encoder-jsp provides the legacy javax JSP tags and EL functions, and encoder-jakarta-jsp provides the Jakarta equivalents. The Maven coordinates for the core are:
<dependency>
<groupId>org.owasp.encoder</groupId>
<artifactId>encoder</artifactId>
<version>1.6.0</version>
</dependency>You replace encoder with one of the tag adapter artifact IDs when a view layer needs it, and each adapter brings the core in with it, so you do not have to declare both. If you manage the core and the adapter separately, the README asks you to keep their versions aligned, since a mismatch between a taglib and the encoder it calls is exactly the kind of drift that produces an encoding gap in production.
A one-call use of the core looks like this, writing straight to the response writer so no intermediate String is built:
import org.owasp.encoder.Encode;
out.write("<p>");
Encode.forHtmlContent(out, userText); // Writer overload; no intermediate String
out.write("</p>");The String equivalent is Encode.forHtmlContent(userText). The caller supplies the trusted surrounding syntax and the attribute quotes, and the README says to encode raw data once at the output boundary while accounting for any escaping the template engine already performs.
1.5.0 carries security fixes that 1.4.1 and earlier do not
The version history here carries a security note that deserves to be read before anything else, because it changes which version you are allowed to deploy. The parser-boundary security fixes introduced in 1.5.0 remain in effect, and versions through 1.4.1 do not contain those fixes. The repository links the security advisory and a VERIFYING.md file, and the README points at both rather than describing the issue in detail.
That fixes the floor for a new adoption at 1.5.0, and the current release goes further. Version 1.6.0 is available from Maven Central, and the README describes it as a minor release that reduces unnecessary JavaScript letter escaping while preserving recognised HTML-token protection across trusted fragments. The note that encoded bytes and streaming behaviour change is the part to plan around, because a change in encoded output is observable in stored pages, in signatures computed over HTML, and in snapshot tests.
For that reason the release notes and the publication verification document in the releases directory are linked from the README, and the published API documentation for 1.6.0 is on javadoc.io for the core and both adapters, with a warning that hosted indexing can lag publication.
forUri is deprecated, and it never validated a URL anyway
The deprecation of forUri is recent and the guidance is specific. Encode.forUri is deprecated in the released API, and version 1.5.0 extended that deprecation to Encoders.URI, to both ForUriTag classes and to the forUri tag and function documentation. All of those entry points are retained through the 1.x line, and the README is careful about what that means: removing them needs a separately reviewed future-major decision, and deprecation is not a removal schedule.
The reason for the change is a behaviour people relied on by mistake. Encoding a whole URI does not validate it, so forUri with the value javascript:alert(1) returns it unchanged, and existing percent signs are encoded again. The replacement is forUriComponent, which is meant for one raw parameter name or value, one path segment or one fragment. Validating a complete URL is a separate step, and placing the result in a quoted HTML attribute is a third step using forHtmlAttribute.
There is a warning about a common false comfort here: parsing with java.net.URI alone does not establish safety. The worked URL example in the usage documentation is where to see the three steps composed.
The two taglib adapters share a package and must not coexist
A deployment mistake is easy to make here and the README calls it out. Use one of the javax or Jakarta taglib JARs, because they share the org.owasp.encoder.tag package and must not coexist on the same classpath or module path. Both adapters expose the same tag names, so a project that ends up with both on the classpath has an ambiguous resolution rather than a clear error, which is the worse outcome for a security library.
The taglib URIs are identifiers rather than URLs that need to open in a browser, which is worth knowing before you conclude a setup is broken. The Jakarta basic identifier is owasp.encoder.jakarta and the advanced one is owasp.encoder.jakarta.advanced, while the javax JSP identifier is a long www.owasp.org project URL, with the same identifier plus #advanced appended for the advanced variant. A Jakarta page can therefore look like this:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<%@ taglib prefix="e" uri="owasp.encoder.jakarta" %>
<p>${e:forHtmlContent(param.message)}</p>Tags use an empty body and a required value attribute, for example the forHtmlContent tag with a value attribute. In 1.5 the advanced taglibs expose every Encode.forX(String) context except forJava, since Java source generation is not a JSP context, and XML 1.1 bindings are new in 1.5.
What the encoder does not do for you
The list of non-responsibilities is the most useful paragraph in the README, because it is where people over-trust a library like this. Encoding does not sanitise HTML, does not validate input or URLs, does not serialise JSON documents, does not perform SQL parameterisation, and does not decode or canonicalise data. The guidance that follows is direct: use an HTML sanitizer when markup has to be allowed, and use parameterised queries for SQL. For JSON, prefer a serializer for a complete document rather than the string encoder.
Several of the context table entries repeat the same idea from the other direction. forHtmlAttribute is not an event-handler expression and not a URL validator. forJavaScript is never to be used in tagged templates, expression bodies, JSON or script URLs, and ordinary untagged template literal text needs version 1.5 or later. forJson requires double quotes to be supplied by the caller. forXml11Content and forXml11Attribute need an XML 1.1 document and parser rather than HTML, and forCDATA and forXmlComment are not for HTML comments.
The distinction the README draws is between transforming data and validating it. The encoder changes bytes so that a parser reads them as text rather than as structure. Deciding whether a URL is safe, whether a value is acceptable, or whether markup is allowed stays with the application, and the OWASP Java security-library guide is referenced for those separate roles.
Verification, licensing and the boundaries of the project
The repository carries the paperwork that a security project owes its users. There is a SECURITY.md, a MAINTAINERS.md, a KEYS file, a VERIFYING.md, a CHANGELOG.md, a RELEASING.md and a BUILDING.md, plus a compatibility directory holding the runtime matrix and a docs directory with the dependency and license inventory, the context guide and the usage examples. The README points at the security advisory for the 1.5.0 parser-boundary fixes and at a publication verification document for the 1.6.0 Central release, which together let you check what you are actually running against what was published.
The licence is BSD-3-Clause, which is permissive and carries a notice requirement rather than a copyleft obligation, and the dependency inventory exists so you can see what a given artifact pulls in.
Two structural notes. The javax adapter is described as legacy, which suggests new projects should start from the Jakarta one, and the separate historical ESAPI adapter, encoder-esapi, was retired after 1.4.1 and is neither included nor supported in 1.5.x or 1.6.0, with a migration document for applications still using it. The repository also has a section on Java 9 or newer module names and OSGi, and a jakarta-test directory, which is where to look for how the library behaves outside a plain classpath.
Editorial conclusion
The OWASP Java Encoder fits any Java 8 or newer service that writes untrusted text into HTML, JavaScript, CSS, XML or a URL component and wants the context chosen explicitly rather than by a template engine default. Do not adopt it below 1.5.0, since the parser-boundary security fixes in 1.5.0 are absent from versions through 1.4.1, and do not expect it to sanitise markup, validate URLs, serialise JSON or parameterise SQL. Before you wire it in, check three things: that your output context maps to the right facade method, since forHtmlAttribute is not an event-handler expression and forJavaScript is not for tagged templates, that exactly one of the javax or Jakarta taglib JARs is on the classpath, since they share the org.owasp.encoder.tag package and must not coexist, and that you are on 1.6.0, where the reduced JavaScript letter escaping changes encoded bytes and streaming behaviour. The licence is BSD-3-Clause and the last push was on 2026-10-20.
Frequently asked questions
How do I add the OWASP Java Encoder to a Maven project?
Add the core artifact with groupId `org.owasp.encoder`, artifactId `encoder` and version 1.6.0. The core has no runtime dependencies, and the `encoder-jsp` and `encoder-jakarta-jsp` adapters bring the core in with them.
Which version of the OWASP Java Encoder should I use?
Use 1.5.0 or later. The parser-boundary security fixes introduced in 1.5.0 remain in effect, and versions through 1.4.1 do not contain those fixes. The current release is 1.6.0, available from Maven Central.
What is the difference between forUri and forUriComponent?
forUri is deprecated in the released API and never validated a URL, since `forUri("javascript:alert(1)")` returns the value unchanged and existing percent signs are encoded again. forUriComponent is for one raw parameter name or value, path segment or fragment, and complete URLs must be validated separately.
Can I use the javax and Jakarta JSP taglibs at the same time?
No. They share the `org.owasp.encoder.tag` package and must not coexist on the same classpath or module path. Use the javax identifier for a javax container and the Jakarta identifiers for a Jakarta container.
Does the OWASP Java Encoder sanitise HTML or protect against SQL injection?
No. The README states that encoding does not sanitise HTML, validate input or URLs, serialise JSON documents, perform SQL parameterisation, or decode and canonicalise data. Use an HTML sanitizer when markup must be allowed and parameterised queries for SQL.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/owasp-owasp-java-encoder)Was this useful?