What Is the BREACH Attack? How It Works and How to Stop It

Okay, deep breath. The BREACH attack sounds like a straight-to-streaming heist movie, but it is a real, very practical web attack with absolutely no charm. It picks on something nearly every website turns on for very good reasons: HTTP compression. Gzip and Brotli make pages smaller, faster, and cheaper to deliver. Wonderful. BREACH walks in and asks the rudest possible question — “what if that same lovely compression can help an attacker guess the secrets hiding in your page, one letter at a time?”

Spoiler: it can. The BREACH attack is a compression side-channel attack against HTTPS responses. It does not snap TLS like a cartoon villain with bolt cutters. Instead, it abuses a quiet little detail — compressed responses get a tiny bit shorter when their contents repeat. If an attacker can sneak text into a request and then squint at how big the encrypted reply comes back, they can slowly, patiently, annoyingly recover hidden things like CSRF tokens, password reset codes, email addresses, or other values you genuinely did not want on the front page.

Yes, really. Your site can have a perfectly modern TLS setup, an A+ on every scanner, and still gently leak secrets because compression was trying to be helpful at exactly the wrong moment. I know. Rude.

BREACH attack compression side-channel illustrated as a vacuum-packed suitcase

What Is the BREACH Attack, in Plain English?

BREACH stands for Browser Reconnaissance and Exfiltration via Adaptive Compression of Hypertext. That name is doing the absolute most, so let us put it in human language.

Imagine you are packing for a trip and you have one of those vacuum bags that suck out all the air. Stuff two fluffy sweaters in there, and the bag shrinks more than it would with just one. Now imagine a nosy neighbour who is not allowed to look inside the bag — but they can pick it up and weigh it. They sneak in different items, weigh the bag each time, and the second one of their items matches something already inside, the bag gets noticeably lighter. Congratulations, neighbour. You just learned what is in someone else’s suitcase without ever opening it.

That is BREACH. The vacuum bag is the compressed HTTP response. The sweater already inside is the secret, like a CSRF token. The attacker’s test items are bits of text reflected into the page — a search query, an error message, a URL parameter, the kind of thing that bounces back into HTML without anyone thinking twice. All the attacker really needs is the ability to trigger lots of requests and watch how big the encrypted responses come back.

The key point — and please tattoo this on your brain — BREACH is not about breaking encryption directly. TLS can be flawless. The leak comes from the size of the response. Encryption hides the words. It does not hide the page count.

How Does a BREACH Attack Actually Work?

For the BREACH attack to succeed, four ingredients usually have to show up at the same party:

  1. The victim is logged in, or the response carries a secret tied to that session.
  2. The application reflects attacker-controlled input somewhere in that same response.
  3. The response is compressed with gzip, Brotli, or similar content compression.
  4. The attacker can fire off many requests and measure how big the replies come back.

When those four line up, you have the world’s most patient, most boring guessing game. Let’s walk through it.

Step 1: A secret is sitting inside the HTML

Picture a logged-in page with a sneaky little hidden form field:

<input type="hidden" name="csrf" value="r9Gk3LxV...">

That token is there for a great reason — it stops forged form submissions. The drama starts when the same page also reflects user input, for example:

<p>You searched for: BRE...</p>

If the attacker can choose the search term, they can keep tossing in guesses that might overlap with the secret token. Whenever the guess matches a chunk of the token, gzip and Brotli go “ooh, repetition, let’s save bytes!” and the response shrinks ever so slightly. That tiny shrink is the leak.

Step 2: The attacker keeps poking

The attacker drops the victim onto a malicious page somewhere else on the internet — maybe an ad, maybe a sketchy link, maybe a popup that absolutely no one asked for. That malicious page tells the victim’s browser to send lots of requests to the target site. The browser obediently attaches the victim’s cookies (because that is what browsers do), so the target site returns the real, logged-in page with the real secret tucked inside.

The attacker cannot read the response body because of the same-origin policy. Cool, but they don’t need to. They just need the size of each encrypted reply. Each guess that happens to share characters with the secret produces a slightly smaller payload, and the attacker takes note like a stalker with a kitchen scale.

Step 3: One byte at a time, like an exceptionally patient bird

By repeating this with thousands of slightly different guesses and comparing the sizes, the attacker can recover the secret one character at a time. It is slow, fiddly, and data-hungry. It is also extremely real. Side-channel attacks always sound silly until you remember they are basically statistics in a balaclava.

Step 4: The attacker uses the stolen secret

Once a CSRF token, reset code, or other session-bound secret has been recovered, the attacker can use it to bypass the very protection that secret was supposed to provide. Which, if we are being honest, is the genuinely insulting bit. You added a CSRF token for safety. Compression turned it into a hint system. Cool. Cool cool cool.

BREACH attack measuring encrypted HTTPS response sizes on a code screen

Why HTTPS Alone Does Not Save You

This is the part that feels deeply unfair, so let’s name it: HTTPS does its job. It protects the contents of your traffic in transit. It just does not hide the brute fact that one encrypted response is 12,438 bytes and the next one is 12,431 bytes.

BREACH lives in that seven-byte difference.

Think of it like this: someone outside your living room cannot hear the actual words of your conversation through the wall. But they can absolutely tell when the room goes dead silent, when everybody laughs at once, and when somebody drops a wine glass. Metadata leaks are still leaks. Compression side-channel leaks are metadata leaks with a postgraduate degree and a smug attitude.

This is also why “just turn off TLS” is not a fix. That is fixing a squeaky brake by setting the car on fire. Please do not.

BREACH vs CRIME vs HEIST: Same Family, Different Tricks

Security researchers love naming attacks like rejected metal bands, so here is the quick family tree:

CRIME targeted TLS or SPDY compression itself. It mostly went out of style once TLS-level compression was disabled almost everywhere.

BREACH targets HTTP response body compression. That is the one that still matters today, because gzip and Brotli are still happily compressing your responses for all the right performance reasons.

HEIST and related browser-side techniques showed that even without a network vantage point, an attacker can sometimes coax the browser into revealing timing or size details. Cute. Hate it.

The unifying lesson is simple: if secrets and attacker-controlled input share the same compressed output, somebody clever will turn that into an oracle. Treat that as a law of nature.

Why Padding Is Usually False Security

Padding sounds smart at first. “If response sizes leak information, just add some random bytes so the sizes become noisy. Done. Pub?” Sadly, no pub.

Padding is usually false security because it treats the symptom, not the cause. Let’s look at why.

Random noise gets averaged out

If an attacker can fire off thousands of requests — and they can — random padding turns into background fuzz. The attack signal does not vanish. It just needs more samples. Given enough tries, averages chew through the noise and the size differences become useful again. Statistics: still in the balaclava.

Fixed padding is essentially decoration

If you always add the exact same amount of padding, you have done… almost nothing. Every response is still comparable to every other response. You have just made the whole site slightly bigger, which is an expensive way to feel safe.

Developers wildly overestimate it

This is the genuinely dangerous bit. Once padding is enabled, teams move on with confidence. “Yep, BREACH? Handled.” Meanwhile the actual vulnerability sits there untouched, because a secret and attacker-controlled reflection are still cohabiting in the same compressed response.

Padding rarely lands exactly where it matters

For padding to be useful, it must be unpredictable, carefully sized, applied consistently, and hard for an attacker to model. In real life? One template adds it. Another forgets. One response path pads before compression. Another pads after. Suddenly your safety blanket has holes, the holes are also on fire, and somebody is filming it for TikTok.

Padding can make exploitation a bit harder. That is fine as a secondary friction control. It is not a trustworthy primary defense against the BREACH attack.

What Can a BREACH Attack Actually Leak?

The poster child is the CSRF token because it loves to live directly in HTML forms. But BREACH is not married to CSRF. Any secret that ends up in a compressible response can become a target, such as:

  • CSRF tokens
  • Password reset tokens
  • Email addresses or partial personal info
  • Session-bound anti-abuse tokens
  • OAuth state values or one-time nonces
  • API keys or internal identifiers accidentally rendered into templates

If it is secret, stable enough to guess incrementally, and shares a compressed response with attacker-controlled input, it earns a spot on your threat model. No exceptions, sorry.

BREACH attack defenses protecting CSRF tokens behind a digital lock

How to Prevent the BREACH Attack Properly

Okay. Enough admiring the problem. Adult mode now.

1. Disable compression on sensitive dynamic responses

The most direct mitigation is to turn off gzip and Brotli for the responses that contain secrets and reflect input. Think login forms, account pages, authenticated search, password reset flows, settings forms, and admin panels. Basically anywhere your site whispers secret things to a logged-in user.

For NGINX or Angie, be surgical instead of nuking from orbit. You do not need to disable compression for the entire site. Static assets and public pages can stay compressed. The sensitive authenticated HTML is where you want to be cautious.

location /account/ {
    gzip off;
    brotli off;
}

location /wp-admin/ {
    gzip off;
    brotli off;
}

If you are already tuning compression on NGINX or Angie, the broader context lives in our comparison of Brotli, zstd, and zlib-ng compression.

2. Stop reflecting attacker-controlled input next to secrets

This is honestly one of the best fixes, because it kills the oracle dead. If a page contains a CSRF token, it should not also be echoing user-controlled query parameters, search terms, or any other reflected data straight into the same compressed template — at least not unless you really have to.

Separate those concerns. Move the search UI onto a different response. Push live reflection out to a public endpoint that doesn’t carry session secrets. Rewrite templates so secrets are not nestling up against attacker-controlled bytes like they’re on a first date. Less glamorous than buying a shiny new WAF module, but often dramatically more effective.

3. Use CSRF defenses that do not create a stable compression target

Yes, you still need CSRF protection. No, plain old static tokens stamped into every compressed page are not enough on their own.

Safer patterns include:

  • Per-request or masked CSRF tokens — the exposed value changes every time, so a recovered value is useless about three milliseconds later.
  • Synchronizer tokens combined with careful placement, so they are not sharing a compressed response with attacker input.
  • Double-submit cookie patterns where appropriate, with strong cookie flags and proper validation.

The goal is not “remove CSRF protection.” The goal is “do not let your CSRF token act as a reusable compression oracle.”

4. Enforce SameSite cookies

Cookies tagged with SameSite=Lax or SameSite=Strict can reduce the browser’s willingness to send authenticated cookies on cross-site requests. That matters, because BREACH usually relies on the victim’s browser cheerfully sending credentialed requests from an attacker-controlled page.

This is not a complete BREACH attack fix on its own, but it shrinks the attack surface in a real way, and it helps with garden-variety CSRF too. One of those rare security knobs that is both boring and useful — which honestly is the dream.

5. Validate Origin and Referer headers

For state-changing requests, validate the Origin header where you can, and fall back to Referer checks where it makes sense. Not perfect, not magical, but it raises the bar for cross-site abuse and pairs nicely with token-based defenses.

If your app accepts dangerous actions purely because one hidden field happened to match, you are trusting one layer to do everything. BREACH absolutely loves a single layer.

6. Rate-limit suspicious repeated requests

BREACH needs a lot of measurements. That makes rate limiting, anomaly detection, and bot friction genuinely useful as supporting controls. If one page is getting hit by thousands of tiny variant requests in a tight window, that is something your stack should notice and react to, not shrug at.

On the web-server side, you can pair app logic with controls like ModSecurity or request throttling. If you are already hardening a WordPress or NGINX stack, our ModSecurity and OWASP CRS guide and our WordPress hardening write-up cover broader request-filtering patterns that help here too.

7. Keep secrets out of compressible HTML when you can

If a secret does not need to be in the page, do not put it there. Move it server-side. Use headers or dedicated non-compressed endpoints where it fits. Ask whether the template genuinely needs to render that value at all — a shocking amount of “required” template data turns out to be legacy furniture nobody has questioned since 2019.

8. Segment public and authenticated experiences

Public pages can be aggressively cached and compressed. Authenticated pages deserve stricter handling. If you serve WordPress through NGINX or Angie, this split is already a good idea for both performance and security. The same architecture that gives you a great cache hit rate also makes it much easier to disable compression precisely where secrets live.

The broader setup work is covered in our WordPress on NGINX and PHP-FPM guide and in our reverse proxy configuration article.

What Not to Do

  • Do not assume HTTPS by itself solves the BREACH attack. It does not.
  • Do not keep secrets and attacker reflection in the same compressed response just because “the page works fine.”
  • Do not rely on random padding as your main mitigation. It is a friction control at best.
  • Do not nuke compression across your whole site unless you genuinely have no better option. Be selective and deliberate.
  • Do not treat CSRF tokens as magical objects. They are only as strong as the design around them.

A Practical BREACH Defense Checklist

  1. Inventory pages that contain secrets and user-controlled reflection.
  2. Disable gzip and Brotli on those sensitive responses.
  3. Mask or rotate CSRF tokens so they are not stable reusable targets.
  4. Enable SameSite, Secure, and HttpOnly on cookies.
  5. Validate Origin and, where appropriate, Referer on state-changing requests.
  6. Rate-limit repeated probing patterns and watch for anomalies.
  7. Keep public compressed content separated from authenticated secret-bearing content.
  8. Audit templates for needless reflection of attacker-controlled input.

That list is dramatically less sexy than saying “we added padding.” It is also dramatically more likely to actually protect you. Mood.

Why the BREACH Attack Still Matters in 2026

Because performance tuning and security tuning keep slamming into each other in the same hallway, and neither is willing to apologise.

Everyone wants smaller responses, faster pages, lower bandwidth bills, and greener hosting. That is why compression is everywhere. Modern stacks also keep pushing more state, more personalization, and more tokens into dynamic HTML. That is why compression side channels keep finding fresh rooms to lurk in.

BREACH is not the only compression-based leak on the planet, but it remains the perfect reminder that a feature can be correct, useful, and dangerous all at the same time — depending on what you wrap around it. Compression itself is not the villain. Mixing secrets and attacker-controlled input into one compressed response is the villain. Blame the packing choices, not the vacuum bag.

Frequently Asked Questions

What is the BREACH attack in one sentence?

The BREACH attack is a compression side-channel attack that uses tiny differences in compressed HTTPS response sizes to help an attacker guess secrets such as CSRF tokens or password reset tokens, one character at a time.

Does BREACH mean I should disable gzip or Brotli everywhere?

No, and please don’t. The practical fix is to disable compression selectively on sensitive dynamic pages that contain secrets and attacker-controlled reflection. Public assets and ordinary static content can stay compressed.

Why are CSRF tokens involved so often?

Because CSRF tokens are usually baked directly into HTML forms and stay stable long enough to be guessed in pieces. If they show up in a compressed response alongside attacker-controlled input, they become a perfect BREACH attack target.

Is random padding a good BREACH mitigation?

Only as a minor supporting friction control. Padding adds noise, but attackers can usually average that noise out with enough samples. Do not let it be your main defense.

Can SameSite cookies help prevent BREACH?

They reduce the browser’s willingness to send authenticated cookies on cross-site requests, which makes BREACH-style probing harder. Useful and very welcome, but not a full replacement for careful compression and template design.

Is BREACH only a WordPress problem?

Not at all. Any web application can be vulnerable if it compresses responses that contain secrets and attacker-controlled reflection. WordPress, custom PHP apps, Python frameworks, Node.js apps, Java stacks — all can fall into the same trap.

How long does a BREACH attack actually take?

It depends on how stable the secret is, how cleanly attacker input is reflected, and how aggressively the attacker can issue requests. In friendly conditions a token can be recovered in minutes to hours; in less friendly conditions it may not be practical at all. Either way, “not always fast” is a poor reason to leave the door open.

Related Posts