HTTP Request

HTTP support has a dedicated DSL, whose entry point is the http(requestName: Expression[String]) method.

This request name is important because it will act as a key when computing stats for the reports. If the same name appears in multiple places in a Simulation, Gatling will consider those requests are of the same type and their statistics will be aggregated.

HTTP requests have to be passed to the exec() method in order to be attached to the scenario and be executed.

// embedded style

// non embedded style
val request = http("RequestName").get("url")


Method and URL

HTTP protocol requires 2 mandatory parameters: the method and the URL.

Gatling provides built-ins for the most common methods. Those are simply the method name in minor case:

  • get(url: Expression[String])
  • put(url: Expression[String])
  • post(url: Expression[String])
  • delete(url: Expression[String])
  • head(url: Expression[String])
  • patch(url: Expression[String])
  • options(url: Expression[String])


These methods are the ones used in REST web services and RESTful applications; thus, such services can be tested with Gatling.

Gatling also supports custom methods (e.g. you can use the method PURGE to purge Nginx cache):

  • httpRequest(method: String, url: Expression[String])

This is how an HTTP request is declared:

// general structure of an HTTP request

// concrete examples
http("Retrieve home page").get("")
http("Nginx cache purge").httpRequest("PURGE", "")

Query Parameters

Frameworks and developers often pass additional information in the query, which is the part of the url after the ?. A query is composed of key=value pairs, separated by &. Those are named query parameters.

For example, contains 2 query parameters:

  • milestone=1 : the key is milestone and its value is 1
  • state=open : the key is state and its value is open


Query parameter keys and values have to be URL encoded, as per RFC3986. Sometimes, HTTP server implementations are very permissive, but Gatling currently isn’t and sticks to the RFC.

In order to set the query parameters of an HTTP request, you can:

  • either pass the full query in the url, e.g.:

    http("Getting issues")
  • or pass query parameters one by one to the method named queryParam(key: Expression[String], value: Expression[Any]), e.g.:

    http("Getting issues")
      .queryParam("milestone", "1")
      .queryParam("state", "open")

Of course, you can use Gatling Expression Language (EL) to make those values dynamic based on data in the virtual user’s session:

http("Value from session example")
  .queryParam("myKey", "${sessionKey}")

If you’d like to specify a query parameter without value, you have to use queryParam("key", ""):

// GET
http("Empty value example")
  .queryParam("myKey", "")

If you’d like to pass multiple values for your parameter, but all at once, you can use multivaluedQueryParam(key: Expression[String], values: Expression[Seq[Any]]):

http("Request with multivaluedQueryParam")
  .multivaluedQueryParam("multi1", "${foo}") // where foo is the name of a Seq Session attribute
  .multivaluedQueryParam("multi2", session => Seq("foo", "bar"))

If you want to add multiple query parameters at once, there are two suitable methods:

  • queryParamSeq(seq: Expression[Seq[(String, Any)]])

    http("Getting issues")
      .queryParamSeq(Seq(("milestone", "1"), ("state", "open")))
  • queryParamMap(map: Expression[Map[String, Any]])

    http("Getting issues")
      .queryParamMap(Map("milestone" -> "1", "state" -> "open"))


As all method parameters are Expression[T], i.e. ‘key’ parameter is an Expression[String] and so on, if you have more specific needs you can also provide an arbitrary Expression[T], i.e. a Session => Validation[T] function. This function will be evaluated against the user session every time this one pass through it. For a deeper look at Expression see dedicated section here.


HTTP protocol uses headers to exchange information between client and server that is not part of the message (stored in the body of the request, if there is one).

Gatling HTTP allows you to specify any header you want to with the header(name: String, value: Expression[String]) and headers(newHeaders: Map[String, String]) methods.

Here are some examples:

// Defining a map of headers before the scenario allows you to reuse these in several requests
val sentHeaders = Map("Content-Type" -> "application/javascript", "Accept" -> "text/html")

http("Custom headers")
  // Adds several headers at once
  // Adds another header to the request
  .header("Keep-Alive", "150")
  // Overrides the Content-Type header
  .header("Content-Type", "application/json")


Headers keys are defined as constants usable in the scenario, for example: HttpHeaderNames.ContentType. You can find a list of the predefined constants here.


There are two handful methods to help you set the required headers for JSON and XML requests:

  • http("foo").get("bar").asJson is equivalent to:

      .header(HttpHeaderNames.ContentType, HttpHeaderValues.ApplicationJson)
      .header(HttpHeaderNames.Accept, HttpHeaderValues.ApplicationJson)
  • http("foo").get("bar").asXml is equivalent to:

      .header(HttpHeaderNames.ContentType, HttpHeaderValues.ApplicationXml)
      .header(HttpHeaderNames.Accept, HttpHeaderValues.ApplicationXml)


Headers can also be defined on the HttpProtocol.

Signature Calculator

You might want to edit the HTTP requests before they’re being sent over the wire, based on other request information: url, headers and/or body. For example, you might want to generate some HMAC header.

This can only happen after Gatling has resolved the request, e.g. computed the body based on a template.

Gatling provides the SignatureCalculator API:

public interface SignatureCalculator {

  void sign(Request request) throws Exception;

request is the mutable object that’s been computed so far. You can typically use its attributes to compute a new header that you will add to the existing headers.

The proper method signature for setting a SignatureCalculator is:

.sign(calculator: Expression[SignatureCalculator])

but you can pass a static SignatureCalculator instead of an Expression and Gatling DSL will automatically lift it for you.

Gatling also provides a built-in for OAuth1

.signWithOAuth1(consumerKey: Expression[String],
                clientSharedSecret: Expression[String],
                token: Expression[String],
                tokenSecret: Expression[String])


You can set the authentication methods at request level with these methods:

  • basicAuth(username: Expression[String], password: Expression[String])
  • digestAuth(username: Expression[String], password: Expression[String])
http("My BASIC secured request").get("http://my.secured.uri").basicAuth("myUser", "myPassword")

http("My DIGEST secured request").get("http://my.secured.uri").digestAuth("myUser", "myPassword")


Authentication can also be defined on the HttpProtocol.

Outgoing Proxy

You can tell Gatling to use a proxy to send the HTTP requests. You can optionally set a different port for HTTPS and credentials:

http("Getting issues")
  .proxy(Proxy("myHttpProxyHost", 8080).httpsPort(8143).credentials("myUsername", "myPassword")) // HTTP proxy
  .proxy(Proxy("mySocks4proxyHost", 8080).socks4) // SOCKS 4 proxy
  .proxy(Proxy("mySocks5proxyHost", 8080).socks5) // SOCKS 5 proxy


Proxy can also be defined on the HttpProtocol.

Virtual Host

You can tell Gatling to override the default computed virtual host with the method virtualHost(virtualHost: Expression[String]):

// GET instead of GET
http("Getting issues")


Virtual Host can also be defined on the HttpProtocol.

HTTP Checks

You can add checks on a request:

http("Getting issues")

For more information, see the HTTP Checks reference section.

For a given request, you can also disable common checks that were defined on the HttpProtocol with ignoreDefaultChecks:

http("Getting issues")


For a given request, you can use disableFollowRedirect, just like it can be done globally on the HttpProtocol:

http("Getting issues")

Url Encoding

Url components are supposed to be urlencoded. Gatling will encode them for you, there might be some corner cases where already encoded components might be encoded twice.

If you know that your urls are already properly encoded, you can disable this feature with .disableUrlEncoding.


See silencing protocol section for more details.

You can then make the request silent:

http("Getting issues")

You might also want to do the exact opposite, typically on a given resource while resources have been globally turned silent at protocol level:

  http("Gatling Logo")

Form Parameters

Requests can have parameters defined in their body. This is typically used for form submission, where all the values are stored as POST parameters in the body of the request.

To add such parameters to a POST request, you must use the method formParam(key: Expression[String], value: Expression[Any]) which is actually the same as queryParam in terms of usage (it has the same signatures).

http("My Form Data")
  .formParam("myKey", "myValue")

As for queryParam you have two methods to add multiple parameters at once:

  • formParamSeq(seq: Expression[Seq[(String, Any)]]):

    http("My Form Data")
      .formParamSeq(Seq(("myKey", "myValue"), ("anotherKey", "anotherValue")))
  • formParamMap(map: Expression[Map[String, Any]]):

    http("My Form Data")
      .formParamMap(Map("myKey" -> "myValue", "anotherKey" -> "anotherValue"))

If you’d like to pass multiple values for your parameter, but all at once, you can use multivaluedFormParam(key: Expression[String], values: Expression[Seq[Any]]):

http("Request with multivaluedFormParam")
  .multivaluedFormParam("multi1", "${foo}") // where foo is the name of a Seq Session attribute
  .multivaluedFormParam("multi2", session => List("foo", "bar"))

The method formParam can also take directly an HttpParam instance, if you want to build it by hand.

  • form(seq: Expression[Map[String, Any]):
http("Request with form")
  .formParam("fieldToOverride", "newValue")

Typically used after capturing a whole form with a form check.

You can override the form field values with the formParam and the likes.


Gatling will automatically set the Content-Type header for you if you didn’t specify one. It will use application/x-www-form-urlencoded except if there’s also some body parts, in which case it will set multipart/form-data.

File Based Request Bodies

Gatling provides various ways of sending files.

When using the bundle distribution, files must be in the user-files/resources directory. This location can be overridden, see Configuration.

When using a build tool such as maven, files must be in src/main/resources or src/test/resources.

Multipart Form

This applies only for POST requests. When you find forms asking for text values and a file to upload (usually an email attachment), your browser will send a multipart encoded request.

To define such a request, you have to add the parameters as stated above, and the file to be uploaded at the same time with the following method: formUpload(name: Expression[String], filePath: Expression[String]).

The Content-Type header will be set to multipart/form-data and the file added in addition to the parameters.

One can call formUpload() multiple times in order to upload multiple files.

http("My Multipart Request")
  .formParam("myKey", "myValue")
  .formUpload("myKey2", "myAttachment.txt")


Gatling will automatically set the Content-Type header to multipart/form-data if you didn’t specify one.


The MIME Type of the uploaded file defaults to application/octet-stream and the character set defaults to the one configured in gatling.conf (UTF-8 by default). Don’t forget to override them when needed. Then, directly use a body part, e.g. .bodyPart(RawFileBodyPart("file", data.xls").contentType("application/").fileName("data.xls")).asMultipartForm.


There is a helpful method to help you deal with multipart form requests: asMultipartForm. It is equivalent to header(HttpHeaderNames.ContentType, HttpHeaderValues.MultipartFormData). If you use formUpload the header is automatically set for you.

Request Body

You can add a full body to an HTTP request with the dedicated method body(body), where body can be:

  • RawFileBody(path: Expression[String]) where path is the location of a file that will be uploaded as is

RawFileBody lets you pass a raw file that will be sent as is. Over regular HTTP, Gatling can optimise sending such a body and directly stream from the file to the socket, without copying in memory. Of course, this optimisation is disabled over HTTPS, as bytes have to be encoded, i.e. loaded in memory.:

// myFileBody.json is a file that contains
// { "myContent": "myHardCodedValue" }
  • ElFileBody(path: Expression[String]) where path is the location of a file whose content will be parsed and resolved with Gatling EL engine

Here, the file content is parsed and turned into a Gatling EL expression. Of course, it can’t be binary.:

// myFileBody.json is a file that contains
// { "myContent": "${myDynamicValue}" }
  • StringBody(string: Expression[String])

Here, you can pass a raw String, a Gatling EL String, or an Expression function.:

.body(StringBody("""{ "myContent": "myHardCodedValue" }""")).asJson

.body(StringBody("""{ "myContent": "${myDynamicValue}" }""")).asJson

.body(StringBody(session => """{ "myContent": """" + someGenerator(session) + """" }""")).asJson
  • ByteArrayBody(bytes: Expression[Array[Byte]])

Here, you can pass bytes instead of text.

  • InputStreamBody(stream: Expression[InputStream])

Here, you can pass a Stream.

  • PebbleFileBody(path: Expression[String])

Gatling Expression Language is definitively the most optimized templating engine for Gatling, in terms of raw performance. However, it’s a bit limited in terms of logic you can implement in there. If you want loops and conditional blocks, you can use Gatling’s Pebble based templating engine.

.body(PebbleStringBody("""{ "myContent": "{% if myCondition %}{{myDynamicValue}}{% endif %}" }""")).asJson

// myFileBody.json is a file that contains
// { "myContent": "{myDynamicValue}" }


When you pass a path, Gatling searches first for an absolute path on the filesystem, then in the classpath.

Note that one can take full advantage of Scala 2.10 macros for writing template directly in Scala compiled code instead of relying on a templating engine. See Scala 2.10 string interpolation and Fastring.

For example:

object Templates {
  val template: Expression[String] = (session: Session) =>
    for {
      foo <- session("foo").validate[String]
      bar <- session("bar").validate[String]
    } yield s"""{ foo: $foo, bar: $bar }"""


For simple use cases, prefer EL strings or based files, for more complex ones where programming capability is required, prefer String interpolation or Fastring.

Multipart Request

You can add a multipart body to an HTTP request and add parts with the dedicated method bodyPart(bodyPart), where bodyPart can be:

  • RawFileBodyPart(path: Expression[String])
  • RawFileBodyPart(name: Expression[String], path: Expression[String])

where path is the location of a file that will be uploaded as is.

Similar to RawFileBody.

  • ElFileBodyPart(path: Expression[String])
  • ElFileBodyPart(name: Expression[String], path: Expression[String])

where path is the location of a file whose content will be parsed and resolved with Gatling EL engine.

Similar to ElFileBody.

  • StringBodyPart(string: Expression[String])
  • StringBodyPart(name: Expression[String], string: Expression[String])

Similar to StringBody.

  • ByteArrayBodyPart(bytes: Expression[Array[Byte])
  • ByteArrayBodyPart(name: Expression[String], bytes: Expression[Array[Byte])

Similar to ByteArrayBody.

Once bootstrapped, BodyPart has the following methods for setting additional optional information:

  • contentType(contentType: String)
  • charset(charset: String), part of of Content-Type header. If not set, defaults to the one from gatling.conf file.
  • fileName(fileName: Expression[String]), part of the Content-Disposition header.
  • dispositionType(contentId: String), part of the Content-Disposition header. If not set, defaults to form-data.
  • contentId(contentId: Expression[String])
  • transferEncoding(transferEncoding: String)
  • header(name: String, value: Expression[String]), let you define additional part headers

Request Body Processor

You might want to process the request body before it’s being sent to the wire.

processRequestBody(processor: Body => Body): takes a Body => Body

Gatling ships two built-ins:

  • gzipBody: compress the request body with GZIP
  • streamBody: turn the body into a stream

Response Transformers

Similarly, one might want to process the response before it’s passed to the checks pipeline:

transformResponse(responseTransformer: (Session => Response) => Validation[Response])

The example below shows how to decode some Base64 encoded response body:

import java.util.Base64
import io.gatling.http.response._
import java.nio.charset.StandardCharsets.UTF_8

  // ignore when response status code is not 200
  .transformResponse {
    (session, response) =>
      if (response.status.code == 200) {
        response.copy(body = new ByteArrayResponseBody(Base64.getDecoder.decode(response.body.string), UTF_8))
      } else {


Gatling allow to fetch resources in parallel in order to emulate the behavior of a real web browser.

At the request level you can use the resources(res: AbstractHttpRequestBuilder[_]*) method.

For example:

http("Getting issues")