JFR In-Process Data Redaction in Java 27 (JEP 536): A Beginner’s Guide

  • Last Updated: October 18, 2026
  • By: javahandson
  • Series
img

JFR In-Process Data Redaction in Java 27 (JEP 536): A Beginner’s Guide

JFR In-Process Data Redaction (JEP 536) masks secrets inside the JVM in Java 27. Learn how it works, how to configure redact-key and redact-argument.

1. Introduction

Java Flight Recorder (JFR) is a powerful, low-overhead tool for diagnosing JVM problems. Yet it carries a quiet risk. The recordings it produces can hold secrets. JFR In-Process Data Redaction, delivered as JEP 536 in Java 27, solves this problem at the source. It hides sensitive values before they ever reach the recording file.

In older Java versions, a flight recording could capture your database password or API token in plain text. The value just sat there inside the file. Anyone who opened it could read the secret. So teams had to scrub recordings by hand, which is slow and easy to forget.

This guide explains the feature from the ground up. We will keep the language simple and the examples practical. By the end, you will know:

  • what JFR records, and why that can leak secrets
  • what JEP 536 actually changes in Java 27
  • how redaction works inside the running JVM
  • how to configure patterns with a couple of simple flags
  • what the feature does not protect, so you avoid nasty surprises

Let us begin with a short refresher on JFR itself.

2. A Quick Refresher on JFR

Before the new feature makes sense, you need a clear picture of JFR. So let me keep this part short and friendly.

2.1 What JFR Records

JFR is built right into the JVM. It records events while your application runs. These events cover garbage collection, thread activity, method timing, and much more. The overhead stays tiny, so you can even run it in production.

Here is the key point for our topic. At startup, JFR also captures the details of how your app was launched. That means three things in particular:

  • environment variables, like DB_PASSWORD or API_TOKEN
  • system properties, the ones you set with the -D flag
  • the full command line used to start the application

These details are genuinely useful. They help you reproduce a bug with the exact same setup. Sadly, they are also where secrets love to hide.

Why does the JVM bother capturing all this? The reason is reproducibility. When a bug shows up only with a certain flag or variable, that startup snapshot is gold. You can recreate the exact conditions later. Unfortunately, that same strength is what makes a leak so easy.

2.2 Why Recordings Can Leak Secrets

Think about how a typical service starts. You often pass secrets through the environment or the command line. A simple example looks like this:

export STRIPE_API_KEY=sk_live_abc123
 
java -Ddb.password=S3cr3t! \
     -jar payments-service.jar

Both the system property and the environment variable now belong to the JVM startup. As a result, JFR records them. Before Java 27, it stored the raw value with no masking.

Now picture sharing that recording file. Maybe you attach it to a support ticket. Perhaps you drop it on a shared drive for a teammate. The secret travels along with it. I have seen this exact slip in banking projects, where a shared recording quietly carried a live credential.

3. The Problem JEP 536 Solves

3.1 The Old Way: Clean It Up Yourself

Before Java 27, the JVM gave no automatic help here. You had two options, and honestly neither was great.

The first option was pure discipline. You tried never to pass secrets on the command line. Instead, your app read them from a vault or a file at runtime. That is good practice, yet mistakes still creep in. One rushed deployment, and a secret slips onto the command line again.

The second option was cleanup after the fact. Java shipped a tool called jfr scrub for this. You run it on a finished recording to filter out certain events or fields. It works, but it has real limits:

  • The secret already exists in the original file, so you must scrub every copy.
  • jfr scrub often removes a whole event, not one field. You lose useful data with the secret.
  • It is a manual, extra step. People simply forget it under pressure.

3.2 Why In-Process Redaction Is Better

JEP 536 takes a smarter route. The redaction happens inside the JVM, while the recording is being written. The raw secret never lands on disk at all.

Compare the two models for a moment:

  • jfr scrub cleans a file that already holds the secret. It is a cure.
  • JEP 536 stops the secret from being written in the first place. It is prevention.

Prevention is almost always safer. There is no original file to leak. There is no cleanup step to forget. The secret simply never reaches the recording.

💡 Interview Insight
Interviewers often ask for the difference between jfr scrub and JEP 536. Here is the clean answer: jfr scrub cleans an existing recording after the fact, while JEP 536 redacts inside the JVM before anything is written to disk. One is a cure, the other is prevention.

4. What JEP 536 Actually Does

Now let us look at the feature itself. The idea is simple, even though the name sounds heavy.

4.1 Redaction Inside the JVM

“In-process” simply means “inside the running JVM”. As JFR prepares to write your startup data, it checks each value against a set of patterns. If a value looks like a secret, the JVM replaces it. Only the safe version then reaches the file.

So the flow is short and clear:

  • JFR collects a value, such as an environment variable.
  • The redaction engine checks the name or text against patterns.
  • On a match, the value becomes [REDACTED].
  • Finally, the safe version is written into the recording.
Diagram of how JEP 536 redacts secrets inside the JVM before the .jfr file is written.

4.2 Which Data Is Covered

JEP 536 focuses on the three startup sources we just saw. In JFR terms, these map to specific events:

  • jdk.InitialEnvironmentVariable — your environment variables
  • jdk.InitialSystemProperty — properties set with -D
  • jdk.JVMInformation — the full command line, including program arguments

For environment variables and system properties, redaction matches on the name. For command-line arguments, it can also match on the argument text. We will try both a little later.

4.3 What a Redacted Value Looks Like

The replacement is deliberately boring. A matched value gets swapped for the text [REDACTED]. The name stays visible, but the value is gone.

So inside the recording, you might see something like this:

DB_PASSWORD    = [REDACTED]
STRIPE_API_KEY = [REDACTED]
db.user        = payments_app      # not a secret, kept as-is

This strikes a nice balance. You still know which variables existed, which helps during debugging. Yet the actual secret is never present.

4.4 It Is On by Default

Here comes the best part for most teams. Redaction is enabled by default in Java 27. You do not add a flag to switch it on. The moment your app runs on Java 27, the built-in patterns already protect you.

Put another way, upgrading to Java 27 gives you this safety for free. You only touch the configuration when you want to add your own patterns or, in rare cases, turn it off.

💡 Interview Insight
A common question is whether you must enable JEP 536 manually. The answer is no. Redaction is on by default in Java 27 with a built-in pattern list. You configure it only to extend the patterns or to disable it on purpose.

5. Configuring Redaction

Most teams never need to configure anything. Still, it helps to know the knobs. All settings live under one JVM flag: -XX:FlightRecorderOptions.

5.1 The Two Sub-Options

There are two sub-options, and their names tell you what they do:

  • redact-key — matches the name of an environment variable, system property, or -D key. A match hides the value.
  • redact-argument — matches the text of a command-line argument. A match hides that whole argument.

So redact-key protects named values. Meanwhile, redact-argument protects loose arguments on the command line. Together they cover all three sources.

You will almost always use redact-key. Most secrets live in named values anyway. The redact-argument option is for the odd case where a secret rides along as a bare argument. A JDBC URL is the classic example, and we will cover it later.

5.2 How Patterns Work

The patterns are simple glob patterns, not full regular expressions. A few rules cover everything:

  • The star * is a wildcard. It matches any run of characters.
  • Matching is case-insensitive, so *TOKEN* also matches “token” and “Token”.
  • You separate multiple patterns with a semicolon.

For example, the pattern *secret* matches CLIENT_SECRET, my_secret_key, and Secret123. It is a wide net, and that is intentional.

5.3 Extend or Replace the Defaults

This part trips up beginners, so please read it twice. When you set your own patterns, you usually replace the built-in list rather than adding to it.

To keep the defaults and add your own, start the value with a plus sign:

# ADD "confidential" on top of the built-in defaults
java -XX:FlightRecorderOptions:redact-key=+*confidential* \
     -jar app.jar
 
# REPLACE all defaults with a single pattern (risky!)
java -XX:FlightRecorderOptions:redact-key=*confidential* \
     -jar app.jar

Notice the difference between the two lines. The first keeps every built-in pattern and adds one more. The second throws away all the built-in protection and keeps only your single pattern. That second form is a classic way to leak secrets by accident.

5.4 Load Patterns from a File

Long pattern lists on the command line get messy fast. So JEP 536 lets you point to a file with the @ symbol. The file holds one pattern per line.

# keys.txt
*password*
*token*
*merchant*secret*
 
# use the file:
java -XX:FlightRecorderOptions:redact-key=@keys.txt -jar app.jar

This option is handy for teams. You keep one shared file of patterns and reuse it across services. One place to update, and everywhere stays protected.

5.5 Turning Redaction Off

Sometimes you want recordings with no redaction at all. For instance, on a local machine that only has fake test data. The special value none disables it:

java -XX:FlightRecorderOptions:redact-key=none,redact-argument=none \
     -jar app.jar

Use this with care. Turn it off only where you are sure there are no real secrets. On anything close to production, always leave redaction on.

💡 Interview Insight
An interviewer may ask how to add a custom pattern without losing the defaults. The one-word answer is the + prefix. Without it, your patterns replace the whole built-in list. Pointing out that trap shows real, hands-on understanding.

6. The Built-in Default Patterns

6.1 What Ships Out of the Box

The default list targets the words that secrets usually contain. The exact set can grow across releases, but it covers the common cases well. Representative patterns include:

  • *password*, *passwd*, *pwd*, *passphrase*
  • *token*, *secret*, *credential*
  • *api*key*, *private*key*, *client*secret*
  • *auth*

So common names like DB_PASSWORD, API_TOKEN, and CLIENT_SECRET get caught right away. You enjoy sensible coverage without writing a single pattern.

6.2 Why the Patterns Match Generously

You may notice the patterns are quite broad. That is a design choice, not an oversight. The team behind JEP 536 chose to redact a little too much rather than too little.

Think about the trade-off for a second. If redaction misses a real secret, that secret leaks. If it hides one harmless value by mistake, you lose a small piece of debug data. The second cost is tiny next to the first. Therefore, when in doubt, the JVM hides the value.

6.3 Checking the Active Patterns

You might wonder which patterns are active right now. There is a simple way to check. Ask the JVM to print its FlightRecorderOptions help. The output lists the current redaction settings, so you never have to guess what is protected.

java -XX:FlightRecorderOptions:help

Run this on your target Java 27 build. The exact default list can shift a little between minor releases. A quick check keeps you honest about what is covered.

7. Hands-On: Try It Yourself

Reading is fine, but watching it work really sticks. So let us run a tiny test. You only need Java 27 installed.

7.1 Start a Recording with a Secret

Launch any small app with a fake secret and a recording turned on:

java -DDB_PASSWORD=SuperSecret123 \
     -XX:StartFlightRecording=duration=10s,filename=demo.jfr \
     -jar hello.jar

Here we pass a system property named DB_PASSWORD. Its name contains “password”, so a default pattern should match it.

7.2 Inspect the Recording

After the app finishes, read the recording with the jfr command. We look at the system property event:

jfr print --events jdk.InitialSystemProperty demo.jfr

In the output, find the DB_PASSWORD entry. On Java 27, its value shows as [REDACTED], not SuperSecret123. The secret never made it into the file.

7.3 Add Your Own Pattern

Now suppose your company uses a key named MERCHANT_SIGNING. That name has no “secret” or “password” in it. As a result, the defaults miss it. Add your own pattern, but keep the defaults with +:

java -DMERCHANT_SIGNING=abc-123 \
     -XX:FlightRecorderOptions:redact-key=+*merchant* \
     -XX:StartFlightRecording=duration=10s,filename=demo2.jfr \
     -jar hello.jar

Run the jfr print command again. This time MERCHANT_SIGNING also shows [REDACTED]. You have extended the protection to your own naming style.

7.4 A Quick Note on Overhead

You may worry about speed. Does all this matching slow things down? In practice, no. The checks run once at startup, over a short list of values. The cost is tiny, and you will not notice it. So there is no real reason to switch redaction off for performance.

8. Real-World Usage Patterns

Let me share a few ways I would actually use this in a real service.

8.1 Hiding Database URLs with Credentials

A classic leak is a JDBC URL with the password baked in. For example:

jdbc:postgresql://user:S3cr3t@db-host:5432/payments

The whole URL often arrives as a command-line argument. So redact-argument is the right tool here. You match the argument text, not a key name:

java -XX:FlightRecorderOptions:redact-argument=+jdbc:*//*:*@* \
     -jar app.jar

The pattern targets the shape of a JDBC URL that carries credentials. When it matches, the whole argument is hidden. Your recording no longer carries a live database password.

8.2 A Shared Pattern File per Team

In a company with many services, repeating patterns everywhere gets painful. So I would keep a single file in a shared config repo:

# company-redaction-keys.txt
*password*
*token*
*secret*
*merchant*
*signing*

Every service then points to the same file. When security adds a new sensitive word, you update one place. All services pick it up on their next restart.

8.3 Rolling It Out Safely

When you upgrade a fleet to Java 27, redaction turns on by itself. That is good news. Even so, I would run two quick checks:

  • Grab one recording per service and confirm real secrets show [REDACTED].
  • Check that no important, non-secret field got hidden by a too-wide custom pattern.

These checks take only minutes. They give you real confidence that the feature helps, instead of hiding the wrong things.

8.4 Containers and Kubernetes

Containers lean heavily on environment variables. A Kubernetes pod often injects secrets as env vars at startup. So a flight recording from a pod is a prime candidate for a leak.

The good news is that JEP 536 works the same way inside a container. The JVM does not care where the env var came from. A variable named PAYMENT_API_TOKEN still matches the default *token* pattern. So your pod recordings stay clean too.

Here is one practical tip for Kubernetes teams. Map your secret names to the default patterns on purpose. Name a secret CARD_VAULT_TOKEN, not CARD_VAULT_X. The first name matches out of the box, while the second needs a custom pattern.

9. What JEP 536 Does Not Cover

This section matters most for avoiding a false sense of safety. Redaction is strong, yet it has clear limits. Please do not assume it protects everything.

JEP 536 does not touch these areas:

  • Heap dumps. A dump from -XX:+HeapDumpOnOutOfMemoryError can still hold secrets in memory.
  • Custom JFR events. If your own event has a field like apiKey, JFR records it as-is, so you must sanitize it yourself.
  • Secrets inside thread names or log messages captured by other means.
  • Plain strings sitting in stack traces or error messages.
  • Data captured by third-party profilers that bypass JFR’s startup events.

Let me stress the custom-events point, because it bites people. Say you build an event to track payment calls. You add fields for the amount and a card token. JFR will store whatever you put there. The redaction engine does not look inside your custom fields. So you must mask that token in your own code, before you commit the event.

It also does not fix old recordings. The feature works going forward only. A recording made on Java 26 stays exactly as it was.

So treat JEP 536 as one strong layer, not the whole wall. Keep your other good habits in place too. Read secrets from a vault. Avoid putting them on the command line whenever you can.

💡 Interview Insight
A sharp follow-up question is what JEP 536 does not protect. Good points to raise: heap dumps, custom JFR event fields, and existing recordings. Saying “it is one layer, not a complete solution” signals mature, security-aware thinking.

10. Best Practices and Common Mistakes

10.1 Best Practices

  • Stay on the defaults unless you have a real reason to change them.
  • When you add patterns, always use the + prefix to keep the built-ins.
  • Keep team patterns in one shared file with the @ syntax.
  • Still prefer vaults and secret managers over command-line secrets.
  • Sanitize fields in your own custom JFR events by hand.
  • Verify one real recording after upgrading, just to be safe.

10.2 Common Mistakes

  • Forgetting the + prefix, which silently drops every default pattern.
  • Assuming redaction also cleans heap dumps, which it does not.
  • Trusting it on old recordings, when it only protects new ones.
  • Writing one super-wide pattern like * that hides everything useful.
  • Turning redaction off on a server “just for now” and never turning it back on.
  • Believing the feature removes the need for a vault, because it does not.

11. FAQ’s on JFR In-Process Data Redaction

Q: Is JFR In-Process Data Redaction enabled by default in Java 27?

A: Yes. JEP 536 is on by default in Java 27 with a built-in list of patterns. You only touch the configuration to add your own patterns or to turn it off on purpose.

Q: What is the difference between jfr scrub and JEP 536?

A: jfr scrub cleans a recording after it is already on disk, so the secret existed at some point. JEP 536 redacts inside the JVM before anything is written. One is a cure, the other is prevention.

Q: How do I add a custom redaction pattern without losing the defaults?

A: Start your pattern with a plus sign, for example redact-key=+*merchant*. Without the +, your patterns replace the entire built-in list, which can silently drop protection.

Q: Does JEP 536 protect heap dumps and custom JFR events?

A: No. It only covers environment variables, system properties, and command-line arguments in JFR’s startup events. Heap dumps and your own custom event fields must be sanitized separately.

Q: Does redaction affect old recordings made on earlier Java versions?

A: No. The feature works going forward only. A .jfr file created on Java 26 stays exactly as it was; redaction applies only to new recordings on Java 27.

12. Conclusion

JFR In-Process Data Redaction is a small feature with a big payoff. For years, flight recordings could quietly carry passwords and tokens. JEP 536 finally closes that gap right at the source.

Here is the quick recap:

  • JFR records startup data, which often includes secrets.
  • JEP 536 redacts those secrets inside the JVM before writing.
  • The feature is on by default in Java 27, with a sensible pattern list.
  • You tune it with redact-key and redact-argument, and the + prefix keeps the defaults.
  • It does not cover heap dumps, custom events, or old recordings.

My advice is simple. Upgrade to Java 27, confirm one recording, and enjoy the free safety. Then layer your own patterns for the names that are special to your domain. For payments and banking work especially, that small effort pays off quickly.

13. Further Reading

Leave a Comment