§Built-in HTTP filters
Play provides several standard filters that can modify the HTTP behavior of your application. You can also write your own filters in either Java or Scala.
- Configuring gzip encoding
- Configuring security headers
- Configuring CORS
- Configuring CSP
- Configuring allowed hosts
- Configuring Redirect HTTPS filter
§Default Filters
Play now comes with a default set of enabled filters, defined through configuration. If the property play.http.filters
is null, then the default is now play.api.http.EnabledFilters
, which loads up the filters defined by fully qualified class name in the play.filters.enabled
configuration property.
In Play itself, play.filters.enabled
is an empty list. However, the filters library is automatically loaded in SBT as an AutoPlugin called PlayFilters
, and will append the following values to the play.filters.enabled
property:
play.filters.csrf.CSRFFilter
play.filters.headers.SecurityHeadersFilter
play.filters.hosts.AllowedHostsFilter
This means that on new projects, CSRF protection (ScalaCsrf / JavaCsrf), SecurityHeaders and AllowedHostsFilter are all defined automatically.
To append to the defaults list, use the +=
:
play.filters.enabled+=MyFilter
If you have previously defined your own filters by extending play.api.http.DefaultHttpFilters
, then you can also combine EnabledFilters
with your own filters in code:
- Java
-
import play.api.http.EnabledFilters; import play.filters.cors.CORSFilter; import play.http.DefaultHttpFilters; import play.mvc.EssentialFilter; import javax.inject.Inject; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Filters extends DefaultHttpFilters { @Inject public Filters(EnabledFilters enabledFilters, CORSFilter corsFilter) { super(combine(enabledFilters.asJava().getFilters(), corsFilter.asJava())); } private static List<EssentialFilter> combine(List<EssentialFilter> filters, EssentialFilter toAppend) { List<EssentialFilter> combinedFilters = new ArrayList<>(filters); combinedFilters.add(toAppend); return combinedFilters; } }
- Scala
-
import javax.inject.Inject import play.filters.cors.CORSFilter import play.api.http.{ DefaultHttpFilters, EnabledFilters } class Filters @Inject()(enabledFilters: EnabledFilters, corsFilter: CORSFilter) extends DefaultHttpFilters(enabledFilters.filters :+ corsFilter: _*)
Otherwise, if you have a Filters
class in the root or have play.http.filters
defined explicitly, it will take precedence over the EnabledFilters
functionality described below.
§Testing Default Filters
Because there are several filters enabled, functional tests may need to change slightly to ensure that all the tests pass and requests are valid. For example, a request that does not have a Host
HTTP header set to localhost
will not pass the AllowedHostsFilter and will return a 400 Forbidden response instead.
§Testing with AllowedHostsFilter
Because the AllowedHostsFilter filter is added automatically, functional tests need to have the Host HTTP header added.
If you are using FakeRequest
or Helpers.fakeRequest
, then the Host
HTTP header is added for you automatically. If you are using play.mvc.Http.RequestBuilder
, then you may need to add your own line to add the header manually:
Http.RequestBuilder request = new Http.RequestBuilder()
.method(GET)
.header(Http.HeaderNames.HOST, "localhost")
.uri("/xx/Kiwi");
§Testing with CSRFFilter
Because the CSRFFilter filter is added automatically, tests that render a Twirl template that includes CSRF.formField
, i.e.
@(userForm: Form[UserData])(implicit request: RequestHeader, m: Messages)
<h1>user form</h1>
@request.flash.get("success").getOrElse("")
@helper.form(action = routes.UserController.userPost()) {
@helper.CSRF.formField
@helper.inputText(userForm("name"))
@helper.inputText(userForm("age"))
<input type="submit" value="submit"/>
}
must contain a CSRF token in the request. In the Scala API, this is done by importing play.api.test.CSRFTokenHelper._
, which enriches play.api.test.FakeRequest
with the withCSRFToken
method:
import org.specs2.mutable.Specification
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.test.CSRFTokenHelper._
import play.api.test.Helpers._
import play.api.test.{ FakeRequest, WithApplication }
class UserControllerSpec extends Specification {
"UserController GET" should {
"render the index page from the application" in new WithApplication() {
val controller = app.injector.instanceOf[UserController]
val request = FakeRequest().withCSRFToken
val result = controller.userGet().apply(request)
status(result) must beEqualTo(OK)
contentType(result) must beSome("text/html")
}
}
}
In the Java API, this is done by calling CSRFTokenHelper.addCSRFToken
on a play.mvc.Http.RequestBuilder
instance:
Http.RequestBuilder request = new Http.RequestBuilder()
.method(POST)
.uri("/xx/Kiwi");
request = CSRFTokenHelper.addCSRFToken(request);
§Disabling Default Filters
The simplest way to disable a filter is to add it to the play.filters.disabled
list in application.conf
:
play.filters.disabled+=play.filters.hosts.AllowedHostsFilter
This may be useful if you have functional tests that you do not want to go through the default filters.
To remove the default filters, you can set the entire list manually:
play.filters.enabled=[]
If you want to remove all filter classes, you can disable it through the disablePlugins
mechanism:
lazy val root = project.in(file(".")).enablePlugins(PlayScala).disablePlugins(PlayFilters)
If you are writing functional tests involving GuiceApplicationBuilder
, then you can disable all filters in a test by calling configure
:
class UserControllerWithoutFiltersSpec extends Specification {
"UserControllerWithoutFiltersSpec GET" should {
"render the index page from the application" in new WithApplication(
GuiceApplicationBuilder().configure("play.http.filters" -> "play.api.http.NoHttpFilters").build()
) {
val controller = app.injector.instanceOf[UserController]
val request = FakeRequest().withCSRFToken
val result = controller.userGet().apply(request)
status(result) must beEqualTo(OK)
contentType(result) must beSome("text/html")
}
}
}
§Compile Time Default Filters
If you are using compile time dependency injection, then the default filters are resolved at compile time, rather than through runtime.
This means that the play.api.BuiltInComponents
trait (for Scala) and play.BuiltInComponents
interface (for Java) now contains an httpFilters
method which is left abstract. The default list of filters is defined in play.filters.HttpFiltersComponents
for Scala and play.filters.components.HttpFiltersComponents
for Java. So, for most cases you will want to mixin HttpFiltersComponents
and append your own filters:
- Java
-
import play.ApplicationLoader; import play.BuiltInComponentsFromContext; import play.filters.components.HttpFiltersComponents; import play.mvc.EssentialFilter; import play.routing.Router; import java.util.ArrayList; import java.util.List; public class MyAppComponents extends BuiltInComponentsFromContext implements HttpFiltersComponents { public MyAppComponents(ApplicationLoader.Context context) { super(context); } @Override public List<EssentialFilter> httpFilters() { List<EssentialFilter> combinedFilters = new ArrayList<>(HttpFiltersComponents.super.httpFilters()); combinedFilters.add(new LoggingFilter(materializer())); return combinedFilters; } @Override public Router router() { return Router.empty(); // implement the router as needed } }
- Scala
-
import akka.util.ByteString import play.api.{ApplicationLoader, BuiltInComponentsFromContext, NoHttpFiltersComponents} import play.api.libs.streams.Accumulator import play.api.mvc.{EssentialAction, EssentialFilter, RequestHeader, Result} import play.api.routing.Router import play.filters.csrf.CSRFFilter class MyAppComponents(context: ApplicationLoader.Context) extends BuiltInComponentsFromContext(context) with play.filters.HttpFiltersComponents { lazy val loggingFilter = new LoggingFilter() override def httpFilters: Seq[EssentialFilter] = { super.httpFilters :+ loggingFilter } override def router: Router = Router.empty // implement the router as needed }
If you want to filter elements out of the list, you can do the following:
- Java
-
public class MyAppComponents extends BuiltInComponentsFromContext implements HttpFiltersComponents { public MyAppComponents(ApplicationLoader.Context context) { super(context); } @Override public List<EssentialFilter> httpFilters() { return HttpFiltersComponents.super.httpFilters() .stream() .filter(filter -> !filter.getClass().equals(CSRFFilter.class)) .collect(Collectors.toList()); } @Override public Router router() { return Router.empty(); // implement the router as needed } }
- Scala
-
class MyAppComponents(context: ApplicationLoader.Context) extends BuiltInComponentsFromContext(context) with play.filters.HttpFiltersComponents { override def httpFilters: Seq[EssentialFilter] = { super.httpFilters.filterNot(_.getClass == classOf[CSRFFilter]) } override def router: Router = Router.empty // implement the router as needed }
§Disabling Compile Time Default Filters
To disable the default filters, mix in play.api.NoHttpFiltersComponents
for Scala and play.filters.components.NoHttpFiltersComponents
for Java:
- Java
-
public class MyAppComponents extends BuiltInComponentsFromContext implements NoHttpFiltersComponents { public MyAppComponents(ApplicationLoader.Context context) { super(context); } // no need to override httpFilters method @Override public Router router() { return Router.empty(); // implement the router as needed } }
- Scala
-
class MyAppComponents(context: ApplicationLoader.Context) extends BuiltInComponentsFromContext(context) with NoHttpFiltersComponents { override def router: Router = Router.empty // implement the router as needed }
Both Scala play.api.NoHttpFiltersComponents
and play.filters.components.NoHttpFiltersComponents
have httpFilters
method which returns an empty list of filters.