Short answer: Loki gzips query_range when frontend.compress_responses is true and the client sends Accept-Encoding: gzip. On 3.4.2 the help text for -querier.compress-http-responses says that default is true. It is not the value the process runs with. RegisterFlags then registers -frontend.support-parquet-encoding on that same bool, with default false, so gzip stays off until the YAML key is set. That second registration is a bug. It is still there in 3.6.17 and 3.7.8. Main corrects it in #23885, which those releases do not contain.
What query_range returns
GET /loki/api/v1/query_range returns JSON. With compress_responses true and Accept-Encoding: gzip, the body is gzip and the response carries Content-Encoding: gzip and Vary: Accept-Encoding. A short body stays plain JSON and has no Content-Encoding.
In pkg/lokifrontend/config.go the first BoolVar points -querier.compress-http-responses at compress_responses and sets it true. The next BoolVar points -frontend.support-parquet-encoding at that same field and sets it false. Go writes a flag’s default into the variable when the flag is registered, so the second call wins. -help still prints the first flag’s own default, true. The support_parquet_encoding field is never attached to its flag.
A config file that omits the key leaves the field false. On 3.4.2 the expanded config showed compress_responses: false, and the same query came back as plain JSON. Setting the key and restarting produced gzip. Read the expanded config after the reload.
One 500-line query_range was 103,278 bytes as identity and 10,655 bytes as gzip.
The push is a different body. Promtail’s default POST to /loki/api/v1/push is snappy-compressed protobuf. This flag does not change that POST.
What the client does with the body
A Go net/http client that does not set Accept-Encoding itself asks for gzip and decompresses before the application reads the body. Grafana’s datasource is in that class, so turning the key on does not break it.
Python’s urllib does not. If the request sets Accept-Encoding: gzip, urlopen returns the compressed bytes. json.load on that body fails. Decompress only when Content-Encoding is gzip, then parse. A response with no such header is already JSON.
The working path
- On 3.4.2 through 3.7.8, set
frontend.compress_responses: true. - Reload the process and read the expanded config. The field has to say true.
- Call
query_rangewithAccept-Encoding: gzip. - If
Content-Encodingis gzip, decompress, then parse JSON. If the header is absent, parse the bytes as JSON.
#23885 binds -frontend.support-parquet-encoding to support_parquet_encoding. A binary that contains that change keeps the documented default, and the YAML key is optional.
Approaches that look simpler and fail
| Approach | Why it fails |
|---|---|
Trust -help once the flags have been registered |
The parquet flag is registered on compress_responses and writes false |
json.load the body after Python urllib sends Accept-Encoding: gzip |
urllib does not decompress. The first bytes are the gzip header |
| Wait for a 3.6 or 3.7 release | 3.6.17 and 3.7.8 still register the parquet flag on compress_responses |
-help prints each flag’s own default. The expanded config is the value the process uses.
When you can omit the key
Omit frontend.compress_responses on a build that contains #23885. That is a source change, not a switch inside 3.4.2. Until the binary has that registration, set the key.
Why this stays published
We hit this on Loki 3.4.2: query_range answered with plain JSON while the flag help said gzip was on. The parquet flag is registered on compress_responses and clears it. Set frontend.compress_responses: true until the binary contains the #23885 fix.
本文由 HoHo 與 AI 協作整理,最後更新於 2026 年 10 月 4 日。
