Content-Encoding: gzip

When making a request for server-rendered HTML or CSS in a Go app, I noticed that the response when served from *.fly.dev has Content-Encoding: gzip

Does fly have a layer that handles compression? Does it only do gzip, or can it handle brotli?

I am trying to understand what features I need to build into the app itself. I can’t find a clear answer in the documentation though.

These are not files from [[statics]], these are server-rendered files.

We handle compression automatically, unless explicitly disabled via a, currently, undocumented option.

We used to support brotli, but the rust library we used kept panicking. We left it out until this is resolved upstream.

Short answer: if you want to use brotli, you’ll need to implement it yourself. If you want gzip or deflate, that’s already handled by our proxy.

1 Like

@jerome thank you, that is amazing.

Is this documented anywhere? Are there other benefits of the proxy that I can leave out?

For example, because you are handling this, I don’t need to set a Vary header. But I see you’re not setting it either.

It’s not documented at this time :slight_smile:.

We try to keep requests and responses as-is as much as possible. Compression seemed like something we should do to prevent thousands of apps implementing their own.

Hmm, you might still want to set the Vary header here. We’re not caching anything, but if something upstream does, then it should vary by content-encoding.

FYI, if you’re considering promoting this to a supported feature, an example of how others do it: The folks at Cloudflare key off of Cache-Control: no-transform to disable features like gzip, image minification, etc (their docs).

(We found this out the hard way when their frontends were stripping our etags, a consequence of one of those rewriting features…)

2 Likes

Good call! I’ve added this to our internal issue tracker.

3 Likes

Hi @jerome, is brotli supported now?
If not, is there some documentation regarding how to implement it on our own?

@jerome The auto-gzipping prevents using HTML streaming on Fly.io. This is a major bug. Please let me know how to turn it off for my HTML routes.

gzip should work fine with html streaming!

You should be able to disable our gzip by setting content-encoding: none on responses, though.

I think you misunderstand. In order for you to gzip a response you have to wait for that response to complete from the origin. That means that the user has to wait for the full length of the response. I have tested this on Fly.io.

I’ll try your header suggestion, thanks.

content-encoding: none does fix the problem, thanks.

I’d be happy to share a minimal reproduction so you can see the problem this causes. On all text/html responses you should not buffer the response in order to gzip it. For statics that’s fine, but not responses coming from the origin.

Ok we’ll double check that. If it’s not gzipping a body stream, that’s a bug for sure.

For what it’s worth, you can also gzip in your app. When the content-encoding header is set, or proxy passes it through as is.

Enabling gzip by default has security implications (the BREACH attack). Maybe compression should be an opt-in feature, at least for non-static content?

Sorry, I think there’s some confusion here so let me just back up a second @kurt @jerome

HTML streaming is where you can respond to an HTTP request with chunks of html like:

<li>1</li>

Wait 5 seconds

<li>2</li>

Wait 5 seconds

etc. And the user will see 1 right away.

On Fly.io if you attempt to use this feature of the web it does not work. Instead the user sees nothing for, in this example, 10 seconds until the response is fully complete.

That is the bug. For me it is fixed by setting content-encoding: none. From @kurt’s reply it sounds like you think that gzipping is not happening, but it is happening, and the fact that it’s happening is the problem.

Ok, hope that clarifies the issue so that we’re all on the same page about what the problem is. Happy to discuss any solution (or no solution) from here.

I never replied, but this is what I understood :slight_smile:

We’re going to try to reproduce on our end and come up with a solution.

What’s the expected behaviour in your opinion? Is it possible to do gzip compression with streaming chunks in general (I haven’t yet done the research)?

1 Like

I admit that I’m missing some knowledge here as well. I thought it wasn’t possible to get streaming with gzip, but it seems that Deno Deploy is able to do so: https://twitter.com/lcasdev/status/1571872773684740103

So there’s possibly something else missing here in Fly.io’s implementation. Could it be that you’re omitting the Vary: Accept-Encoding header?

I don’t think Vary is the problem here. That’s used for caching and we don’t cache anything.

I have a tentative fix in progress. Do you have a live app I can reproduce the issue with? Or a some code I can use in an app of my own to test the fix?

@jerome This app sees the problem: https://frosty-frog-2648.fly.dev/

If you want some code I can create a more reduced example.

Thanks, that’s good enough!

I can see a slow (too slow) drip of HTML streaming with this:

curl -N https://frosty-frog-2648.fly.dev/ --compressed