HTTP Sessions, Headers, Cookies & Authentication
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. |
The reference material consulted while preparing these pages is listed in the section bibliography.
This page was written against Apache JMeter 5.6.3 (latest release on 2026-10-14; it runs on Java 8 and later, Java 17 or
later is recommended), with the differences planned for JMeter 6.0 noted where they matter. JMeter 6.0 is not
released: the planned changes come from xdocs/changes.xml on the master branch, need Java 17 or later (Java 21
recommended) and are described as planned, not as shipped behaviour. No plugin is needed: the four managers and the URL
Re-writing Modifier ship with core JMeter. The HTTP Request sampler implementations discussed are the two that remain in
5.6.3, HttpClient4 (Apache HttpComponents 4.5.x) and Java. The books on the market target JMeter 2.11 to 3.0; the
managers they describe still exist, but defaults such as the cookie policy and pre-emptive authentication have changed
since (listed at the end of the page). Every field name, property and rule in this page is taken from the official
component reference, properties reference and user manual, and, where the documentation is ambiguous, from the 5.6.3
source code of the Apache JMeter project.
A browser keeps state between requests without being asked: it stores the cookies a server sets and sends them back, it remembers which resources it already has, it adds the same headers to every request and it answers authentication challenges. JMeter does none of that unless the test plan says so. An HTTP Request sampler on its own sends exactly what it is configured with, and every thread starts from nothing. The managers are configuration elements that give each virtual user that browser-like memory: the HTTP Header Manager (headers), the HTTP Cookie Manager (cookies), the HTTP Cache Manager (conditional and cached GETs) and the HTTP Authorization Manager (server authentication). For applications that carry the session in the URL instead of a cookie, the HTTP URL Re-writing Modifier does the same job.
This page describes each element field by field, then explains how several of them in scope are combined, and ends with a complete worked example for the Bookshelf API: log in once per virtual user, extract the JWT from the login response and send it as a bearer token on every later request. It builds on Test Plan Elements, Execution Order & Scoping and Properties, Variables & Functions, and pairs with HTTP Requests. The OAuth and JWT background (what a bearer token is, how it is obtained and refreshed) is in the OAuth Reference and is not repeated here.
What "a session" means for a JMeter thread
Each JMeter thread is one virtual user, and everything a manager stores is stored per thread. The table lists the pieces of state a real browser session has and the element that reproduces each one.
| Browser behaviour | JMeter element | Per thread? |
|---|---|---|
Sends the same headers on every request ( |
HTTP Header Manager |
The header definitions are shared by all threads; the values are resolved per thread when they contain variables
such as |
Stores cookies set by the server and sends them back |
HTTP Cookie Manager |
Yes: each thread has its own cookie storage area. Cookies typed into the manager’s table are shared by all threads. |
Reuses cached responses and sends |
HTTP Cache Manager |
Yes: each thread has its own cache (5000 entries by default). |
Answers a |
HTTP Authorization Manager |
The credential table is shared; use variables to give each thread its own user. Kerberos subjects are kept per thread. |
Keeps a session id in the URL ( |
HTTP URL Re-writing Modifier |
Yes: the id is read from each thread’s previous response. |
Remembers a token returned in a JSON body |
A post-processor (JSON Extractor) plus a variable |
Yes: variables are per thread. |
Bearer tokens such as a JWT are not managed by any manager: they are ordinary values extracted from a response into a variable and sent back through a Header Manager. That combination is the worked example at the end of this page.
The managers are not samplers: they never send a request. A sampler that has a manager in scope asks it for headers, cookies, cache entries or credentials just before it sends the request, and hands it the response afterwards:
Where the managers sit in the test plan
All four managers are configuration elements, added with a right-click on the Thread Group (or on a controller or a sampler) and Add → Config Element → HTTP Header Manager (or HTTP Cookie Manager, HTTP Cache Manager, HTTP Authorization Manager). The HTTP URL Re-writing Modifier is a pre-processor: Add → Pre Processors → HTTP URL Re-writing Modifier. They follow the normal scoping rules: an element applies to every sampler in the branch it is placed in. The user manual’s advanced web test plan chapter recommends adding the Header Manager, like the Cookie Manager, at Thread Group level unless different requests need different headers.
The skeleton used on every HTTP page of this section:
Test Plan bookshelf-load.jmx
├── HTTP Request Defaults (${__P(host,localhost)}:${__P(port,8080)})
└── Thread Group "Borrowers" (${__P(threads,1)} threads, ${__P(rampup,1)} s ramp-up)
├── HTTP Header Manager Accept and Content-Type: application/json
├── HTTP Cookie Manager one per Thread Group
├── HTTP Cache Manager only when caching behaviour matters
├── CSV Data Set Config users.csv: username,password
├── Once Only Controller "Login once per user"
│ └── POST /api/auth/login + JSON Extractor (token)
└── Simple Controller "Authenticated journey"
├── HTTP Header Manager Authorization: Bearer ${token}
├── GET /api/books?query=...
├── GET /api/books/{id}
├── POST /api/loans
└── GET /api/loans/{id}
HTTP Header Manager
The Header Manager lets a plan add or override HTTP request headers. Typical uses for an API test are the content
negotiation headers (Accept: application/json and Content-Type: application/json for JSON bodies), an
Authorization: Bearer ... header built from an extracted token, correlation or tenant headers expected by the
service, and a realistic User-Agent.
Fields
| Field | Meaning | Required |
|---|---|---|
Name |
Descriptive name shown in the tree. |
No |
Name (Header) |
Name of the request header. Matching between managers is case-insensitive (see below). |
No (at least one row is expected) |
Value |
Request header value. Variables and functions are allowed and resolved for each request in the context of the sampler that uses the manager (the functions reference states that config elements referenced by samplers, such as the Header and Cookie managers, resolve functions in the context of the HTTP sampler). |
No (at least one row is expected) |
The table is titled Headers Stored in the Header Manager and has five buttons:
| Button | Action |
|---|---|
Add |
Adds an empty row. |
Add from Clipboard |
Splits the clipboard into lines and each line on the first |
Delete |
Deletes the selected row. |
Load |
Loads a previously saved header table and adds its rows to the existing ones. |
Save |
Saves the header table to a file. |
In the .jmx file each row is a Header element:
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="JSON headers">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Accept</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
Several Header Managers: merged in 5.6.3
The official documentation contradicts itself here. The Elements of a Test Plan chapter of the user manual says that the Header Manager, Cookie Manager and Authorization Manager are not merged: if more than one is in scope only one is used, with no way to say which. The HTTP Header Manager entry of the component reference says that JMeter supports multiple Header Managers, that their entries are merged to form the list for the sampler, and that an entry whose name matches an existing header replaces it.
The component reference describes the 5.6.3 behaviour. In the 5.6.3 source, the HTTP sampler merges every Header
Manager it receives into the one it already has (HeaderManager.merge), while for the Cookie, Cache and Authorization
managers it keeps only one and logs a warning that the existing manager was superseded. The configuration elements are
handed to the sampler starting with the one closest to it, and the merge keeps a header that is already present, so in
practice:
-
every header name from every Header Manager in scope is sent once;
-
when two managers define the same header name (compared case-insensitively), the manager closest to the sampler wins, so a sampler-level manager overrides a Thread Group-level one, exactly like a field of HTTP Request Defaults;
-
a header with an empty value in the closer manager does not remove the header: it replaces its value with an empty one, as the component reference warns.
The test-plan chapter sentence is therefore accurate for the Cookie and Authorization managers but out of date for the
Header Manager. As on the
scoping page,
keep the layering simple (one Header Manager per scope) and confirm the headers that were really sent in the Request
tab of View Results Tree, which shows the request headers, or in the jmeter.log debug output of the sampler.
A layered example for Bookshelf. The Thread Group manager sets the JSON headers and an X-Client header; the manager
under "Authenticated journey" adds the bearer token and overrides X-Client:
| Header | Thread Group manager | "Authenticated journey" manager | Sent by GET /api/books |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Cost and pitfalls
The Header Manager is cheap: the headers are a short list resolved per request, and a merge copies that list. The practical problems are about correctness rather than load:
-
An unresolved variable is sent literally. If
${token}has not been set yet (the login failed, or the header is in scope of the login request itself), JMeter sends the textBearer ${token}. The server answers401, which looks like a server problem in the report. Scope the Authorization header so that the login request does not get it, give the JSON Extractor a recognisable default value, and assert on the login response. -
Content-Typefor files and forms. A Thread Group-wideContent-Type: application/jsonalso reaches multipart uploads and form posts, where the sampler builds its own content type. Override it in a closer manager for those samplers or keep the JSON content type at the level of the samplers that send JSON. -
Do not copy browser headers blindly. Recorded plans carry
Cookie,Authorizationand caching headers in Header Managers. Those values belong to the recording session; the Cookie and Cache managers and an extracted token must produce them at run time instead. The recording page later in this guide explains which headers the recorder drops. -
Secrets in the plan. A literal token or API key in a Header Manager is saved in the
.jmxfile. Read it from a property (${__P(apiKey)}, set with-JapiKey=...on the command line) or from a CSV file instead.
HTTP Cookie Manager
The Cookie Manager has two functions. First, it stores and sends cookies like a browser: when a response contains a cookie, the manager stores it and sends it on later requests to the same site. Each thread has its own cookie storage area, so a site that keeps the session in a cookie gets one session per virtual user. Cookies received this way do not appear in the manager’s table; they are visible in the request and response headers in View Results Tree. Second, a plan can type cookies into the manager by hand; those cookies are shared by every thread and are created with an expiry time far in the future.
The manager checks that a received cookie is valid for the URL that set it, so cross-domain cookies are not stored. Cookies with null (empty) values are ignored, and a cookie name must be unique in the table: a second row with the same name replaces the first.
Fields
| Field | Meaning | Required |
|---|---|---|
Name |
Descriptive name shown in the tree. |
No |
Clear cookies each iteration? |
If selected, all server-defined cookies are cleared each time the main Thread Group loop starts a new iteration. Cookies defined in the GUI table are not cleared. Disabled when the next option is selected. |
Yes |
Use Thread Group configuration to control cookie clearing |
Delegates the decision to the Thread Group’s Same user on each iteration option: cookies are cleared at the start of an iteration only when that option is unchecked. See Clearing state between iterations. |
No |
Cookie Policy |
The cookie specification used to parse and match cookies. |
Yes |
Implementation |
|
Yes |
User-Defined Cookies |
Table of hard-coded cookies used by all threads, with columns Name, Value, Domain (the host name without
|
No |
The table has Add, Delete, Load (adds the cookies of a saved file to the table) and Save As (saves the table, not cookies received from responses) buttons.
The cookie policies offered by HC4CookieHandler in 5.6.3 map onto the Apache HttpClient 4.5 cookie specifications:
| Policy | Behaviour |
|---|---|
|
RFC 6265, relaxed parsing: the right choice for current servers. |
|
RFC 6265, strict parsing of attributes such as |
|
HttpClient’s |
|
The original Netscape draft specification. |
|
The obsolete cookie RFCs. |
|
Accepts and sends no cookies. |
Source: HTTP Cookie Manager (Component Reference);
the policy list is from the 5.6.3 source of HC4CookieHandler in apache/jmeter
Properties
The Cookie Manager is tuned by JMeter properties, set in user.properties or with -J on the command line:
| Property | Default | Meaning |
|---|---|---|
|
|
Store received cookies as thread variables. |
|
|
Prefix added to the cookie name to form the variable name. Set it to one or more spaces to use no prefix. |
|
|
Check that a received cookie is valid for the URL before storing it. Set to
|
|
|
Delete cookies with null or empty values (this also removes such rows from the GUI table when it is refreshed). |
|
|
Whether variable cookies are allowed ( |
|
|
Name of a Netscape-format cookie file. |
Cookie values as variables
With CookieManager.save.cookies=true, every cookie a thread receives is also stored as a variable named
COOKIE_<name>: a cookie named SESSION is available as ${COOKIE_SESSION}. The prefix prevents a cookie from
silently overwriting a variable of the plan. COOKIE_cookiename is listed among the variables JMeter defines
internally. This is the simple way to echo a cookie into a header, for example a CSRF token that the server sets as a
cookie and expects back in a header:
# user.properties
CookieManager.save.cookies=true
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="CSRF header">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">X-CSRF-TOKEN</stringProp>
<stringProp name="Header.value">${COOKIE_CSRF_TOKEN}</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
A Cookie Manager in the plan
The standard arrangement is a single Cookie Manager directly under each Thread Group, with the standard policy:
<CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager">
<collectionProp name="CookieManager.cookies"/>
<boolProp name="CookieManager.clearEachIteration">false</boolProp>
<boolProp name="CookieManager.controlledByThreadGroup">true</boolProp>
<stringProp name="CookieManager.policy">standard</stringProp>
<stringProp name="CookieManager.implementation">org.apache.jmeter.protocol.http.control.HC4CookieHandler</stringProp>
</CookieManager>
<hashTree/>
A user-defined cookie, for example a feature flag cookie expected by every request to localhost, is a Cookie
element in CookieManager.cookies:
<collectionProp name="CookieManager.cookies">
<elementProp name="feature" elementType="Cookie" testname="feature">
<stringProp name="Cookie.value">new-search</stringProp>
<stringProp name="Cookie.domain">localhost</stringProp>
<stringProp name="Cookie.path">/</stringProp>
<boolProp name="Cookie.secure">false</boolProp>
<longProp name="Cookie.expires">0</longProp>
<boolProp name="Cookie.path_specified">true</boolProp>
<boolProp name="Cookie.domain_specified">true</boolProp>
</elementProp>
</collectionProp>
Cost, pitfalls and the 6.0 change
Memory grows with the number of threads times the cookies each thread holds; for an API that sets one or two cookies this is negligible. The points that matter are:
-
No Cookie Manager, no session. Without one, a cookie-based login works for the login request and every later request is anonymous. Most "everything after login fails" reports in a new plan come from a missing Cookie Manager.
-
Hand-typed cookies are global. A session cookie copied from a browser into the table makes every thread share one session, which is neither realistic nor usually allowed by the server.
-
More than one Cookie Manager in scope. Only one is used, there is no way to choose which, and a cookie stored in one manager is not available to another. Keep one per Thread Group.
-
Planned for 6.0: expired cookies stop being sent. The 6.0 change log (on master, not released) lists as an incompatible change that the Cookie Manager no longer sends cookies after their expiry time; in 5.6.3 and earlier they kept being sent indefinitely. A long soak test that outlives a session cookie therefore starts failing with authentication or session errors in 6.0 unless the plan logs in again or refreshes the session. Design soak tests with re-authentication now, and they will behave the same on both versions.
HTTP Cache Manager
The Cache Manager adds browser-like caching to the HTTP requests in its scope. Each virtual user thread has its own cache, which holds up to 5000 entries by default with a least-recently-used eviction policy.
How it works, according to the component reference:
-
When a sample succeeds (a
2xxresponse code), the manager saves theLast-ModifiedandEtagvalues (and the expiry, when relevant) for the URL. -
Before the next request to that URL, the sampler looks for an entry in the cache. If there is one, it sets the conditional headers (
If-Modified-SinceandIf-None-Match). -
If Use Cache-Control/Expires header when processing GET requests is selected, the stored
Cache-Control/Expiresvalue is also checked against the current time. For aGETwhose entry has not expired, the sampler returns immediately without contacting the server, as a browser would. -
A
Cache-Control: no-cacheresponse is stored as already expired, so it always produces a conditionalGET. For any otherCache-Controlvalue,max-agegives the lifetime; without it,Expiresis used; without both, the lifetime is derived fromLast-Modifiedand the responseDateas in RFC 2616 section 13.2.4.
When the document has not changed since it was cached, or its expiry is in the future, the response body is empty, which can break assertions that check the body.
Fields
| Field | Meaning | Required |
|---|---|---|
Name |
Descriptive name shown in the tree. |
No |
Clear cache each iteration? |
If selected, the cache is cleared when a new iteration of the main Thread Group loop starts. (The component reference words it as "at the start of the thread"; the 5.6.3 code clears it at each iteration start.) Disabled when the next option is selected. |
Yes |
Use Thread Group configuration to control cache clearing |
Delegates the decision to the Thread Group’s Same user on each iteration option. |
No |
Use Cache-Control/Expires header when processing GET requests |
Honour |
Yes |
Max Number of elements in cache |
Maximum entries per thread (default 5000). Larger values consume more heap. |
Yes |
Properties
| Property | Default | Meaning |
|---|---|---|
|
|
Space- or comma-separated list of HTTP methods whose responses can be cached. |
|
|
What a cache hit produces. |
|
empty |
Response code for |
|
empty |
Response message for |
In the .jmx file:
<CacheManager guiclass="CacheManagerGui" testclass="CacheManager" testname="HTTP Cache Manager">
<boolProp name="clearEachIteration">false</boolProp>
<boolProp name="useExpires">true</boolProp>
<boolProp name="CacheManager.controlledByThread">true</boolProp>
<intProp name="maxSize">5000</intProp>
</CacheManager>
<hashTree/>
When to use it, and what it costs
For a site test with embedded resources (images, scripts, style sheets), the Cache Manager is what keeps a returning
user from downloading every resource again, and leaving it out overstates the load. For a JSON API such as Bookshelf,
it matters only if the API sends validators (ETag, Last-Modified) or freshness headers and real clients honour
them. Many APIs send Cache-Control: no-store or nothing, and then the manager only adds work.
Worked numbers. The cache is per thread, so the worst-case number of entries is threads × max size: 50 threads × 5000
= 250,000 entries in the load generator’s heap. In practice the number of entries per thread is the number of
distinct cacheable URLs a user requests. A Bookshelf user who draws from 200 search terms and views up to 300 books
creates at most 200 + 300 = 500 entries, 25,000 for 50 threads, well below the limit. Raising the maximum to cache
everything for a site with thousands of resources multiplies heap use by the thread count, so adjust -Xmx
(the HEAP variable) accordingly, as the component reference advises.
Reporting pitfall. With the default RETURN_NO_SAMPLE, a fresh cache hit produces no sample, so the sample count
and throughput in the report drop while users keep "using" the page. That is realistic for server load, but it makes
the request count in the report lower than the iteration count suggests. Choose RETURN_200_CACHE if the report must
show every logical request.
HTTP Authorization Manager
The Authorization Manager specifies user logins for resources protected by server authentication: the kind of protection for which a browser shows its own login dialog (HTTP Basic, Digest, or Kerberos/SPNEGO), not a login form or a token endpoint. JMeter sends the credentials when it requests a matching URL.
Fields
| Field | Meaning | Required |
|---|---|---|
Name |
Descriptive name shown in the tree. |
No |
Clear auth on each iteration? |
Used by Kerberos: if selected, authentication is performed again on each iteration of the main Thread Group loop even if it was done in a previous one. Useful when each iteration represents a different virtual user. Disabled when the next option is selected. |
Yes |
Use Thread Group configuration to control clearing |
Delegates the decision to the Thread Group’s Same user on each iteration option. |
No |
Base URL |
A partial or complete URL that matches one or more request URLs. With a Base URL of |
Yes |
Username |
The user name to authorise. Variables are allowed, so each thread can use its own account from a CSV file. |
Yes |
Password |
The password for that user. It is stored unencrypted in the test plan. |
Yes |
Domain |
The domain to use for NTLM. |
No |
Realm |
The realm to use for NTLM. Applies only to the HttpClient implementation. |
No |
Mechanism |
The type of authentication: see the next table. In the |
No |
The table, titled Authorizations Stored in the Authorization Manager, has Add, Delete, Load (adds the entries of a saved file) and Save As buttons. Saving the test plan saves every row, passwords included, unencrypted.
URL matching checks the rows in order and stops at the first match, so list the most specific Base URLs first. Duplicate URLs are ignored.
Mechanisms by implementation
| HTTP Request implementation | Mechanisms | Pre-emptive? |
|---|---|---|
|
|
Basic authentication is pre-emptive by default since 3.2 ( |
|
|
Always pre-emptive, but the |
Basic authentication with per-thread credentials
Suppose the Bookshelf API’s administrative import endpoints under /admin/ were protected by HTTP Basic, with one test
account per thread in admins.csv (placeholder values only):
adminUser,adminPassword
admin01,change-me-01
admin02,change-me-02
Add a CSV Data Set Config (variable names adminUser,adminPassword) and an HTTP Authorization Manager to the Thread
Group, with one row whose Base URL is http://${__P(host,localhost)}:${__P(port,8080)}/admin/, Username
${adminUser}, Password ${adminPassword} and Mechanism BASIC:
<AuthManager guiclass="AuthPanel" testclass="AuthManager" testname="HTTP Authorization Manager">
<collectionProp name="AuthManager.auth_list">
<elementProp name="" elementType="Authorization">
<stringProp name="Authorization.url">http://${__P(host,localhost)}:${__P(port,8080)}/admin/</stringProp>
<stringProp name="Authorization.username">${adminUser}</stringProp>
<stringProp name="Authorization.password">${adminPassword}</stringProp>
<stringProp name="Authorization.domain"></stringProp>
<stringProp name="Authorization.realm"></stringProp>
<stringProp name="Authorization.mechanism">BASIC</stringProp>
</elementProp>
</collectionProp>
<boolProp name="AuthManager.controlledByThreadGroup">false</boolProp>
</AuthManager>
<hashTree/>
The plan now contains ${adminPassword}, not a password, and the CSV file can be kept out of version control. For a
one-off request to a Basic-protected endpoint a Header Manager with a computed header works too, using the core
__groovy function to base64-encode the pair:
Authorization: Basic ${__groovy("${adminUser}:${adminPassword}".bytes.encodeBase64().toString())}
Kerberos (SPNEGO)
Kerberos needs at least two JVM system properties, which can be put in bin/system.properties:
# bin/system.properties
java.security.krb5.conf=krb5.conf
java.security.auth.login.config=jaas.conf
The sample krb5.conf and jaas.conf files in JMeter’s bin folder point to further documentation. The related
JMeter properties are:
| Property | Default | Meaning |
|---|---|---|
|
|
Name of the application module used in |
|
|
Strip the port from the URL before building the service principal name, like
Internet Explorer and Firefox. Set to |
|
not stated |
Canonicalise the host name used for the service principal name. |
|
|
Delegate credentials in SPNEGO authentication. |
Cost and pitfalls
Pre-emptive Basic adds a header and nothing else. Digest and challenge-based Basic add a 401 round trip whenever the
client has no cached authentication state, which shows up as extra samples or extra latency. Kerberos adds the
ticket exchange with the KDC; with Clear auth on each iteration? every iteration repeats it, which loads the KDC as
well as the system under test. Other points:
-
It is not for form logins or tokens. A login form or
POST /api/auth/loginis a normal sampler; the session it creates lives in a cookie (Cookie Manager) or a token (extractor plus Header Manager). -
Base URL mismatch. A Base URL with
httpswhile the requests usehttp, or with a host name while the requests use an IP address, never matches, and the requests go out without credentials. -
Passwords in clear. Anything typed into the table is saved in the
.jmxfile and in any copy of it. Use variables.
Clearing state between iterations
A Thread Group iteration is one pass of the user journey. Whether the next pass is the same user (keeps cookies, cache and authentication) or a new one (starts clean) is a modelling decision. Since 5.2 it can be made in one place, the Thread Group’s Same user on each iteration option, which the three managers obey when their Use Thread Group configuration… box is selected. The reference describes the option: if selected, cookie and cache data from earlier responses are used in later iterations; if not, they are not, and a new connection is opened between iterations, which increases response times and resource use.
The 5.6.3 code applies the same rule in all three managers at the start of each iteration:
| "Use Thread Group configuration" | Thread Group "Same user on each iteration" | Manager "Clear … each iteration" | Result at iteration start |
|---|---|---|---|
selected |
selected |
ignored (disabled) |
Keep cookies, cache and Kerberos subjects. |
selected |
unchecked |
ignored (disabled) |
Clear them: every iteration is a new user. |
not selected |
ignored |
selected |
Clear them. |
not selected |
ignored |
not selected |
Keep them. |
The Cookie Manager resets to its user-defined cookies; the Cache Manager empties the cache; the Authorization Manager clears its Kerberos subjects. Variables, such as an extracted token, are never cleared by these options.
For Bookshelf, where each thread logs in once and then repeats the journey, select Same user on each iteration (the default) and Use Thread Group configuration… on each manager. For a model where each iteration is a fresh visitor, uncheck the Thread Group option and move the login inside the iteration instead of a Once Only Controller.
Source: Thread Group (Component Reference)
and the 5.6.3 sources of CookieManager, CacheManager and AuthManager in apache/jmeter
URL-rewriting sessions: the HTTP URL Re-writing Modifier
Some web applications keep the session id in the URL instead of a cookie, either as a parameter (?sid=...) or as a
path extension (;jsessionid=...). JMeter then has to read the id from the previous response and add it to the next
request. The HTTP URL Re-writing Modifier is a pre-processor built for this: it works like the HTML Link Parser but is
simpler and more efficient for this one job. Given the name of the session parameter, it finds the value anywhere in
the previous response (in a link or a form) and adds it to every request in its scope, replacing a value the request
already has. Placed at Thread Group level, like a Cookie Manager, it affects every request of the thread; placed under
a controller or a sampler it modifies only those requests.
| Field | Meaning | Required |
|---|---|---|
Name |
Descriptive name shown in the tree. |
No |
Session Argument Name |
The name of the parameter to grab from the previous response, wherever it appears (an |
Yes |
Path Extension (use ";" as separator) |
Select when the application appends the id to the path after a semicolon ( |
No |
Do not use equals in path extension (Intershop Enfinity compatibility) |
The application writes the extension without |
No |
Do not use questionmark in path extension (Intershop Enfinity compatibility) |
Prevents the query string from ending up in the path extension. |
No |
Cache Session Id? |
Save the last value found and reuse it when the previous response does not contain the session id. |
Yes |
URL Encode |
URL-encode the value when writing the parameter. |
No |
<URLRewritingModifier guiclass="URLRewritingModifierGui" testclass="URLRewritingModifier" testname="jsessionid in URL">
<stringProp name="argument_name">jsessionid</stringProp>
<boolProp name="path_extension">true</boolProp>
<boolProp name="path_extension_no_equals">false</boolProp>
<boolProp name="path_extension_no_questionmark">false</boolProp>
<boolProp name="cache_value">true</boolProp>
<boolProp name="encode">false</boolProp>
</URLRewritingModifier>
<hashTree/>
Distributed testing pitfall. The modifier reads the previous response. The component reference warns that in
distributed testing the sample sender mode (the mode property in jmeter.properties) must not be a stripping one,
such as the default StrippedBatch, which removes the response data (Bug 56376).
The Bookshelf API uses tokens, not URL rewriting, so this element is not in bookshelf-load.jmx; it is useful for
older servlet or portal applications.
Why only one manager applies per sampler
The managers are configuration elements and follow the hierarchical scoping rules: a sampler collects every
configuration element on the path from itself up to the Test Plan. Defaults elements (HTTP Request Defaults) are merged
field by field, and so, in 5.6.3, are Header Managers. The Cookie, Cache and Authorization managers are different: a
sampler can hold only one of each. When several are in scope, the 5.6.3 sampler replaces the one it has with the next
one it is given and writes a warning such as Existing CookieManager ... superseded by ... to jmeter.log. Because the
elements are handed over starting with the closest one, the one that survives is not the closest, which is the
opposite of what most people expect, and the documentation says only that there is "no way to specify
which one is used". Two practical consequences:
-
State is split. Cookies stored by one Cookie Manager are not visible to another, so a login handled by a controller-level manager and later requests handled by the Thread Group manager lose the session.
-
Overriding does not work. A Cookie or Authorization manager under one sampler cannot reliably override the Thread Group’s one, the way a Header Manager or an HTTP Request Defaults can.
The rule that follows is the one the user manual and component reference suggest: one Cookie Manager, one Cache
Manager and at most one Authorization Manager per Thread Group, with all URLs the Authorization Manager must cover in
its single table. If the jmeter.log of a run contains superseded by, the plan has more than one in scope.
Source: Elements of a Test Plan (User Manual), HTTP Cookie Manager and HTTP Authorization Manager (Component Reference)
Worked example: a JWT bearer flow for Bookshelf
The Bookshelf API authenticates with a JSON Web Token: POST /api/auth/login with a JSON body containing a user name
and password returns a JSON document with the token, and every other endpoint expects it in an
Authorization: Bearer <token> header. This is the common shape of a token-protected API: the
JWT and the JOSE Family page describes the token itself, and
Access and Refresh Tokens describes lifetimes and refresh. A real
OAuth 2.0 authorization server would issue the token from its token endpoint instead (for a machine client, the
client credentials grant); the JMeter side is the
same: one request obtains the token, an extractor stores it, a Header Manager sends it.
The response of the login endpoint in this example:
{ "token": "eyJhbGciOiJIUzI1NiJ9.placeholder.signature", "expiresIn": 900 }
Steps in the GUI
-
Credentials. Create
users.csvnext to the plan, one placeholder test account per line:username,password reader01,change-me-01 reader02,change-me-02 reader03,change-me-03Add Add → Config Element → CSV Data Set Config to the Thread Group: Filename
users.csv, Variable Names left empty (the first line is the header), Recycle on EOFTrue, Sharing modeAll threads. Each thread reads one line on each iteration; because the login runs only once per thread, each thread keeps the user of its first line. The parameterisation page in this group covers the CSV options in detail. -
JSON headers for everyone. Add Add → Config Element → HTTP Header Manager to the Thread Group with
Accept: application/jsonandContent-Type: application/json. -
Cookie Manager. Add Add → Config Element → HTTP Cookie Manager to the Thread Group, policy
standard, Use Thread Group configuration to control cookie clearing selected. Bookshelf does not need cookies, but a gateway or load balancer in front of it may set a sticky-session cookie; a real client would return it. -
Login once. Add Add → Logic Controller → Once Only Controller named "Login once per user" and, inside it, an HTTP Request
POST /api/auth/loginwith Body Data:{"username":"${username}","password":"${password}"} -
Extract the token. Right-click the login request, Add → Post Processors → JSON Extractor: Names of created variables
token, JSON Path expressions$.token, Match No.1, Default ValuesTOKEN_NOT_FOUND. Add Add → Assertions → Response Assertion checking that the response code equals200, so a failed login is reported as the login’s failure and not as 401 errors further on. -
Send the token. Add Add → Logic Controller → Simple Controller named "Authenticated journey" after the Once Only Controller. Inside it, add an HTTP Header Manager with
Authorization: Bearer ${token}, then the journey samplers. Because this Header Manager is not in scope of the login request, the login never sends the placeholder text.
The plan fragment
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Borrowers">
<stringProp name="ThreadGroup.num_threads">${__P(threads,1)}</stringProp>
<stringProp name="ThreadGroup.ramp_time">${__P(rampup,1)}</stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
<boolProp name="ThreadGroup.scheduler">true</boolProp>
<stringProp name="ThreadGroup.duration">${__P(duration,60)}</stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController">
<intProp name="LoopController.loops">-1</intProp>
<boolProp name="LoopController.continue_forever">false</boolProp>
</elementProp>
</ThreadGroup>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="JSON headers">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Accept</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
<CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager">
<collectionProp name="CookieManager.cookies"/>
<boolProp name="CookieManager.clearEachIteration">false</boolProp>
<boolProp name="CookieManager.controlledByThreadGroup">true</boolProp>
<stringProp name="CookieManager.policy">standard</stringProp>
</CookieManager>
<hashTree/>
<CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="users.csv">
<stringProp name="filename">users.csv</stringProp>
<stringProp name="fileEncoding">UTF-8</stringProp>
<stringProp name="variableNames"></stringProp>
<boolProp name="ignoreFirstLine">false</boolProp>
<stringProp name="delimiter">,</stringProp>
<boolProp name="quotedData">false</boolProp>
<boolProp name="recycle">true</boolProp>
<boolProp name="stopThread">false</boolProp>
<stringProp name="shareMode">shareMode.all</stringProp>
</CSVDataSet>
<hashTree/>
<OnceOnlyController guiclass="OnceOnlyControllerGui" testclass="OnceOnlyController" testname="Login once per user"/>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="POST /api/auth/login">
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">{"username":"${username}","password":"${password}"}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.path">/api/auth/login</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
</HTTPSamplerProxy>
<hashTree>
<JSONPostProcessor guiclass="JSONPostProcessorGui" testclass="JSONPostProcessor" testname="Extract token">
<stringProp name="JSONPostProcessor.referenceNames">token</stringProp>
<stringProp name="JSONPostProcessor.jsonPathExprs">$.token</stringProp>
<stringProp name="JSONPostProcessor.match_numbers">1</stringProp>
<stringProp name="JSONPostProcessor.defaultValues">TOKEN_NOT_FOUND</stringProp>
</JSONPostProcessor>
<hashTree/>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Login returns 200">
<collectionProp name="Asserion.test_strings">
<stringProp name="49586">200</stringProp>
</collectionProp>
<stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
<intProp name="Assertion.test_type">8</intProp>
</ResponseAssertion>
<hashTree/>
</hashTree>
</hashTree>
<GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Authenticated journey"/>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="Bearer token">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Authorization</stringProp>
<stringProp name="Header.value">Bearer ${token}</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="GET /api/books">
<stringProp name="HTTPSampler.path">/api/books?query=jmeter</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
</HTTPSamplerProxy>
<hashTree/>
</hashTree>
</hashTree>
Host and port come from the HTTP Request Defaults at Test Plan level, as on the other pages of this section, and the
remaining journey requests (view a book, create and view a loan) follow the same pattern as GET /api/books. The
Asserion.test_strings spelling is JMeter’s own property name, not a typo in this page.
Source: Once Only Controller, JSON Extractor and HTTP Header Manager (Component Reference)
Running it
A smoke run with one user, then the standard load profile (50 threads, 60 s ramp-up, 660 s duration, which leaves a 10-minute hold):
jmeter -n -t bookshelf-load.jmx -l smoke.jtl -Jthreads=1 -Jrampup=1 -Jduration=30
jmeter -n -t bookshelf-load.jmx -l load.jtl -Jthreads=50 -Jrampup=60 -Jduration=660
The summariser lines of the load run look like this (illustrative values; a local run will differ):
summary + 135 in 00:00:28 = 4.8/s Avg: 41 Min: 7 Max: 288 Err: 0 (0.00%) Active: 24 Started: 24 Finished: 0
summary + 498 in 00:00:30 = 16.6/s Avg: 44 Min: 6 Max: 412 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
Err: 0 and no 401 responses are the signal that the token is extracted and sent. If every request after the login
fails with 401, look at the Request tab of View Results Tree in a GUI smoke run: an Authorization header with the
value Bearer TOKEN_NOT_FOUND means the JSON path did not match; a header with the literal text Bearer ${token}
means the Header Manager was evaluated before any extractor set the variable.
Worked numbers: login once versus login every iteration
Assume one journey iteration, including think time, takes 12 s per user. During the 10-minute hold each of the 50 threads runs 600 / 12 = 50 iterations, 2,500 in total.
-
Login once per user (Once Only Controller): 50 login requests in the whole test, all during the 60 s ramp-up (about 50 / 60 ≈ 0.8 logins/s), and 0 during the hold.
-
Login on every iteration: 2,500 logins during the hold, 2,500 / 600 ≈ 4.2 logins/s on top of the other traffic. Password hashing makes a login one of the most expensive requests a service has, so this second plan measures a different system.
Neither is right in general: the workload model decides. The Bookshelf mix includes 10 % login-heavy iterations (short sessions that authenticate every time), which add 0.10 × 2,500 = 250 logins over the hold, about 0.4/s, on top of the 50 initial ones.
Token expiry in long tests
The token in this example lives 900 s (15 minutes). The 660 s load test ends before it expires, so logging in once is
enough. A soak test of several hours is not: after 15 minutes every request fails with 401. Replace the Once Only
Controller with an If Controller that logs in when there is no token yet or the token is older than a safety margin,
and record the login time after the extraction:
// JSR223 PostProcessor under POST /api/auth/login, after the JSON Extractor
vars.put('tokenTime', String.valueOf(System.currentTimeMillis()))
If Controller condition, with Interpret Condition as Variable Expression? selected (re-login after 12 of the 15 minutes):
${__groovy(vars.get('tokenTime') == null || System.currentTimeMillis() - Long.parseLong(vars.get('tokenTime')) > 720000)}
If the server issues refresh tokens, the If Controller can call the refresh endpoint instead of logging in again; how refresh tokens work is described in Access and Refresh Tokens, and token-related failure modes in Testing and Debugging OAuth. The same design also covers the planned 6.0 Cookie Manager change for cookie-based sessions.
Keeping tokens out of results
Request headers are not written to result files by default (jmeter.save.saveservice.requestHeaders=false), so the
tokens do not end up in a .jtl file. View Results Tree shows them in full, which is one more reason to use it only
for debugging small runs. Test accounts and tokens should belong to a test environment; credentials come from CSV files
or -J properties with placeholder values in the plan.
Load-generator cost at a glance
| Element | Cost on the load generator |
|---|---|
HTTP Header Manager |
A few strings per request; negligible. Many managers in scope mean a list merge per request. |
HTTP Cookie Manager |
Cookie parsing and matching per request; memory = threads × cookies per thread, usually small. |
HTTP Cache Manager |
Up to |
HTTP Authorization Manager |
Negligible for pre-emptive Basic; an extra round trip for challenge-based schemes; Kerberos ticket exchanges, repeated per iteration when clearing is on. |
HTTP URL Re-writing Modifier |
Scans the previous response body for the session parameter on every request in scope. |
Pitfalls
-
Missing Cookie Manager: a cookie-based session is lost after the login request.
-
More than one Cookie, Cache or Authorization manager in scope: one is silently superseded; look for
superseded byinjmeter.log. -
Assuming the closest Cookie Manager wins: it does not; only Header Managers (and Defaults) merge with the closest value winning.
-
Authorization header in scope of the login: the literal
Bearer ${token}is sent; scope the Header Manager to the authenticated requests. -
Hand-typed cookies or tokens from a browser: shared by every thread, expire mid-test, and leak into the
.jmx. -
Cache Manager on a JSON API: empty
304bodies break body assertions, and cache hits produce no sample by default. -
Same user on each iteration unchecked: with Use Thread Group configuration… selected on the managers, every iteration loses cookies and cache while variables (such as a token) survive, a mix that matches no real user.
-
Passwords in the Authorization Manager: stored unencrypted; use variables.
-
Distributed runs with URL rewriting: a stripping sample sender mode breaks the modifier.
From the books to 5.6 to 6.0
| Topic | Books (JMeter 2.11 to 3.0) | 5.6.3 and planned 6.0 |
|---|---|---|
Cookie policy and implementation |
The books target versions in which the HttpClient 3.1 implementation was still available (it was dropped in 3.2). |
|
Clearing per iteration |
Only the per-manager "Clear … each iteration" boxes. |
Since 5.2 the Thread Group’s Same user on each iteration can drive the three managers. |
Pre-emptive Basic authentication |
Challenge-based by default with HttpClient. |
Pre-emptive by default since 3.2 ( |
Several Header Managers |
Multiple Header Managers per sampler have been supported since 2.3.3. |
Merged (5.6.3 source), closest wins on equal names; the test-plan chapter still says otherwise. |
Expired cookies |
Sent indefinitely. |
Still sent in 5.6.3; planned for 6.0: no longer sent after expiry (incompatible change). |
Token correlation |
JSON PathExtractor plugin or regular expressions. |
Core JSON Extractor (and JMESPath, Boundary extractors). |
Session handling with cookies and URL rewriting is treated as a methodology topic in Performance Testing with JMeter, 2nd ed., chapter 4 (Managing Sessions); the element names, fields and defaults on this page come from the 5.6.3 documentation and sources instead.
References
-
Once Only Controller, If Controller and JSON Extractor (Component Reference)
-
Apache JMeter source: the
rel/v5.6.3tag forHTTPSamplerBase,HeaderManager,CookieManager,HC4CookieHandler,CacheManagerandAuthManager, andxdocs/changes.xmlon master for the planned 6.0 changes -
Bayo Erinle, Performance Testing with JMeter, 2nd ed., Packt, 2015 (JMeter 2.12), chapter 4: managing sessions with cookies and URL rewriting, used for the concept only.