This plugin provides a bzm - HTTP Sampler (multi-protocol: HTTP/1.1, HTTP/2, HTTP/3 (QUIC)) and bzm - HTTP Async Controller so multiple BlazeMeter HTTP samplers can run overlapped in time (concurrent sampler execution controlled by that controller—not the same thing as HTTP/2 stream multiplexing on the wire). If your test plan already uses JMeter’s standard HTTP Request sampler, you can convert those elements to bzm - HTTP Sampler in a few clicks—see Migrate from JMeter HTTP Request to BlazeMeter HTTP.
Important
Requires Java 17+
Note
Compatibility: Use Java and Apache JMeter versions that are supported together for the JMeter release you run. Through JMeter 5.6.3, JMeter does not support Java versions newer than 21 — use Java 17 or Java 21 with those JMeter lines unless your vendor documents otherwise. For newer JMeter versions, follow Apache’s prerequisites in Getting Started and the release notes for that version.
- Install
- Migrate from JMeter HTTP Request to BlazeMeter HTTP
- Creating the Test Plan
- HTTP Sampler
- HTTP Async Controller
- JMeter property reference
- Logging and low-level debugging
- Building from source
- License
- Apache JMeter installed and runnable with a Java version that matches that JMeter release (see the Important / Note alerts at the top of this README).
- JMeter Plugins Manager installed inside JMeter. Plugins Manager is the usual way to add and update community plugins such as this one.
Note
Install this plugin via Plugins Manager when possible. Start from the prerequisites, then install, verify, and update as needed.
- Start JMeter and open Plugins Manager (typically Options → Plugins Manager depending on your JMeter build).
- Open the Available Plugins tab and search for BlazeMeter HTTP.
- Select it, click Apply Changes and Restart JMeter, and wait for the install to finish.
If the plugin is already installed, open Plugins Manager → Installed Plugins, find the same entry, and install updates when offered (then restart JMeter when prompted), same as other Plugins Manager extensions.
Use this path when Plugins Manager is not an option—for example offline installs.
- Open Releases for this repository and choose the plugin version that matches your JMeter line (the Latest tag is usually the right default).
- Under Assets for that release, download
jmeter-bzm-http2-<version>.jar. - Copy
jmeter-bzm-http2-<version>.jarinto<JMETER_HOME>/lib/ext. - Restart JMeter.
- In Plugins Manager → Installed Plugins, confirm the BlazeMeter HTTP plugin is listed.
- In a test plan: Add → Sampler and check that
bzm - HTTP Sampleris available. Optionally Add → Logic Controller and verifybzm - HTTP Async Controller.
To build the JAR yourself from this repository, see Building from source below.
If your plan uses Apache JMeter’s standard HTTP Request sampler, convert those elements to bzm - HTTP Sampler to use HTTP/1.1, HTTP/2, and HTTP/3 from this plugin.
The tool replaces HTTP Request → BlazeMeter HTTP in the test tree.
Use this path when:
- Your
.jmxdrives HTTP traffic with JMeter’s stock HTTP Request sampler (tree label HTTP Request; element typeHTTPSamplerProxy, or legacyHTTPSampleron very old plans). - You want
bzm - HTTP Samplerand its multi-protocol client (HTTP/1.1, HTTP/2, HTTP/3, profiles, Jetty behavior) without manually re-adding every request. - You are not starting from a blank plan (for that, see Creating the Test Plan below).
Open Tools → BlazeMeter HTTP and choose one of:
| Menu item | Behavior |
|---|---|
| Migrate Entire Test Plan (HTTP Request → BlazeMeter HTTP)… | Scans the whole tree and replaces every migratable HTTP Request sampler after confirmation. |
| Migrate Selected (HTTP Request → BlazeMeter HTTP)… | Replaces only selected tree nodes that qualify; non-HTTP selections are listed as skipped in the confirm dialog. |
Recommended flow:
- Install and verify the plugin.
- Open your
.jmxin JMeter (stop any running test before migrating). - Save a backup of the test plan—migration cannot be undone.
- Tools → BlazeMeter HTTP → run Migrate Entire Test Plan… or Migrate Selected….
- Confirm the sampler count in the dialog.
- Review migrated
bzm - HTTP Samplernodes; tune Advanced → Client Behavior (profiles, HTTP/2, HTTP/3) as needed. - Save the updated
.jmx.
[!NOTE] Preserved during migration:
- Sampler settings from the original HTTP Request (URL, method, body, timeouts, redirects, etc.)—properties are copied; only the GUI/test class names change to BlazeMeter HTTP.
- Child elements under each HTTP Request (assertions, timers, preprocessors, etc.)—they stay attached under the new sampler.
- Tree position—same parent and index in the test plan.
- Selection—after a batch migration, tree selection is restored to the new nodes where possible.
Already using JMeter’s standard HTTP Request sampler? Migrate from JMeter HTTP Request to BlazeMeter HTTP.
No script yet? Pick an option below.
This plugin can act as a sampler creator during JMeter proxy recording; it is enabled by default.
-
Use JMeter’s HTTP(S) Test Script Recorder as usual.
-
As the elements are recorded, the plugin creates the BlazeMeter HTTP Sampler automatically.
Note
To disable recording support, use Tools → BlazeMeter HTTP → Disable Recording with HTTP(S) Test Script Recorder (restart when prompted).
You can also set blazemeter.http.proxy_enabled=false in user.properties (legacy HTTP2Sampler.proxy_enabled is accepted too).
The recorder creates bzm - HTTP Sampler elements, but you may still want to adjust protocol/profile settings afterwards.
This plugin is also compatible with the BlazeMeter Automatic Correlation Recorder recording method.
For more information: BlazeMeter Automatic Correlation Recorder - Installing the Plugin
Note
Since BlazeMeter Automatic Correlation Recorder inherits and is compatible with the JMeter recorder, the process of enabling and disabling Recording support is the same as the method documented in Option A.
-
Create a Thread Group.
-
Add the HTTP Sampler (Add → Sampler → bzm - HTTP Sampler).
- Configure the sampler URL + client behavior (profile/protocols) as described below.
- Add timers, assertions, listeners, etc.
Note
Where possible the sampler aligns with behaviors you know from JMeter’s HTTP Request. There are deliberate differences wherever multi-protocol negotiation, QUIC, or the Jetty client requires them.
The sampler UI has two tabs: Basic (URL + request basics) and Advanced (client behavior + timeouts/proxy/embedded resources).
This is the standard JMeter URL configuration panel (domain, port, path, method, redirects, body, etc.).
| Field | Description | Default |
|---|---|---|
| Protocol | http or https (URL scheme). This is independent from HTTP/1.1 vs HTTP/2 vs HTTP/3 negotiation. |
http |
| Server name or IP | Target host (no http:// prefix). |
|
| Port number | Target port. | 80 for http, 443 for https |
| Method | Standard HTTP methods supported by JMeter. | GET |
| Path | Path + query (e.g. /api/v1/items?limit=10). |
/ |
| Content encoding | Character encoding used for request body (unrelated to HTTP Content-Encoding header). |
|
| Redirect Automatically / Follow Redirects | Same semantics as JMeter’s HTTP Request sampler. | |
| Use KeepAlive | Reuse connections when possible (same semantics as JMeter’s HTTP Request sampler). | |
| Browser-compatible headers | Add headers that mimic common browser behavior (same semantics as JMeter’s HTTP Request sampler). | |
| Use multipart/form-data | Same semantics as JMeter’s HTTP Request sampler. |
A profile is a named bundle of protocol defaults (which protocols are enabled, whether ALPN is enabled, fallback/caches, and timings). Profiles make it easy to mimic “how browsers behave” vs “legacy compatibility”.
The available profiles are:
- Browser-like (Most Browsers) (
browser-like) (default) - Browser-compatible (Other Browsers) (
browser-compatible) - Legacy / Older Systems (
legacy) - Browser-like (Custom) (
browser-like-custom) (persists per-sampler toggles below)
| Control | What it does |
|---|---|
| Enable HTTP/3 (Alt-Svc + QUIC) | Allows HTTP/3 when the origin advertises it via Alt-Svc. Typically requires https and UDP/QUIC reachability. |
| Enable HTTP/2 | Allows HTTP/2. Over TLS this is typically negotiated via ALPN. For cleartext origins see H2C options below. |
| Enable HTTP/1.1 | Allows HTTP/1.1. If disabled, the client will avoid HTTP/1.1 unless forced by server/proxy constraints. |
| H2C Upgrade (HTTP/1.1 Upgrade) | For cleartext (http://) origins, allow HTTP/1.1 → HTTP/2 (h2c) upgrade. Visible when HTTP/1.1 and HTTP/2 are enabled. |
| Enable ALPN | Enables TLS ALPN for HTTPS (HTTP/1.1 and HTTP/2). (HTTP/3 discovery uses Alt-Svc, not ALPN.) Cleartext h2c uses upgrade/prior-knowledge flows instead of TLS ALPN. |
| Automatic fallback | Enables automatic fallback between protocols (e.g. prefer HTTP/3, then fall back to HTTP/2/1.1 as needed). |
| Fallback on HTTP/2 protocol_error | If an HTTP/2 protocol_error occurs, retry using HTTP/1.1 (when fallback is enabled). |
| Alt-Svc cache | Caches Alt-Svc advertisements used for HTTP/3 discovery. |
| HTTP/1.1-only cache (HTTPS) | Caches “this HTTPS origin should be treated as HTTP/1.1-only” after failures, to avoid repeated negotiation overhead. |
| H2C cache (HTTP) | Caches h2c capability for cleartext origins. |
| HTTP/2 prior knowledge for cleartext (h2c) | Forces HTTP/2 prior knowledge (h2c) for cleartext origins, skipping the upgrade dance. Use only if you know the server speaks h2c. |
| Happy Eyeballs delay (ms) | When trying HTTP/3, delay before starting HTTP/2 in parallel (H3+H2) to reduce latency while avoiding extra connections. |
| HTTP/3 broken cooldown (ms) | How long to consider HTTP/3 “broken” for an origin after failures before retrying. |
| HTTP/1.1-only cache TTL (ms) | How long to keep the “HTTP/1.1-only” decision for an HTTPS origin. |
| H2C cache TTL (ms) | How long to keep the “h2c supported” decision for a cleartext origin. |
| Field | Description |
|---|---|
| Connect | Milliseconds to wait for a connection to open. |
| Response | Milliseconds to wait for a response. |
| Field | Description |
|---|---|
| Scheme | Proxy scheme (typically http). |
| Server name or IP | Proxy host. |
| Port Number | Proxy port. |
| Field | Description |
|---|---|
| Retrieve All Embedded Resources | Parses HTML and downloads embedded resources (images, JS, CSS, etc.). |
| Parallel downloads | When enabled, embedded resources are fetched concurrently; “pool size = 1” forces synchronous behavior. |
| URLs must match | Regex filter for which embedded resources to download. |
The simplest way is to pick a profile and only override when you need a specific behavior.
Common configurations:
- Prefer HTTP/3, fallback to HTTP/2/1.1: profile
browser-like(default). Ensure Enable HTTP/3 and Automatic fallback are enabled. - HTTP/2-only (TLS): disable Enable HTTP/1.1, disable Enable HTTP/3, keep Enable HTTP/2 enabled. (For
https://origins, ALPN typically must be enabled.) - HTTP/1.1-only: keep Enable HTTP/1.1 enabled, disable HTTP/2 and HTTP/3.
- Cleartext h2c (upgrade): use
http://scheme, enable HTTP/1.1 + HTTP/2, and enable H2C Upgrade. - Cleartext h2c (prior knowledge): use
http://scheme, enable HTTP/2, enable HTTP/2 prior knowledge for cleartext (h2c) (only if the server supports it).
Note
HTTP/3 is discovered via Alt-Svc (and optionally cached), not via ALPN.
By default there is no store cap (blazemeter.http.maxBufferSize unset → effective -1): Jetty buffers without a hard fail (unlike Jetty’s own BufferingResponseListener 2 MiB default), and the full decoded body is kept in the sample.
When a positive limit applies, behaviour matches Apache JMeter’s
httpsampler.max_bytes_to_store_per_request: the sample stays successful, only the first N
bytes are stored in response data, bodySize reflects the full decoded length, and JMeter logs
Big response, truncating it to {} bytes at DEBUG. Truncation is skipped while recording.
Precedence (first match wins):
- Plugin
blazemeter.http.maxBufferSize(aliasesHTTP2Sampler.maxBufferSize/httpJettyClient.maxBufferSize) if set - Else JMeter
httpsampler.max_bytes_to_store_per_requestif set - Else
-1(no truncation)
<= 0 means do not truncate.
The plugin uses TLS ALPN so HTTPS connections can advertise HTTP/1.1 and HTTP/2 (h2). HTTP/3 discovery is separate—it relies on Alt-Svc (see Protocols above), not TLS ALPN.
Cleartext h2c never uses TLS ALPN; negotiation uses HTTP/1.1 Upgrade or prior knowledge, matching the http:// options described elsewhere in this README.
For TLS keystores/truststores, see JMeter’s SSL Manager and Keystore Configuration.
Currently, we only support the Basic and Digest authentication mechanisms.
To use Basic preemptive authentication, set blazemeter.http.auth.preemptive to true in jmeter.properties or user.properties.
HTTP/2 multiplexing means many requests can share one TLS/TCP connection as separate streams—the Jetty stack does this whenever negotiation selects HTTP/2.
Overlapping sampler execution (several BlazeMeter HTTP samplers in progress at once within one thread iteration) requires bzm - HTTP Async Controller. The concurrent cap follows blazemeter.http.maxConcurrentAsyncInController (default 100) whenever Limit max number of parallel executions is unchecked. When limiting is checked, saves persist Max parallel as blazemeter.http.controller.maxConcurrentAsyncInController; unchecking restores the global cap at runtime whether or not stale values linger in the saved plan—see HTTP Async Controller and Controller panel. Tune blazemeter.http.maxRequestsPerConnection (default 100) for Jetty concurrency per pooled connection.
Important
Outside bzm - HTTP Async Controller, a Thread Group still runs BlazeMeter HTTP samplers one after another (the sampler returns before JMeter proceeds). That sequential test-plan pacing is orthogonal to HTTP/2 transport multiplexing.
Add it with Add → Logic Controller → bzm - HTTP Async Controller. bzm - HTTP Sampler nodes that sit directly under this logic controller start their HTTP calls without blocking each other, so several requests can be in flight at once within the same thread iteration, up to the concurrency cap (Multiplexing, HTTP/2, and overlapping sampler execution and Controller panel below).
| Field | Description | Default |
|---|---|---|
| Generate Parent Sample | Wraps child BlazeMeter HTTP results in one parent sample (sub-results in listeners/reports). | Off (false) |
| Include duration of timer and pre-post processors in generated sample | Only applies with Generate Parent Sample on. When off, the parent sample measures the requests only: from the first one sent to the last response received, so think times and pre/post-processor time are reported as idle time instead of transaction time. When on, the parent sample spans the whole controller, pauses included (JMeter's Transaction Controller behaviour). | Off (false) |
| Limit max number of parallel executions | When off, the cap is blazemeter.http.maxConcurrentAsyncInController. When on, cap is Max parallel (saved in the .jmx only while this is on; runtime still uses the global cap when off). |
Off (false) |
| Max parallel | Highest number of overlapping BlazeMeter HTTP samplers when limiting is on (integer ≥ 1). Read-only when limiting is off (shows the effective global cap). | 100 (matches blazemeter.http.maxConcurrentAsyncInController unless you override; with limiting on, use the value you enter) |
In View Results Tree, rows may follow completion order, not test-plan order.
Note
Anything else you add as a direct child—timers, a different sampler type, an assertion, another controller, etc.—still runs in tree order. Before that element runs, the controller waits until every BlazeMeter HTTP request started above it has completed. Use that pattern when you mean “kick off these BlazeMeter HTTP calls together, then run the following steps only after they are all done.”
Note
When a Thread Group runs for a duration, the requests this controller had already sent when the time ran out are still collected and reported, the same way JMeter lets a running request finish before it stops a thread. Nothing new is dispatched once the time is up, so no load reaches the system under test past the end of the run; the thread ends as soon as the responses it was already waiting for arrive. Tune or disable that grace with blazemeter.http.schedulerHoldMillis. A Stop is immediate and is never held off.
Restart JMeter after changing JMeter properties that are applied when affected classes initialize.
The diagnostic switches are not in this table: blazemeter.http.lowLevelLog is read as a JVM system property, not as a JMeter property — see Logging and low-level debugging.
| Attribute | Description | Default |
|---|---|---|
| blazemeter.http.proxy_enabled | When true, the HTTP(S) Test Script Recorder creates bzm - HTTP Sampler instead of stock HTTP Request (legacy HTTP2Sampler.proxy_enabled accepted) |
true |
| blazemeter.http.maxBufferSize | Max bytes stored in sample response data (<=0 / unset default path = no truncation). Overrides JMeter store limit when set |
-1 |
| blazemeter.http.minThreads | Minimum number of threads per HTTP client | 1 |
| blazemeter.http.maxThreads | Maximum number of threads per HTTP client | 5 |
| blazemeter.http.maxRequestsQueuedPerDestination | Maximum number of requests that may be queued to a destination | 32767 |
| blazemeter.http.maxConnectionsPerDestination | Sets the maximum number of connections to open to each destination | 100 |
| blazemeter.http.byteBufferPoolFactor | Factor applied when allocating buffers for the HTTP client | 4 |
| blazemeter.http.strictEventOrdering | Force request events ordering | false |
| blazemeter.http.sharedThreadPool | Use a shared thread pool across HTTP clients | false |
| blazemeter.http.idleTimeout | Max time, in milliseconds, a connection can be idle | 60000 |
| blazemeter.http.removeIdleDestinations | When false, disables destination idle timeout (client keeps destinations without expiring them due to idleness) |
true |
| blazemeter.http.auth.preemptive | Use of Basic preemptive authentication results | false |
| blazemeter.http.maxConcurrentPushedStreams | Maximum number of server push streams concurrently received | 100 |
| blazemeter.http.maxRequestsPerConnection | Maximum Jetty HTTP requests per pooled connection | 100 |
| blazemeter.http.maxConcurrentAsyncInController | Default concurrency cap inside bzm - HTTP Async Controller when parallel limiting is unchecked |
100 |
| blazemeter.http.schedulerHoldMillis | How far bzm - HTTP Async Controller may push a thread's scheduled end while its requests are still on the wire, so a duration-limited run collects the responses it already asked for instead of dropping them. Renewed while requests are pending and given back as soon as they are collected; a Stop is never held off. Set to 0 to let the schedule cut mid-flight |
2000 |
| HTTPSampler.response_timeout | Default response timeout (ms) when the sampler defines none | 0 |
| http.post_add_content_type_if_missing | Add Content-Type header if missing? | false |
| blazemeter.http.enableHttp3 | Enable HTTP/3 support (Alt-Svc + QUIC) | profile |
| blazemeter.http.enableHttp2 | Enable HTTP/2 support | profile |
| blazemeter.http.enableHttp1 | Enable HTTP/1.1 support | profile |
| blazemeter.http.alpnEnabled | Enable ALPN | profile |
| blazemeter.http.fallbackEnabled | Enable automatic fallback between protocols | profile |
| blazemeter.http.protocolErrorFallbackEnabled | Enable fallback to HTTP/1.1 for HTTP/2 protocol_error | profile |
| blazemeter.http.disableFallback | Legacy flag (inverse of protocolErrorFallbackEnabled) | false |
| blazemeter.http.goawayRetryEnabled | Enable retry on HTTP/2 GOAWAY | true |
| blazemeter.http.maxGoawayRetries | Max retries on GOAWAY before fallback | 1 |
| blazemeter.http.altSvcCacheEnabled | Enable Alt-Svc cache for HTTP/3 discovery | profile |
| blazemeter.http.http1OnlyCacheEnabled | Enable HTTP/1.1-only cache for HTTPS origins | profile |
| blazemeter.http.h2cCacheEnabled | Enable H2C cache for cleartext origins | profile |
| blazemeter.http.h2cUpgradeEnabled | Enable H2C Upgrade (HTTP/1.1 Upgrade) | false |
| blazemeter.http.h2cCacheTtlMs | H2C cache TTL in milliseconds | profile |
| blazemeter.http.http1OnlyCooldownMs | HTTP/1.1-only cache TTL in milliseconds | profile |
| blazemeter.http.http3BrokenCooldownMs | Cooldown before retrying HTTP/3 after failures (ms) | profile |
| blazemeter.http.happyEyeballsDelayMs | Delay before starting HTTP/2 fallback for HTTP/3 (ms) | profile |
| blazemeter.http.http2PriorKnowledge | Force HTTP/2 prior knowledge for cleartext origins (h2c) | false |
| blazemeter.http.quicMaxIdleTimeout | QUIC max idle timeout in milliseconds | 30000 |
| blazemeter.http.quicMaxBidirectionalStreams | QUIC max bidirectional streams | 100 |
| blazemeter.http.quicMaxUnidirectionalStreams | QUIC max unidirectional streams | 100 |
| blazemeter.http.settingsMaxHeaderListSize | HTTP/2 SETTINGS_MAX_HEADER_LIST_SIZE | 4096 |
| blazemeter.http.controller.generateParentSample | If you group all requests into a parent sample | false |
| blazemeter.http.controller.limitMaxParallel | Limit max number of parallel executions | false |
| blazemeter.http.controller.maxConcurrentAsyncInController | Maximum parallel requests (integer ≥ 1) | 100 |
The plugin logs through JMeter's own logging, so its lines land in jmeter.log next to JMeter's, under logger names like c.b.j.h.c.HTTP2Controller and c.b.j.h.c.HTTP2JettyClient. Warnings and errors are on by default. Everything below is for diagnosing a run and should be off during a load test: with both switches on, a single-thread nine-second run with two requests per iteration wrote about 2900 lines, and that scales with threads and requests.
Two switches, and they do different things:
| Switch | What it does |
|---|---|
-Dblazemeter.http.lowLevelLog=true |
Opens the plugin's internal traces: every dispatch and completion in the async controller, the request/response steps in the client, protocol negotiation and fallback decisions, plus HTTP/2 frame logging. A JVM system property — see the note below |
-Lcom.blazemeter=DEBUG |
Raises the log level so DEBUG lines are written at all. Without it the switch above produces nothing |
Important
-Dblazemeter.http.lowLevelLog=true, not -J. This one is read as a JVM system property, so JMeter's -J (test-plan properties) does not set it. In the GUI, or in any launcher script, pass it through JVM_ARGS instead: set JVM_ARGS=-Dblazemeter.http.lowLevelLog=true on Windows, export JVM_ARGS="-Dblazemeter.http.lowLevelLog=true" on Linux/macOS.
The packaged JAR relocates its Jetty into com.blazemeter.jmeter.http2.shaded.org.eclipse.jetty, which sits under com.blazemeter. So -Lcom.blazemeter=DEBUG also turns on all of Jetty's internal logging — selectors, connection pools, buffer pools, TLS — and that is not something the plugin's own switch has any say over. On a two-iteration plan with two requests each, that is 2659 Jetty lines against 261 from the plugin.
Silence that package and the log becomes readable:
java -Dblazemeter.http.lowLevelLog=true -jar $JMETER_HOME/bin/ApacheJMeter.jar \
-n -t plan.jmx -l results.jtl -j run.log \
-Lcom.blazemeter=DEBUG -Lcom.blazemeter.jmeter.http2.shaded=INFOFor a permanent setting, or for the GUI, put the same two levels in $JMETER_HOME/bin/log4j2.xml:
<Loggers>
<Logger name="com.blazemeter" level="debug"/>
<!-- The plugin's relocated Jetty: leave it at info or the plugin's own lines are buried. -->
<Logger name="com.blazemeter.jmeter.http2.shaded" level="info"/>
</Loggers>Turn Jetty's own logging on only when the question is about the transport itself (a stalled connection, a TLS handshake, HTTP/2 frames) — by dropping that second logger, or by raising just one package, e.g. -Lcom.blazemeter.jmeter.http2.shaded.org.eclipse.jetty.io=DEBUG.
Note
With the low-level switch on, the client also appends HTTP/2 frame traces to an http2-debug.log file, resolved relative to the working directory JMeter was started from (<cwd>/jmeter-http2-plugin/target/http2-debug.log). It is created only while that switch is on.
Uses Maven 3 from the repository root:
mvn clean package
Artifacts land under target/. Compilation uses the jmeter.version declared in pom.xml; at runtime install the packaged JAR against the JMeter build you intend to run and validate with a short smoke plan.
To compare HttpClient4 with the migrated BlazeMeter HTTP sampler against Apache’s official bin/testfiles JMX plans, see docs/jmeter-regression.md.
mvn -Dcheckstyle.skip=true package
mvn -Pjmeter-regression verifyDistributed under the Apache License 2.0. See LICENSE in this repository.




