QA How-To
JMeter correlation tutorial: Step by Step (2026)
Follow this JMeter correlation tutorial to extract and reuse dynamic tokens, IDs, cookies, redirects, JSON values, HTML fields, and OAuth data reliably.
24 min read | 3,307 words
TL;DR
JMeter correlation is a four-step loop: find the dynamic value, extract it from the response that creates it, validate the variable, and substitute that variable in the next request. The examples below cover JSON tokens, HTML CSRF fields, headers, redirects, cookies, OAuth, arrays, and Groovy fallbacks.
Key Takeaways
- Correlation captures a value from one server response and supplies it to a later request in the same virtual user flow.
- Prefer structured extractors for JSON, CSS selectors for HTML, and regular expressions only when the response has no better structure.
- Place each extractor under the sampler that creates the value and validate extraction immediately.
- Use JMeter variables for thread-local session data and properties only for deliberate cross-thread configuration.
- Let HTTP Cookie Manager handle ordinary cookies instead of manually correlating every Set-Cookie header.
- Debug with one thread and View Results Tree, then remove heavy listeners before load execution.
A JMeter correlation tutorial should show more than where to click. Correlation means capturing a server-generated value from one response and reusing it in a later request so every JMeter thread follows a valid, independent session. Without it, a recorded script often works once and then fails with authorization, validation, or not-found errors.
This guide uses a realistic login and order workflow. You will learn how to recognize dynamic data, choose the right extractor, check extraction before use, handle cookies and redirects, and make the flow reliable in non-GUI load runs.
TL;DR
| Response data | Preferred JMeter component | Example expression |
|---|---|---|
| JSON field | JSON Extractor | $.csrfToken |
| JSON array | JSON JMESPath Extractor or JSON Extractor | items[*].id or $.items[*].id |
| HTML element | CSS Selector Extractor | input[name=csrf] plus attribute value |
| Response header | Regular Expression Extractor or JSR223 | Location:\s*(.+) |
| Cookie | HTTP Cookie Manager | Automatic cookie storage and sending |
| Complex transformation | JSR223 PostProcessor with Groovy | Parse, validate, transform, then vars.put |
The repeatable method is detect -> extract -> assert -> reuse -> verify. Always test with at least two iterations, because a hardcoded token can make the first iteration look correct.
1. What correlation means in this JMeter correlation tutorial
A dynamic value is produced by the system during execution and cannot safely be copied into the test plan. Common examples include CSRF tokens, session identifiers, OAuth access tokens, order IDs, upload keys, pagination cursors, WebSocket negotiation values, and redirect parameters.
Correlation connects two requests. Suppose GET /login returns an HTML hidden field named csrf. The next POST /login must send that exact value. In JMeter, an extractor attached to the GET sampler stores the value in a thread-local variable. The POST sampler references it as ${csrfToken}.
Correlation is different from parameterization. Parameterization supplies input data controlled by the test, such as usernames from a CSV file. Correlation supplies data controlled by the server, such as the session-specific account ID returned after login.
| Dimension | Correlation | Parameterization |
|---|---|---|
| Source | Earlier response | CSV, property, generated input, database |
| Ownership | Server or protocol | Test design |
| Typical lifetime | Request, transaction, or session | Iteration, thread, or test |
| Example | CSRF token | Login username |
| Main failure | Stale or missing value | Duplicate or invalid input |
A value may involve both. A username can come from CSV, while the authenticated user ID is extracted from the login response.
2. Record the flow, then identify changing values
Recording is a discovery tool, not a finished performance test. Capture the business flow with JMeter's HTTP(S) Test Script Recorder or build requests from API documentation. Then run the same flow twice with different clean sessions and compare request and response data.
Look for a value that appears first in a response and later in a request. Search response bodies, response headers, cookies, and redirect URLs. A strong correlation candidate usually changes across sessions, is opaque, and causes a downstream failure when replaced with an old value.
For the example flow:
GET /api/session/bootstrapreturns{"csrfToken":"a1b2..."}.POST /api/loginsends headerX-CSRF-Token: a1b2....- Login returns
{"accessToken":"ey...","account":{"id":"acct-42"}}. POST /api/orderssends the access token and returns{"id":"ord-919"}.GET /api/orders/ord-919uses the new order ID.
The CSRF token, access token, account ID, and order ID require correlation. The login credentials are parameterized. Content type, API version, and a deliberate test tenant can be static configuration.
Do not correlate every long string. Hashes in static asset names, analytics IDs, timestamps, and response-only trace IDs may never be required downstream. The proof is data flow, not appearance.
3. Build the baseline JMeter test plan
Create this structure so scope and execution order are obvious:
Test Plan
User Defined Variables
HTTP Request Defaults
HTTP Cookie Manager
Thread Group
Once Only Controller
GET Bootstrap
JSON Extractor: csrfToken
JSR223 Assertion: csrfToken exists
POST Login
JSON Extractor: accessToken
JSON Extractor: accountId
Transaction Controller: Create and read order
POST Create Order
JSON Extractor: orderId
GET Order
View Results Tree (debug only)
Set HTTP Request Defaults to the protocol, host, port, and common timeouts. Keep paths on individual samplers. Add an HTTP Cookie Manager under the Thread Group so each thread receives its own cookie store. The default cookie behavior is usually correct for session cookies.
The Once Only Controller means bootstrap runs once per thread, not once for the entire test. That distinction matters because JMeter threads represent independent virtual users. If the application rotates CSRF tokens after login or per request, place the bootstrap request inside the relevant loop instead.
While debugging, use one thread, one or two loops, and View Results Tree. Inspect request data and response data directly. Before load, disable or remove View Results Tree because retaining full samples consumes significant memory.
Use descriptive sampler names. A failure called POST Create Order is far easier to diagnose than HTTP Request-17.
4. Extract and reuse JSON values
Attach a JSON Extractor as a child of GET Bootstrap. Configure:
| Field | Value |
|---|---|
| Names of created variables | csrfToken |
| JSON Path expressions | $.csrfToken |
| Match numbers | 1 |
| Default values | __MISSING_CSRF__ |
If the response is:
{
"csrfToken": "fbc7c9e2",
"expiresInSeconds": 300
}
JMeter stores fbc7c9e2 in csrfToken for the current thread. In the next HTTP Request, add an HTTP Header Manager:
Content-Type: application/json
X-CSRF-Token: ${csrfToken}
For the login response:
{
"accessToken": "example-token",
"account": {
"id": "acct-42",
"tier": "test"
}
}
One JSON Extractor can create multiple variables by using semicolon-separated fields:
Names of created variables: accessToken;accountId
JSON Path expressions: $.accessToken;$.account.id
Match numbers: 1;1
Default values: __MISSING_TOKEN__;__MISSING_ACCOUNT__
Reference them later as ${accessToken} and ${accountId}. In an Authorization header, use Bearer ${accessToken}.
The default sentinel is important. An empty default can silently create a request that fails far downstream. A recognizable marker makes both the request and the root cause obvious.
5. Correlate nested JSON arrays and random matches
Assume a catalog response contains:
{
"items": [
{"id": "sku-100", "stock": 8},
{"id": "sku-200", "stock": 4},
{"id": "sku-300", "stock": 0}
]
}
A JSON Extractor with expression $.items[*].id and match number -1 captures all matches. If the variable name is sku, JMeter creates variables such as sku_1, sku_2, sku_3, and sku_matchNr.
To choose a random extracted item with supported JMeter functions:
${__V(sku_${__Random(1,${sku_matchNr})})}
Nested function syntax becomes hard to maintain. For selection rules such as in-stock only, a Groovy JSR223 PostProcessor is clearer:
import groovy.json.JsonSlurper
def body = prev.getResponseDataAsString()
def payload = new JsonSlurper().parseText(body)
def candidates = payload.items.findAll { item ->
(item.stock as int) > 0
}
if (candidates.isEmpty()) {
throw new AssertionError('No in-stock catalog item was returned')
}
def selected = candidates[
java.util.concurrent.ThreadLocalRandom.current()
.nextInt(candidates.size())
]
vars.put('selectedSku', selected.id.toString())
Use Groovy, keep Cache compiled script if available enabled, and avoid JavaScript engines in JSR223 components. The script uses JMeter's standard prev and vars bindings.
6. Extract CSRF tokens from HTML
When a server-rendered login page contains a hidden field, a CSS Selector Extractor is more robust than a regex over HTML:
<form method="post" action="/login">
<input type="hidden" name="csrf" value="9d6f2a8c">
<input type="email" name="email">
<button type="submit">Sign in</button>
</form>
Attach a CSS Selector Extractor to GET /login:
| Field | Value |
|---|---|
| Name of created variable | csrfToken |
| CSS selector expression | input[name=csrf] |
| Attribute | value |
| Match number | 1 |
| Default value | __MISSING_CSRF__ |
Then add the form parameter csrf=${csrfToken} to the login POST. Select the correct request body mode. HTML forms commonly use application/x-www-form-urlencoded, while JSON APIs need a raw JSON body and content-type header.
A Regular Expression Extractor can be a fallback:
name=["']csrf["'][^>]*value=["']([^"']+)["']
Use template $1$ and match number 1. This expression still assumes attribute order, so CSS extraction is preferable. HTML is not a regular language, and minor markup changes can defeat an otherwise plausible regex.
When the token appears in JavaScript rather than an element, inspect whether a bootstrap API supplies it. Correlating the underlying structured response is usually more stable than scraping a script block.
7. Handle response headers and redirects
Dynamic values often arrive in the Location response header after a create request:
HTTP/1.1 201 Created
Location: /api/orders/ord-919
A Regular Expression Extractor can target response headers. Configure the field to check as response headers and use:
(?mi)^Location:s*/api/orders/([^s/?]+)
Template $1$ stores only ord-919. Name the variable orderId, use match number 1, and set the default to __MISSING_ORDER_ID__.
Redirect handling changes what the sampler exposes. Follow Redirects records redirect exchanges as sub-samples and finishes on the destination. Redirect Automatically delegates behavior more like the underlying client and provides less testing control. If you need to extract from the initial 302 response, disable automatic following on that sampler or inspect the correct sub-sample behavior.
For complex header rules, use Groovy:
def location = prev.getResponseHeaders()
.readLines()
.find { line -> line.toLowerCase().startsWith('location:') }
if (location == null) {
throw new AssertionError('Location response header is missing')
}
def orderId = location.split(':', 2)[1].trim().tokenize('/').last()
vars.put('orderId', orderId)
Headers are case-insensitive. Normalize names and validate the expected URL structure instead of assuming every colon or slash has the same meaning.
8. Let HTTP Cookie Manager correlate cookies
An HTTP Cookie Manager automatically receives Set-Cookie response headers and sends applicable cookies on later requests for the same JMeter thread. This is the correct solution for ordinary session cookies.
Place one Cookie Manager within the Thread Group scope and leave Clear cookies each iteration unchecked when one thread represents a continuing user session. Check it when each loop must represent a fresh session. The correct setting follows the workload model, not a universal rule.
Do not copy a browser's cookie header into an HTTP Header Manager. That hardcodes expired session material, shares it across virtual users, and bypasses domain, path, expiry, and secure-cookie rules.
Manual extraction is justified only when the application expects a cookie value somewhere other than normal cookie handling, such as echoing an anti-CSRF cookie into a custom header. In that case, first let Cookie Manager store it, then expose the required value through an appropriate JMeter function or a script designed and tested for your cookie policy.
Cookies are correlation even when no extractor is visible. The manager is a protocol-aware correlator. Validate it by viewing request headers with one debug thread, then disable the heavy listener before load.
9. Correlate OAuth and bearer tokens
For a client credentials flow, add a token request before protected API calls:
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&scope=orders.read
Bind the client ID and secret from the execution environment or a secure secret mechanism. Do not commit them in the JMX file. Extract $.access_token as accessToken and, if useful, $.expires_in as tokenLifetime.
Add an HTTP Header Manager in the protected controller:
Authorization: Bearer ${accessToken}
Accept: application/json
Token placement depends on workload semantics. A Once Only Controller obtains one token per thread. A setup thread group that obtains one token for all users may be correct for a service identity, but it is wrong for independent user sessions and can create unintended shared limits. A long soak test may need refresh logic before expiry.
Validate the token response code and extraction. If authentication fails, stop the affected thread rather than sending thousands of invalid protected requests. A Response Assertion or JSR223 Assertion can make the failure immediate.
The API security testing basics guide explains negative cases such as expired, missing, malformed, and insufficient-scope tokens. Keep those functional security checks separate from the main load workload unless they are part of the intended traffic model.
10. Validate every extracted value immediately
A test should fail where correlation breaks, not three requests later. Add a JSR223 Assertion under the sampler that creates the token:
def value = vars.get('csrfToken')
if (value == null || value == '__MISSING_CSRF__' || value.isBlank()) {
AssertionResult.setFailure(true)
AssertionResult.setFailureMessage(
'Bootstrap response did not contain csrfToken'
)
}
For several required variables:
def required = ['accessToken', 'accountId']
def missing = required.findAll { name ->
def value = vars.get(name)
value == null || value.startsWith('__MISSING_') || value.isBlank()
}
if (!missing.isEmpty()) {
AssertionResult.setFailure(true)
AssertionResult.setFailureMessage(
'Missing correlated variables: ' + missing.join(', ')
)
}
This uses the standard AssertionResult binding available to JSR223 Assertions. Keep the message value-free for secrets. Reporting that accessToken is missing is safe; printing its content is not.
Configure Thread Group behavior after a sampler error according to risk. For a login failure, stopping the current thread often prevents meaningless downstream traffic. For an individual catalog miss, continuing might be appropriate. Error policy is part of workload design.
Add assertions for the business outcome too. A successfully extracted order ID does not prove the order was created correctly.
11. Understand JMeter variable scope
JMeter variables in vars are local to the current thread. That is ideal for session tokens, order IDs, cursors, and account state because one virtual user cannot overwrite another user's values.
JMeter properties in props are global to the JMeter process. Use them for deliberate configuration such as a base URL passed with -JbaseUrl=..., not for ordinary per-user correlation. Writing every token to a property creates concurrency races and can leak data between threads.
| Storage | Scope | Good use | Bad use |
|---|---|---|---|
vars |
Current thread | Access token, order ID, cursor | Sharing setup data with all threads |
props |
JMeter process | Environment configuration | Per-user session token |
| CSV row | Allocation depends on sharing mode | Test-controlled accounts | Server-generated ID |
| JMX literal | Entire plan | Stable endpoint path | Any changing credential |
Variables persist for the thread until overwritten or removed. If a loop expects a new token each iteration, clear or replace the variable and use a missing sentinel so an old successful value cannot survive a failed extraction.
In distributed testing, JMeter properties passed on the controller are not automatically magical shared memory across engines. Send configuration to every engine and keep session correlation thread-local. The JMeter distributed testing guide covers remote-engine configuration and result flow.
12. Debug this JMeter correlation tutorial step by step
Debug correlation with the smallest possible execution. Use one thread, one iteration, and no timers unless timing is part of the failure. Add View Results Tree and a Debug Sampler after extraction. The Debug Sampler can display JMeter variables in its response, so never use it with production secrets in shared logs.
Check these items in order:
- Did the source sampler receive the expected status and content type?
- Is the value present in the response body, headers, or sub-sample you selected?
- Does the extractor scope point to that sampler?
- Does the expression return exactly one intended value?
- Does the variable contain a missing sentinel, an old value, or the new value?
- Does the next request reference the exact variable name?
- Is encoding correct in JSON, form data, URL path, query string, or header?
- Does the server accept the value only once or for a limited lifetime?
Use the JMeter log for extractor or script errors. Add temporary Groovy logging of variable names and non-sensitive metadata, not token content:
log.info(
'orderId present={}, responseCode={}',
vars.get('orderId') != null,
prev.getResponseCode()
)
Run the test twice. A script that passes only the first time often contains recorded state. Then increase to two threads and verify each virtual user receives different session values where the application promises uniqueness.
13. Run the correlated test from the CLI
The GUI is for authoring and debugging. Execute load from non-GUI mode:
jmeter -n -t plans/order-flow.jmx -JbaseUrl=https://staging.example.com -Jthreads=10 -JrampUp=30 -Jduration=300 -l results/order-flow.jtl -e -o reports/order-flow
Reference properties in the test plan with JMeter functions, for example ${__P(baseUrl,https://localhost:8443)} and ${__P(threads,1)}. Choose property defaults that are safe and obvious. A default should never point a load test at production.
Before the run, remove View Results Tree, View Results in Table, and other per-sample GUI listeners. Keep Summary Report only for small debugging if needed, and prefer the HTML dashboard generated after execution.
A clean CLI run should create a new results path. JMeter refuses to overwrite a nonempty report directory, which helps prevent mixed evidence. Store the JMX file, input-data version, command parameters, application build, and JMeter version with results.
Correlation correctness comes before concurrency. If the one-user flow is invalid, adding threads only creates faster invalid traffic and misleading error rates.
Interview Questions and Answers
Q: What is correlation in JMeter?
Correlation captures a dynamic value from a server response and reuses it in a later request for the same virtual user. It models protocol state such as tokens, IDs, cursors, and redirects. It is different from parameterization, which supplies test-controlled input.
Q: Which extractor should you choose for JSON?
Use JSON Extractor for JSONPath or JSON JMESPath Extractor when JMESPath fits the query. Structured extraction is clearer and safer than regular expressions over JSON. Add a default sentinel and validate the result immediately.
Q: Why are JMeter variables preferred for session tokens?
Variables are local to a thread, so each virtual user keeps independent state. JMeter properties are global to the process and can cause threads to overwrite one another. Session correlation almost always belongs in vars.
Q: How do you correlate cookies?
Use HTTP Cookie Manager for ordinary cookie behavior. It processes Set-Cookie and sends eligible cookies with later requests for the same thread. Manual extraction is needed only for an unusual workflow that uses a cookie value outside cookie semantics.
Q: How do you debug a correlation failure?
I run one thread and inspect the exact source response, extractor scope, expression result, variable value, and next request. I use a missing sentinel and an immediate assertion. After it works twice for one thread, I test at least two threads for isolation.
Q: When is a JSR223 PostProcessor appropriate?
Use it when structured extractors cannot express required filtering, transformation, or validation. Groovy is the preferred scripting language, and compiled script caching should remain enabled. I keep scripts small and avoid logging sensitive values.
Q: What happens if an extractor is scoped incorrectly?
It may parse the wrong sampler, a redirect sub-sample, or no response, leaving a default or stale variable. The next request then fails for an apparently unrelated reason. Attaching the extractor directly to the producer sampler makes the data flow clear.
Common Mistakes
- Treating recorded tokens, session IDs, and order IDs as permanent test data.
- Using a regular expression for structured JSON or HTML when a structured extractor exists.
- Attaching the extractor after the request that needs the value rather than under the producer sampler.
- Leaving the default empty, which hides extraction failure until a downstream request.
- Storing per-user tokens in global JMeter properties.
- Manually hardcoding Cookie headers instead of using HTTP Cookie Manager.
- Logging bearer tokens or displaying them through Debug Sampler in shared artifacts.
- Testing only one iteration, which allows recorded state to appear valid.
- Running View Results Tree during load and exhausting the load generator's memory.
- Increasing threads before the one-user business transaction is correct.
Conclusion
This JMeter correlation tutorial follows one durable rule: extract dynamic state from the response that owns it, validate it immediately, and reuse it only within the correct virtual-user scope. JSON Extractor, CSS Selector Extractor, HTTP Cookie Manager, and small Groovy postprocessors cover nearly every common API and web workflow.
Build the sample login and order flow with one thread, run it twice, and deliberately break one extractor to confirm the assertion points to the source. Only after correlation survives multiple iterations and users should you add realistic pacing and load.
Interview Questions and Answers
Explain correlation versus parameterization in JMeter.
Correlation captures server-controlled state from an earlier response, while parameterization supplies test-controlled input from CSV, properties, or generators. A CSRF token is correlated, but a username is usually parameterized. A realistic flow often uses both.
How would you correlate an access token from JSON?
I attach a JSON Extractor to the token sampler, extract $.access_token into a thread-local variable, and use a visible missing sentinel. An immediate assertion stops the thread if extraction fails. Protected samplers then send the variable in the Authorization header without logging it.
Why is extractor placement important?
Postprocessors run after samplers within their scope. Attaching the extractor directly to the response that creates the value makes timing and ownership explicit. Incorrect scope can parse a controller's last sample, a redirect destination, or an unrelated request.
How do you handle multiple JSON matches?
I extract all matches when the workflow needs a collection, inspect the generated match count, and select according to a defined business rule. For filtering or transformation, I use a small cached Groovy JSR223 PostProcessor. I fail clearly when no valid candidate exists.
Why should session IDs not be stored in JMeter properties?
Properties are shared across threads in one engine, so virtual users can overwrite each other's session state. Variables are thread-local and preserve user isolation. Properties are better for stable run configuration such as host, thread count, or environment name.
How do redirects affect correlation?
The required value may exist on the initial redirect response rather than the final destination. I verify Follow Redirects and Redirect Automatically behavior, inspect sub-samples, and extract at the correct response boundary. If needed, I temporarily disable redirect following to prove the source.
What is your correlation debugging workflow?
I use one thread, inspect the producer response, confirm extractor scope and expression, and assert a visible missing sentinel immediately. I verify the consumer request and business response, repeat the iteration, then add a second thread. Only after isolation is proven do I remove debug listeners and add load.
Frequently Asked Questions
What is JMeter correlation with an example?
Correlation is extracting a changing server value and using it later. For example, a JSON Extractor saves $.csrfToken from a bootstrap response as csrfToken, and the login request sends it in the X-CSRF-Token header.
How do I extract a JSON value in JMeter?
Add a JSON Extractor under the sampler that returns JSON, provide a variable name, JSONPath expression, match number, and visible default sentinel. Reference the created variable in a later sampler with JMeter variable syntax.
Should I use a regular expression extractor for JSON?
No when the response is valid JSON. Prefer JSON Extractor or JSON JMESPath Extractor because they understand structure and escaping. Use regex only for unstructured text or carefully targeted headers.
How do I correlate a CSRF token in JMeter?
Extract it from the page, bootstrap response, header, or cookie that issues it. Use JSON extraction for APIs or CSS Selector Extractor for an HTML hidden input, then send the variable in the exact form field or header expected by the server.
Are JMeter variables shared between threads?
No. Normal JMeter variables are thread-local, which is correct for user sessions and dynamic IDs. JMeter properties are process-wide and should be reserved for deliberate shared configuration.
Why does my JMeter extractor return a default value?
The source response may differ, the extractor may be attached to the wrong sampler, the expression may not match, or redirect handling may expose another sample. Inspect the exact response with one thread and validate content type, scope, and expression.
How do I test JMeter correlation before a load test?
Run one thread for at least two iterations with View Results Tree and immediate assertions enabled. Then run two threads to confirm values remain isolated, remove heavy listeners, and execute the final plan in non-GUI mode.