Apache JMeter Part 3: Testing Website Performance


We've already described most of JMeter in part 1 and part 2, but let's summarize what we've learned so far. We've seen that JMeter consists of the following elements:

  • Thread Group: A mandatory element that specifies the number of concurrent users that will use the target application.
  • Sampler: Sends requests to the server and waits for a response.
  • Logical Controller: Specifies when to send requests from samplers.
  • Assertion: Uses a regular expression to group interesting strings from the responses.
  • Pre-processor: Executes some actions before a request is sent.
  • Post-processor: Executes some actions after a response is received.
  • Listener: Listens for responses, saves and analyzes them.
  • Timer: Can be used to delay sending of requests between two subsequent requests.
  • Configuration Element: Can be used to add or modify requests, but cannot send requests.

Testing Website Performance

Let's pretend for a second that we're doing a penetration test and our customer wants us to do a performance analysis of a specific web page. We know that the web page runs on both HTTP and HTTPS, which we must test. When testing the performance of any kind of target application, we must ask ourselves the following questions [1]:

  • What is our anticipated average number of users (normal load)?
  • What is our anticipated peak number of users?
  • When is a good time to load-test our application (i.e. off-hours or weekends), bearing in mind that this may very well crash one or more of our servers?
  • Does our application have a state? If so, how does our application manage it (cookies, session-rewriting, or some other method)?
  • What is the testing intended to achieve?

Intercepting Requests with Burp

First we must intercept the requests we would like to send with JMeter with Burp. This isn't strictly necessary, it's just an easier way to imagine what we'll need to set in the JMeter.

A request can be as simple as something like the following:

GET / HTTP/1.1

Or it can also contain a cookie, like this:

GET / HTTP/1.1
Cookie: PHPSESSID=a101scfn1qhkb2kg0ivr0a0i67

Or maybe some other HTTP headers as well:

GET / HTTP/1.1
User-Agent: Opera/9.80
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: en
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=a101scfn1qhkb2kg0ivr0a0i67

After all, the following request should be sent if we authenticated against a web site (for better formatting, a blank line between the POST and arguments was intentionally removed):

POST /login HTTP/1.1

We must know what we would like to do when testing the performance of a web page and set JMeter accordingly.

Intercepting Responses with Burp

Requests are only half of the story. We must also take a look at responses. If nothing else, we would at least like to know what we've gotten back from the web server, if anything at all.

The most simple response can be something like:

HTTP/1.1 200 OK
Server: nginx/0.7.59
Date: Fri, 31 Aug 2012 16:41:44 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Content-Length: 112538

But a response can also return a Cookie when we've successfully authenticated:

HTTP/1.1 302 Found
Set-Cookie: uid=895376952; expires=Wed, 02-Apr-2014 09:23:40 GMT; path=/
Content-Length: 0

The Thread Group

First we must add a Thread Group, which can be seen in the picture below:


We have to set the following options: * Number of Threads: The number of simultaneous users that will visit the web page in a configured time frame. * Ramp-Up Period: How much time to wait after starting each thread (user) and before starting a new one; how much time to take to start up all the threads/users. If the ramp-up period is 10 seconds and we have 1000 threads, then all 1000 threads need to be started by the end of 10 seconds, which means that JMeter will start the next thread 10/1000 = 0.01 seconds after it started the previous thread. * Loop Count: How many times to repeat the whole test.

Here we must think about what we would like to achieve. We would like to test the performance of a web page, but how? The best way is to simulate a different number of users and actually perform a couple of tests. We can test the performance of a web site if we use the following number of users:

#Users Ramp-Up Period
100 unknown
1000 unknown
5000 unknown
20000 unknown

We can see that the ramp-up period is unknown at a time, but we'll measure the best ramp-up period for the 1000 number of users in part 4 of this series. For now, let's look at how to configure sending requests and receiving responses with JMeter.

Adding JMeter Elements

After we've decided how many users we would like to simulate, it's time to determine what JMeter elements we need. We'll take a look at an example request/response that we would like to send to the target web site for processing.

Sending the Request

First, let's take a look at the request that's presented in the picture below:


We can see that different parts of the request are colored differently. Each of the different colored parts of the request is represented by a different element in JMeter. This is because we can't really present the whole request with one element in JMeter; this was done for usability and extendability in mind, so that JMeter can be used to assemble even the most complicated requests.

It's about time to present the elements we have to use to assemble the above requests. The needed elements are:

HTTP Request Defaults (color: pink)

This element can be added by right-clicking on the Thread Group and selecting: Add – Config Element – HTTP Request Defaults. This element specifies the default settings that each HTTP request will be using. It is here that we specify the target address where we'll be sending our requests. This can be seen in the picture below:


Notice how we set the "Server Name or IP" to http://www.website.com, which is part of our request. We also specified the "Path" setting, which specifies the resource that we're requesting from the target web site. We also remembered to define the appropriate HTTP headers. But when running the above configuration we receive the following error:

<httpSample t="0" lt="0" ts="0" s="false" lb="Error: " rc="Non HTTP response code: java.lang.IllegalArgumentException" rm="Non HTTP response message: URI can&apos;t be null." tn="Users 1-1" dt="text" by="1141"/>

But why? It's because we specified the "http://" in the server name; we should leave the http:// part out and specify only the hostname or IP. The picture below contains the correctly specified "Server Name or IP":


This element simply defines the values that the HTTP request elements use and does not in any way send the actual request.

HTTP Authorization Manager (color: green)

This is an element which will be used to authenticate the user when a /login path is requested. The configuration of this element can be seen in the picture below:


HTTP Request (color: blue)

After all is set-up, we must of course send the request to the target application. To do that we need to right-click on Thread Group and select: Add – Sampler – HTTP Request. The request looks like the picture below:


We can see a lot of options that we can set, which are very similar to the "HTTP Request Defaults" element. We have to change the Path, which indicates what resource we'll be requesting. Note that we don't have to specify the "Server Name or IP", because if not given, it's taken from the "HTTP Request Defaults" element. We can add as many HTTP Request elements as we wish and all requests will be sent in turn one after the other.

Receiving the Response

Now we've successfully written our request. What's left is the response. The response can be represented by the picture below:


To construct a response, we need the following elements:

HTTP Request Defaults (color: pink)

We already described this element when defining a request, so we won't repeat ourselves.

Graph Results Listener (color: blue)

This elements receives the response of the request, stores it, analyzes it, and presents the findings in a graph. We can add this listener by right-clicking on the Thread Group and selecting: Add – Listener – Graph Results. We can optionally specify the file where to save all the responses for future analysis, but this field isn't necessary.

The picture below presents a graph of a test plan, where we simulated 1000 users which accessed the default /index.html resource on www.infosecinstitute.com (with ramp-up period 10):


At the bottom of the picture there are the following statistics, represented in colors:

  • Black: The total number of samples sent.
  • Blue: Average of all samples sent.
  • Red: Standard deviation.
  • Green: Throughput rate, that represents the number of requests per minute the server handled. This number takes into account all delays we added to the test plan. We can lower/raise the number of simulated threads to find out the maximum throughput the server can handle.


We've seen what elements we need to set when sending a request and receiving a response: this should be enough to perform the performance test of any server.


[1] Help! My boss wants me to load test our web app, http://jmeter.apache.org/usermanual/boss.html.