Skip to main content

NGINX / Security

NGINX Length Hiding Module: Does It Actually Prevent BREACH Attacks?

by , , revisited on


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

The NGINX length hiding module is often recommended as a mitigation for the BREACH compression attack. However, security research has demonstrated that random padding does NOT prevent BREACH—it only slows down the attack slightly. This article explains how the module works, why it’s insufficient, and what actually protects against BREACH in 2026.

Important Disclosure: Random length padding has been proven ineffective at preventing BREACH attacks. The module at most slows down attackers, who can bypass the randomization through statistical analysis. Modern browser protections (SameSite cookies) and application-level mitigations (CSRF token rotation) provide far better protection.

The BREACH (Browser Reconnaissance and Exfiltration via Adaptive Compression of Hypertext) attack was discovered in 2013. It is tracked as CVE-2013-3587. This attack allows attackers to extract sensitive data like CSRF tokens, session identifiers, and API keys from compressed HTTPS responses by observing subtle changes in response sizes to guess secrets one character at a time.

Understanding the BREACH Attack

BREACH exploits a fundamental property of compression algorithms like gzip and deflate. When data contains repeated sequences, the compressed output becomes smaller. An attacker who can inject content into a compressed response containing secrets can systematically guess those secrets by observing size changes.

For example, if an HTML page contains a CSRF token csrf_token=abc123, an attacker might inject csrf_token=a through a form field or URL parameter. If this injection causes the compressed response to become smaller, the attacker knows the token starts with “a”. Repeating this process reveals the entire secret.

Conditions for BREACH Vulnerability

Your application is vulnerable to BREACH when all three conditions are present simultaneously:

  1. HTTP compression is enabled (gzip or deflate in Accept-Encoding)
  2. User input is reflected in responses (search terms, form values, URL parameters)
  3. Secrets appear in the response body (CSRF tokens, API keys, session data)

Why Length Hiding Does NOT Prevent BREACH

The theory behind length hiding is that adding random-length padding makes it impossible for attackers to correlate size changes with their guesses. However, this has been proven wrong.

From the original BREACH paper:

“While this measure does make the attack take longer, it does so only slightly… By repeating requests and averaging the sizes of the corresponding responses, the attacker can quickly learn the true length of the cipher text.”

The nginx issue tracker confirms this:

“It was demonstrated more than once that padding cannot prevent the BREACH attack. The random padding at most slows down the attack.”

The Statistical Bypass

The bypass works because of basic statistics. Random padding adds noise, but noise can be filtered out by making more requests and averaging the results. The standard error of the mean is inversely proportional to √N—attackers simply make more requests to filter out the random padding noise.

If an attacker needs 100 requests to guess a character without padding, they might need 1,000-10,000 requests with padding. This is slower but still completely feasible for an automated attack.

What Actually Protects Against BREACH

According to Qualys and security researchers, these mitigations actually work:

1. SameSite Cookies (Most Important)

BREACH requires a cross-site request forgery (CSRF) vector—the attacker must trick the victim’s browser into making requests to the target site with cookies attached. SameSite cookies break this attack vector entirely.

  • Chrome, Edge, and Opera now default to SameSite=Lax
  • This prevents cross-site POST requests from including cookies
  • Most users on these browsers are already protected

However: Firefox and Safari have not adopted this default. Explicitly set SameSite on your cookies:

# In your application or via NGINX headers
add_header Set-Cookie "session=value; SameSite=Lax; Secure; HttpOnly";

2. CSRF Token Randomization Per Request

Modern frameworks like Django and Rails now randomize CSRF tokens on every request. Since the target secret changes constantly, BREACH cannot extract it character by character.

If your application uses static CSRF tokens, update your framework or implement per-request token rotation.

3. Disable Compression for Sensitive Responses

The most bulletproof solution is disabling compression for responses containing both user input and secrets:

location /api/sensitive {
    gzip off;
    proxy_pass http://backend;
}

This has a bandwidth cost but eliminates the vulnerability entirely.

4. Separate Secrets from User Input

Architect your application so secrets never appear in the same response as user-controlled content:

  • Return CSRF tokens via separate API calls
  • Use HttpOnly cookies for session identifiers (cookies are not in the response body)
  • Deliver sensitive data through JavaScript-only endpoints that don’t reflect user input

The Length Hiding Module: When It Might Still Be Useful

Despite its limitations, the length hiding module is not entirely useless. It provides defense in depth as one layer among many. If you’ve already implemented SameSite cookies and CSRF token rotation, length hiding adds marginal additional protection by increasing the attacker’s workload.

How the Module Works

The module appends a randomly generated HTML comment to response bodies:

<!-- random-length HTML comment: JnSLGWeWYWsoJ4dXS3ubLw3YOu3zfGTotlzx7UJUo26xuXICQ2cbpVy1Dprgv8Icj6QfOZx2Ptp9HxCVoevTxhKzMzV6xeYXao0oCngRWJRb4Tvive1iBAXLzrHlLg6jKwNKXrct0tJuA2TvWIRVIng6UoffIbCQLPbi63PwmWemOxVi6m3CPa6hCbAK2CaBR1jLux7UJa4WNN4H0yIDMElMglWWouY5m5FUqAn0afMmtErj0zkA2LMWxisZRES38XLoYycySmaBrIih5IixUsJFR0ei4uZ0IifgV5SnitoNzMusSQem9npObHuU2HKApneAjwnFdPSQZA9sRdSOE8agDI05P832mV1JIcOjsg0FgzxvSG7UEX0HdqBqp2jPOYYW0k5gGtmkiXWydRJfn9lGomxReUeqq2Aec69gplEM6a8aqH5TFgXrGK8jcaPISQlsKkMxJQ7Fp6fVDbmI59xCIvlk -->

The random string varies from 1 to 2048 bytes (configurable). This forces attackers to make more requests to average out the noise, but does not prevent the attack.

Installation

If you still want to use the module as part of a defense-in-depth strategy, install it from the GetPageSpeed repository on RHEL-based systems:

sudo dnf install nginx-module-length-hiding

This works on Rocky Linux, AlmaLinux, CentOS, Fedora, and other RHEL-compatible distributions.

Load the module by adding this line at the top of your /etc/nginx/nginx.conf, before the events block:

load_module modules/ngx_http_length_hiding_filter_module.so;

Verify the module loads correctly:

nginx -t

Configuration Directives

length_hiding

Enables or disables the feature for responses.

Syntax: length_hiding on | off;

Default: off

Context: http, server, location, if in location

length_hiding_max

Sets the maximum length of the random string inside the HTML comment.

Syntax: length_hiding_max size;

Default: 2048

Context: http, server, location

Valid range: 256 to 2048 bytes

length_hiding_types

Specifies which MIME types receive the treatment.

Syntax: length_hiding_types mime-type ...;

Default: text/html

Context: http, server, location

Note: The module appends HTML comments. Applying it to non-HTML formats like JSON may produce technically invalid output.

Configuration Example

server {
    listen 443 ssl;
    server_name example.com;

    # Enable compression - see our gzip guide for details
    gzip on;
    gzip_types text/html text/css application/javascript;

    # Length hiding as defense-in-depth (not primary protection)
    length_hiding on;

    location / {
        root /var/www/html;
    }

    # Disable compression entirely for highly sensitive endpoints
    location /api/tokens {
        gzip off;
        length_hiding off;
        proxy_pass http://backend;
    }
}

For optimal compression settings, see our guide on NGINX gzip compression.

Verifying the Module Works

Make multiple requests and observe varying response sizes:

for i in {1..5}; do
    curl -s -o /dev/null -w "Request $i: %{size_download} bytes\n" \
        -H "Accept-Encoding: gzip" \
        https://example.com/protected-page
done

With length hiding enabled, response sizes vary:

Request 1: 597 bytes
Request 2: 701 bytes
Request 3: 489 bytes
Request 4: 823 bytes
Request 5: 556 bytes

Remember: this variation slows attackers but does not stop them.

Here’s the priority order for protecting against BREACH:

Priority Mitigation Effectiveness
1 Set SameSite=Lax or SameSite=Strict on cookies Breaks the CSRF attack vector entirely
2 Rotate CSRF tokens per request Target changes constantly, cannot be guessed
3 Separate secrets from user input No secrets to extract
4 Disable compression for sensitive responses Eliminates the vulnerability
5 Length hiding (this module) Slows down attacks only

Do not rely on length hiding as your primary or only BREACH protection.

Troubleshooting

Module Not Loading

If nginx -t fails after adding the load_module directive, verify:

  1. The module file exists at /usr/lib64/nginx/modules/ngx_http_length_hiding_filter_module.so
  2. The module version matches your NGINX version
  3. The load_module directive appears before the events block

Length Hiding Not Working

If responses do not include the random comment:

  1. Verify length_hiding on; is set in the correct context
  2. Check that the response MIME type matches length_hiding_types
  3. Ensure the response is not a HEAD request or 204 No Content
  4. Verify the request is not a subrequest

Alternative Compression Options

While this module works with gzip, you might also consider modern compression algorithms:

Conclusion

The NGINX length hiding module adds random-length HTML comments to responses, which increases the number of requests an attacker needs to execute a BREACH attack. However, it does not prevent the attack—statistical analysis allows attackers to filter out the random noise.

For real protection against BREACH:

  1. Set SameSite cookies to break the CSRF vector
  2. Rotate CSRF tokens per request so the target constantly changes
  3. Disable compression for responses containing both secrets and user input

Use length hiding only as an additional layer in a defense-in-depth strategy, never as your primary protection.

The module is maintained by Nulab and available under the MIT license. View the source code on GitHub.

Further Reading

D

Danila Vershinin

Founder & Lead Engineer

NGINX configuration and optimizationLinux system administrationWeb performance engineering

10+ years NGINX experience • Maintainer of GetPageSpeed RPM repository • Contributor to open-source NGINX modules

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.