Skip to content

Why gzip compression is not disabled for strong etag #13047

Open

Description

@sroui

Jetty version(s)
12.0.12

Jetty Environment
ee 10

Java version/vendor
Amazon correto 21

OS type/version
WSL

Description
I believe GzipHandler handles the gzip compression, but I do not know why it does not disable compression for strong etags.

Similar issue for cowboy webserver: ninenines/cowboy#1546

How to reproduce?

Enable compression on jetty and fire a request with a strong ETag, and the compression is applied to the response

Activity

  1. added
    BugFor general bugs on Jetty side
    on Apr 25, 2025
  2. joakime commented on Apr 25, 2025

    @joakime
    Contributor

    Compression adds the suffix of the compression on the ETag response for endpoints that generate the content and care about etags, maintaining the original non-weak etag, but with compression indicated (to satisfy http caching requirement)

    Example:

    If the original request is ...

    GET /context/data HTTP/1.1
    Host: localhost:8080
    Connection: close
    
    

    And that request passes through GzipHandler reaching a webapp DataServlet.
    That DataServlet produces a response, and also adds the ETag: 2341olla response header.
    Since that request didn't have an Accept-Encoding, there's no compression applied, and the response will be roughly ...

    HTTP/1.1 200 OK
    Content-Type: application/octet-stream
    Content-Length: 5123
    ETag: 2341olla
    
    <lots of response data here>
    

    Now, if that same client makes a request, with that Etag ...

    GET /context/data HTTP/1.1
    Host: localhost:8080
    If-None-Match: 2341olla
    Connection: close
    
    

    Then the response will again, go through GzipHandler, reach the webapp DataServlet.
    That DataServlet checks the If-None-Match etag and decides, that it won't produce a response, but instead responds with a 304.
    It goes back up through the response chain, past GzipHandler on the way out and the response seen by the client is ...

    HTTP/1.1 304 Not Modified
    Content-Type: application/octet-stream
    ETag: 2341olla
    
    

    However, if the client suddenly decides that it can support gzip, like this ...

    GET /context/data HTTP/1.1
    Host: localhost:8080
    If-None-Match: 2341olla
    Accept-Encoding: gzip
    Connection: close
    
    

    That will pass through the GzipHandler, it will see the support for Gzip in the request headers and prepare for Gzip response.
    The request reaches the webapp DataServlet.
    That DataServlet checks the If-None-Match etag and decides, that it won't produce a response, but instead responds with a 304.
    The GzipHandler will see the 304 and not compress the response, and not mess with the ETag.
    Producing a response like ...

    HTTP/1.1 304 Not Modified
    Content-Type: application/octet-stream
    ETag: 2341olla
    
    

    Nothing surprising yet, right?

    Now, lets look at a new client, that has never asked for the resource, but supports ETag too.

    Initial request ...

    GET /context/data HTTP/1.1
    Host: localhost:8080
    Connection: close
    
    

    And that request passes through GzipHandler reaching a webapp DataServlet.
    That DataServlet produces a response, and also adds the ETag: 2341olla response header.
    Since that request did have an Accept-Encoding, there is compression applied, and the ETag will have a --gzip suffix applied by the GzipHandler.

    HTTP/1.1 200 OK
    Content-Type: application/octet-stream
    Content-Length: 413
    Content-Encoding: gzip
    ETag: 2341olla--gzip
    
    <lots of compressed response data here>
    

    The next request, by this new client will include the ETag in its full form, not being aware of the GzipHandler behavior.

    GET /context/data HTTP/1.1
    Host: localhost:8080
    Accept-Encoding: gzip
    If-None-Match: 2341olla--gzip
    Connection: close
    
    

    Then the response will again, go through GzipHandler, and it strips the --gzip suffix on the request If-None-Match header, sending the new modified request to the webapp DataServlet.
    That DataServlet checks the If-None-Match etag (which it sees without the --gzip suffix) and decides, that it won't produce a response, but instead responds with a 304.
    It goes back up through the response chain, past GzipHandler on the way out and the response seen by the client is ...

    HTTP/1.1 304 Not Modified
    Content-Type: application/octet-stream
    ETag: 2341olla
    
    
  3. sroui commented on Apr 29, 2025

    @sroui
    Author

    @joakime, thank you for your reply. You are saying that the calculation/comparison of the ETag is done on the decompressed response, and I agree that compression will work properly in that case.

    To give you a little bit of context, I recently migrated from Tomcat to Jetty, and compression is disabled by default for strong tags for Tomcat.

    When I deployed the application after the migration, the production (iOS app that requests gzip compressed response) went down because we are heavily using ETags for caching, and Tomcat did not compress the response and did not send headers like 'Vary'.

    I have overcome the issue, and I am just saying if disabling compression for strong etags is part of a standard, then Jetty should do the same as Tomcat, and other developers will not have the same issue when doing tomcat-jetty migration.

  4. github-actions commented on Apr 30, 2026

    @github-actions

    This issue has been automatically marked as stale because it has been a
    full year without activity. It will be closed if no further activity occurs.
    Thank you for your contributions.

  5. added
    SpecificationFor all industry Specifications (IETF / Servlet / etc)
    and removed
    StaleFor auto-closed stale issues and pull requests
    on May 2, 2026
  6. joakime commented on May 2, 2026

    @joakime
    Contributor

    We should add a configurable to 12.1.x CompressionHandler to support this behavior / use-case.

  7. self-assigned this
    on May 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

BugFor general bugs on Jetty sideSpecificationFor all industry Specifications (IETF / Servlet / etc)

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions