Correlation in JMeter means capturing a value the server generates during the test, such as a CSRF token, a session token or an order ID, and sending it back in the requests that follow. You do it with a post-processor (a Regular Expression, JSON, Boundary, CSS Selector or XPath2 Extractor) attached to the response that contains the value, then reference the variable as ${name}.
Recorded scripts break without this step, because the values they replay belonged to a session that no longer exists. This guide shows how to find those values, which extractor to use, and what the same test plan returned with and without extractors on JMeter 5.6.3.
Key Takeaways
- Extract every value the server creates per session or per request: CSRF tokens, bearer tokens, IDs, view states. Session cookies are handled by the HTTP Cookie Manager.
- Use the JSON Extractor for JSON, the Boundary Extractor for HTML or text with fixed delimiters, and the Regular Expression Extractor when you need a pattern.
- Set Match No. to 1 for a single value, and -1 to capture every match for a ForEach Controller.
- Give every extractor a default value such as
TOKEN_NOT_FOUND, so a failed extraction shows up in the results instead of hiding. - In our test, the plan without extractors failed 30 of its 45 HTTP requests and silently skipped 45 more.
What is correlation in JMeter?
Correlation is the step that turns a recorded script into one that works for many users. A server issues fresh values on every login or page view: a CSRF token in a form, a JWT in a login response, a cart or order ID. A script that replays the values from the recording sends stale data, and the server rejects it. The fix is to extract each fresh value from the response where it first appears and substitute it into later requests.
JMeter has no single element for this. The job is done by post-processors, which the manual describes as elements “most often used to process the response data, often to extract values from it.” They run after the sampler they are attached to, store what they find in a JMeter variable, and every later element in the same thread can read it with ${variable}. Variables are local to each thread, so 100 virtual users each carry their own token.
Parameterization looks similar, because both end in a ${variable}. Parameterization feeds data you prepared before the test, such as usernames from a CSV file; extraction captures data the server creates during it. Our guide to parameterization in JMeter covers the first half; most realistic scripts need both.
How do you find the dynamic values in a recorded script?
Record the same journey twice and compare the two scripts. Any request parameter, header or path segment that differs between the recordings, and that you did not type differently, was generated by the server and needs an extractor. Long random-looking strings, hidden form fields, and values in Authorization headers are the usual suspects.
Then trace each value back to the first response that contains it. Search the response bodies and headers of the earlier samples in View Results Tree for the exact string. The response where it first appears is where the extractor goes.
A replay of stale values usually tells you something is wrong through status codes. In our test the login answered 403 (the CSRF check failed) and every authenticated call answered 401. Applications built on older frameworks may answer 200 with an error page instead, so add a Response Assertion that checks for content only a logged-in user sees.
Leave session cookies alone. The HTTP Cookie Manager “stores and sends cookies just like a web browser”, and each JMeter thread gets its own cookie store. Add one Cookie Manager to the Thread Group and cookie-based sessions work without any extractor.
Which JMeter extractor should you use?
Pick the extractor that matches the format of the response. The table lists the five extractors you will use most; JMeter 5.6.3 also ships the older XPath Extractor and a JSON JMESPath Extractor.
| Extractor | Use it for | How you point it at the value | Notes |
|---|---|---|---|
| JSON Extractor | JSON APIs | JSONPath, e.g. $.token | Several expressions in one element, _ALL concatenation option |
| Boundary Extractor | HTML or any text with fixed text around the value | Left and right boundary strings | No regex to write; added in JMeter 4.0 “with better performances” |
| Regular Expression Extractor | Values that need a pattern, or headers, URL, response code | Perl-style regex with a capture group and a template like $1$ | The most flexible, and the easiest to get subtly wrong |
| CSS Selector Extractor | HTML where the value sits in an element or attribute | CSS selector plus attribute name | Formerly the CSS/JQuery Extractor |
| XPath2 Extractor | XML or XHTML, SOAP | XPath 2 query | See the XPath Extractor guide |
All five share the same core fields: the name of the variable to create, which match to take, and a default value used when nothing matches. The Regular Expression and Boundary Extractors can also read response headers, the URL or the response code instead of the body, through the Field to check option.
Step by step: a CSRF token, a bearer token and a list of IDs
We built a small local application with the three kinds of dynamic value a login flow usually has, and handled them in a JMeter 5.6.3 test plan run in CLI mode (5 threads, 3 iterations each). The app works like this:
GET /login-formsets a session cookie and returns<input type="hidden" name="csrf" value="...">, a new value per session.POST /loginaccepts the form only ifcsrfmatches the value issued for that session cookie, and returns JSON:{"token": "...", "user": {"id": 42}}.GET /ordersrequiresAuthorization: Bearer <token>and returns{"orders": [{"id": "o-1"}, {"id": "o-2"}, {"id": "o-3"}]}.GET /orders/<id>returns 200 only for an ID that exists.
Step 1: Capture the CSRF value with a Boundary Extractor
Right-click the GET /login-form sampler and add Post Processors > Boundary Extractor, as a child of that sampler so it reads only that response. Set:
- Name of created variable:
csrf - Left Boundary:
name="csrf" value=" - Right Boundary:
" - Match No.:
1 - Default Value:
CSRF_NOT_FOUND
In the POST /login sampler, send the form field csrf with the value ${csrf}. A Regular Expression Extractor with name="csrf" value="([^"]+)" and template $1$ does the same job.
Step 2: Extract the bearer token with a JSON Extractor
Add a JSON Extractor under POST /login:
- Names of created variables:
token - JSON Path expressions:
$.token - Match No.:
1 - Default Values:
TOKEN_NOT_FOUND
Set Match No. explicitly. The manual says an empty Match No. means 0, and 0 means “random”, which is harmless for one value and wrong as soon as the path matches several. If you also need the user ID, add it in the same element: token;user_id with $.token;$.user.id, Match No. 1;1 and two defaults: every list needs one entry per expression.
Step 3: Send the token on every authenticated request
Put the authenticated samplers under a Simple Controller and give it an HTTP Header Manager with Authorization = Bearer ${token}. Because the Header Manager sits on the controller, it applies to every sampler below it, and you set the header once. When the login itself comes from CSV credentials, the CSV login and token extraction walkthrough shows the full flow per user.
Step 4: Extract every order ID and loop over them
Add a second JSON Extractor under GET /orders with variable order_id, path $..id, Match No. -1 and Compute concatenation var ticked. With -1, JMeter creates numbered variables instead of one value. Our Debug Sampler showed:
order_id_1=o-1
order_id_2=o-2
order_id_3=o-3
order_id_ALL=o-1,o-2,o-3
order_id_matchNr=3
Add a ForEach Controller after it, inside the same Simple Controller so the Authorization header still applies, with Input variable prefix order_id and Output variable name order_id, and put GET /orders/${order_id} inside. The controller walked order_id_1 to order_id_3, so each iteration of each thread requested all three orders: 45 calls across the run.
The order of these elements matters: an extractor runs after its own sampler and before the next one, so a variable exists only for samplers that come later in the tree. The execution order of JMeter elements explains the rules.
What happens when a script has no extractors?
We ran three versions of the same plan against the same app on 25 October 2026. The first had all three extractors. The second replayed the CSRF value and token captured during a recording, which is what a recorded script does when nobody adds extractors. The third kept the extractors but disabled the HTTP Cookie Manager.
| Sampler | With extractors | Recorded values replayed | Extractors, no Cookie Manager |
|---|---|---|---|
| GET /login-form | 15 x 200 | 15 x 200 | 15 x 200 |
| POST /login | 15 x 200 | 15 x 403 | 15 x 403 |
| GET /orders | 15 x 200 | 15 x 401 | 15 x 401 |
| GET /orders/{id} | 45 x 200 | not sent | not sent |
| Debug Sampler (kept in for the demo) | 15 x 200 | 15 x 200 | 15 x 200 |
| Summary line | 105 samples, 0 errors | 60 samples, 30 errors (50%) | 60 samples, 30 errors (50%) |
Three things in that table are easy to miss on a real project.
The error rate understates the damage. The replay reported 50% errors, not 100%, because the login form and our Debug Sampler still passed; counting HTTP requests only, 30 of 45 failed. Worse, the ForEach Controller ran zero times: the manual says it “does not run any samples if inputVar_1 is null”, so 45 requests vanished without a single error. Compare request counts per label against a good run, not only the error rate.
Every stale value fails on its own. The replay sent both a recorded CSRF value and a recorded token, so the login answered 403 and every authenticated call answered 401. Fixing one extractor is not enough; the script works only when all of them are in place.
A correct extraction can still produce a rejected request. In the third run the Boundary Extractor captured a valid-looking CSRF value every time, and every login still got 403, because the app ties the token to the session cookie and no cookie was sent. Extractors and the Cookie Manager work together.
How do you debug an extractor that returns nothing?
Start with the default value. If a request goes out carrying TOKEN_NOT_FOUND, the extractor did not match. Without a default, the Regular Expression and Boundary Extractors leave the variable unset, and JMeter sends the literal text ${token}, because “if an undefined function or variable is referenced, JMeter does not report/log an error”, so a missing extraction looks like a strange value rather than a failure.
Then check, in this order:
- Scope. The extractor must be a child of the sampler whose response holds the value. Placed under a controller, it runs after every sampler in that controller, and the last one wins.
- Field to check. A token in a
LocationorSet-Cookieheader needs Field to check set to Response Headers, not Body. - Escaping. HTML often encodes characters (
&,/). Match the response as it arrives, or use Body (unescaped), which the manual warns “highly impacts performances”. - Match number. 0 picks a random match. With several matches, use 1 or -1 on purpose.
- What the variable holds. Add a Debug Sampler (or a Debug PostProcessor) and read its output in View Results Tree. View Results Tree also has a tester for each extractor type, so you can try a boundary or JSONPath against a saved response before running again.
Remove the Debug Sampler before a load run. It counts as a sample, so it inflates the totals and the throughput figures.
Extractors in large and distributed load tests
Everything above happens per thread: each virtual user extracts its own values and never sees another thread’s. That is what makes such a script safe to scale. Two things change when it runs at full load.
First, extractors cost CPU on the load generator. Prefer the JSON or Boundary Extractor where they fit, keep regexes anchored to nearby text, and avoid Body (unescaped) unless you need it. Listeners such as View Results Tree “MUST NOT BE USED during load test”, so debug with a single thread and run the real test in CLI mode, as the non-GUI mode guide describes.
Second, some values cannot be extracted from a response at all, because a browser computes them in JavaScript, and JMeter “does not execute the Javascript found in HTML pages”. Those need a JSR223 PreProcessor that reproduces the calculation, or a test of the API behind the page instead.
When the script works locally, JMeter cloud load testing on LoadFocus runs the same .jmx unchanged from 25 cloud regions: upload the plan with any CSV files it reads, set virtual users, duration and ramp-up in the browser, and each virtual user still extracts its own tokens. Run it once with a few users first: a missing extractor shows up as 401s and 403s on the steps after login long before load matters. The free JMeter script validator checks a plan before you run it.
Frequently Asked Questions
What is correlation in JMeter?
Correlation in JMeter is capturing a dynamic value from a server response, such as a session token, CSRF token or ID, and passing it to later requests. You add an extractor (post-processor) to the sampler whose response contains the value, store it in a variable, and reference it as ${variable} in the requests that need it.
What is the difference between correlation and parameterization in JMeter?
Parameterization supplies values you prepare before the test, for example usernames and passwords from a CSV Data Set Config. Correlation captures values the server creates while the test runs, for example the token returned by the login, using extractors. A realistic script usually needs both: parameterized credentials to log in, extracted tokens afterwards.
Which extractor is best for correlation in JMeter?
Match the extractor to the response format. Use the JSON Extractor for JSON APIs, the Boundary Extractor when the value sits between fixed strings in HTML or text, the Regular Expression Extractor when you need a pattern or must read headers, the CSS Selector Extractor for HTML attributes, and the XPath2 Extractor for XML and SOAP.
Does JMeter correlate values automatically?
No. Core JMeter 5.6.3 has no automatic option for this; you add extractors yourself. The one thing it handles for you is cookies: an HTTP Cookie Manager stores and resends cookies per thread, so cookie-based sessions need no extractor. Values in bodies, headers or URLs still need one.
What does Match No. -1 do in a JMeter extractor?
Match No. -1 tells the extractor to capture every match instead of one. JMeter creates name_1, name_2 and so on, plus name_matchNr with the count. A ForEach Controller can then loop over those variables. In the Regular Expression Extractor, the plain name variable is set to the default value in this mode.
Why does my extracted variable show as ${variable} in the request?
The extractor did not run or did not match, and JMeter sends an undefined variable reference unchanged instead of failing. Check that the extractor is a child of the right sampler, that Field to check points at the body or headers where the value is, and set a default value so the failure is visible.
Sources
- JMeter component reference: Regular Expression Extractor, JSON Extractor, Boundary Extractor, CSS Selector Extractor, XPath2 Extractor, ForEach Controller, HTTP Cookie Manager, Debug Sampler, View Results Tree, checked 2026-10-25 (JMeter 5.6.3).
- JMeter user manual, Elements of a Test Plan: post-processors, scoping rules, execution order, variables are local to each thread, checked 2026-10-25.
- JMeter functions and variables: undefined references are returned unchanged, checked 2026-10-25.
- Apache JMeter home page: “JMeter does not execute the Javascript found in HTML pages”, checked 2026-10-25.
- JMeter 4.0 release notes: Boundary Extractor, checked 2026-10-25.
- Test runs: JMeter 5.6.3 (Java 11), CLI mode, 5 threads x 3 iterations against a local Python test application, 2026-10-25.