# curl --compressed: qué clientes HTTP no piden compresión

Source: https://ipvolt.com/es/guides/curl-compressed-accept-encoding
Markdown: https://ipvolt.com/es/guides/curl-compressed-accept-encoding.md
Language: es

[Inicio de ipvolt](https://ipvolt.com/es.md) / [Guías](https://ipvolt.com/es/guides.md) / curl --compressed: qué clientes HTTP no piden compresión

Integración
Revisado: 2026-09-27
Publicado: 2026-09-27
20 min de lectura
Por ipvolt

curl, Wget 1.x, urllib, node:http, PHP curl, Guzzle y java.net.http.HttpClient no envían Accept-Encoding (o envían identity) por defecto. Soluciones y bytes comprobados.

curl no envía la cabecera `Accept-Encoding` a menos que le pases `--compressed`. GNU Wget 1.x y `urllib.request` de Python envían `Accept-Encoding: identity`, y `node:http` de Node, ext-curl de PHP, Guzzle y `java.net.http.HttpClient` de Java no envían ninguna cabecera. Un servidor que solo comprime cuando se le pide devuelve entonces el texto completo sin comprimir, y cada uno de esos bytes pasa por tu proxy. Requests, httpx, aiohttp, Scrapy, el `fetch` integrado de Node, axios, got, Go y los navegadores ya piden compresión: con ellos no hay nada que activar, y como mucho un decodificador opcional de br recorta algo más en algunas páginas.

Cada valor por defecto de abajo lo registró un laboratorio local que ipvolt ejecutó el 27 de septiembre de 2026, con las versiones que se indican bajo cada tabla. Los resultados son sintéticos: un servidor y un proxy en 127.0.0.1 sirvieron una página de prueba local, así que ningún recuento de bytes de esta guía procede de un sitio público ni de la factura de un proveedor.

## Quién pide compresión por defecto

### No envían Accept-Encoding, o envían identity

Un servidor que solo comprime cuando se le pide envía a estos clientes el texto sin comprimir. Sin cabecera: curl, node:http, node:https, PHP ext-curl, Guzzle, Laravel Http, Java HttpClient. `identity`: Wget, urllib, http.client.

| Cliente | Solución |
|---|---|
| curl | Añade `--compressed` |
| Wget | Añade `--compression=auto` |
| urllib, http.client | Usa Requests o httpx |
| node:http, node:https | Usa `fetch`, got o axios |
| PHP ext-curl | Establece `CURLOPT_ACCEPT_ENCODING` en `''` |
| Guzzle, Laravel Http | Establece `decode_content` en `'gzip'` |
| Java HttpClient | Envía `Accept-Encoding: gzip` y descomprime con `GZIPInputStream` |

Versiones ejecutadas: curl 8.7.1, 8.22.0; Wget 1.25.0; urllib.request, http.client (Python 3.13.15, 3.14.7); node:http, node:https (Node.js 22.23.3, 24.21.0, 26.10.0); PHP 8.5.8 ext-curl (libcurl 8.20.0); Guzzle 8.2.0, Laravel Http 13.33.0; Java 25.0.4.1 HttpClient.

- curl: con `-H 'Accept-Encoding: gzip'` puesto a mano, la cabecera se envía, pero el cuerpo sigue comprimido.
- Wget: con `--compression=auto` envió `gzip` y decodificó la respuesta.
- urllib.request y http.client devuelven los cuerpos comprimidos tal como llegaron.
- node:http y node:https devuelven los cuerpos comprimidos tal como llegaron.
- PHP ext-curl: con `CURLOPT_ACCEPT_ENCODING` en `''`, este libcurl envió `deflate, gzip, br, zstd`. Una cabecera puesta a mano se envía, pero no se decodifica.
- Laravel Http: `withOptions(['decode_content' => 'gzip'])`.
- Java HttpClient nunca decodifica: envuelve el cuerpo en `GZIPInputStream` cuando la respuesta viene en gzip.

### Ya piden compresión

No hay nada que cambiar: estos clientes piden compresión y decodifican la respuesta.

| Cliente | Cabecera enviada |
|---|---|
| Requests | Python 3.13.15: `gzip, deflate`; Python 3.14.7: `gzip, deflate, zstd` |
| httpx | `gzip, deflate` |
| aiohttp | Python 3.13.15: `gzip, deflate`; Python 3.14.7: `gzip, deflate, zstd` |
| Scrapy | `gzip, deflate, br, zstd` |
| fetch integrado de Node.js | Node.js 22.23.3/24.21.0: `br, gzip, deflate`; Node.js 26.10.0: `br, gzip, deflate, zstd` |
| Go net/http | `gzip` |

Versiones ejecutadas: Requests 2.34.2; httpx 0.28.1; aiohttp 3.14.3; Scrapy 2.19.0; fetch integrado (Node.js 22.23.3, 24.21.0, 26.10.0); Go 1.27.1 net/http.

- Requests: `brotli` añade br; en Python 3.13, `backports.zstd` añade zstd.
- httpx: `brotli` añade br y `zstandard` añade zstd.
- aiohttp: `Brotli` añade br; en Python 3.13, `backports.zstd` añade zstd.
- fetch integrado con URL `http://`: `gzip, deflate`.
- Go: si pones la cabecera tú mismo, la decodificación se desactiva.

Si tu cliente ya pide compresión, confírmalo con la comprobación de bytes de más abajo, y lee la sección sobre cabeceras copiadas del navegador antes de añadir ninguna cabecera a mano. La compresión no aporta nada con imágenes, vídeo y otros contenidos que ya vienen comprimidos, uses el cliente que uses.

Cada fila es una ejecución del laboratorio, no una lectura del código fuente: 567 celdas registradas, ninguna marcada como leída en el código fuente. [tables.md](https://ipvolt.com/downloads/accept-encoding-defaults/tables.md) enumera los registros que respaldan cada fila. Aquí, «Wget» significa GNU Wget 1.x. GNU Wget2 pide por defecto todas las codificaciones con las que se compiló ([wget.c](https://gitlab.com/gnuwget/wget2/-/blob/v2.3.0/src/wget.c#L4306-4331)), y Fedora 40 y posteriores instalan wget2 como el comando `wget` ([cambio de Fedora](https://fedoraproject.org/wiki/Changes/Wget2asWget)); esto se ha leído en el código fuente, y el laboratorio no ejecutó Wget2.

## curl: sin cabecera hasta que pasas --compressed

Un `curl URL` a secas no envía ningún `Accept-Encoding`. En libcurl, [`CURLOPT_ACCEPT_ENCODING`](https://curl.se/libcurl/c/CURLOPT_ACCEPT_ENCODING.html) vale NULL por defecto, lo que «hace que libcurl no envíe una cabecera `Accept-Encoding:` y no descomprima automáticamente el contenido recibido», y la herramienta curl solo lo cambia con `--compressed`. Ninguna de las dos compilaciones de curl del laboratorio envió la cabecera, ni en directo, ni a través de un proxy `http://`, ni por CONNECT. [curl#11091](https://github.com/curl/curl/issues/11091) muestra lo mismo con una captura de `nc` de curl 7.88.1.

`--compressed` pide todas las codificaciones que tu compilación sabe decodificar y después decodifica la respuesta:

- El curl 8.7.1 del sistema en macOS, compilado solo con zlib, envió `deflate, gzip`.
- El curl 8.22.0 de Homebrew, compilado con brotli y zstd, envió `deflate, gzip, br, zstd`.

Para saber qué pedirá el tuyo, lee la línea `Features:` de `curl -V`: `libz` significa gzip y deflate, `brotli` añade br y `zstd` añade zstd.

Tres detalles deciden si sirve de algo:

- Es «una petición, no una orden; el servidor puede entregar los datos comprimidos o no» ([página del manual](https://curl.se/docs/manpage.html#--compressed)). curl no informa de ningún error cuando un servidor la ignora y no tiene ninguna opción para fallar en ese caso ([curl#7516](https://github.com/curl/curl/issues/7516)), así que comprueba los bytes como se muestra más abajo.
- Un `-H 'Accept-Encoding: gzip'` puesto a mano se envía, pero curl no decodifica la respuesta. En el laboratorio, curl imprimió el flujo gzip de 17.750 bytes en lugar de la página de 100.129 bytes. Usa `--compressed`, no una cabecera.
- `--no-compressed` lo vuelve a desactivar, por ejemplo después de una línea `--compressed` en `.curlrc`.

Cuando el servidor del laboratorio envió br o zstd, que la compilación de macOS no sabe decodificar, `curl --compressed` se detuvo con el código de salida 61:

```text
curl: (61) Unrecognized content encoding type. libcurl understands deflate, gzip content encodings.
```

La descompresión tiene su propio coste. La página del manual advierte de que «incluso transferencias diminutas podrían expandirse y generar una cantidad enorme de bytes» y sugiere `--max-filesize`. Esa opción detiene una transferencia que crece al descomprimirse con `--compressed` solo «desde 8.20.0» ([página del manual](https://curl.se/docs/manpage.html#--max-filesize)), así que no protege al curl 8.7.1 del sistema en macOS. Ese límite está documentado; el laboratorio no lo probó.

Sin `--compressed`, un servidor que comprime de todos modos te deja con bytes gzip en bruto, como en [curl#2836](https://github.com/curl/curl/issues/2836); es el conocido caso de la «salida binaria», y `--compressed` lo resuelve. Para las opciones de proxy en sí, consulta [usar un proxy con curl](/es/guides/curl-proxy-setup).

## Python: urllib envía identity; Requests, httpx, aiohttp y Scrapy comprimen

- **urllib.request y http.client** envían `Accept-Encoding: identity` en CPython 3.13.15 y 3.14.7. http.client la añade salvo que pongas la tuya, con el comentario «no admitimos codificaciones como x-gzip o x-deflate» ([client.py](https://github.com/python/cpython/blob/v3.14.7/Lib/http/client.py#L1296-L1299)), y ninguno de los dos módulos decodifica la respuesta. En el laboratorio, urllib.request devolvió las respuestas gzip, br y zstd tal como llegaron; http.client, a través del cual urllib.request envía sus peticiones, solo se ejecutó con su cabecera por defecto, así que para http.client esto se basa en el código fuente. Poner la cabecera tú mismo solo te da bytes comprimidos que tendrás que decodificar a mano, así que mejor cambia de cliente. Un script que deba quedarse en la biblioteca estándar puede enviar `Accept-Encoding: gzip` y, cuando la respuesta traiga `Content-Encoding: gzip`, decodificar el cuerpo con [`gzip.decompress()`](https://docs.python.org/3.14/library/gzip.html#gzip.decompress); esa vía no se ejecutó en el laboratorio.
- **Requests 2.34.2** (con urllib3 2.8.0) toma su valor por defecto de urllib3 ([utils.py](https://github.com/psf/requests/blob/v2.34.2/src/requests/utils.py#L91-L93)): `gzip, deflate` en Python 3.13.15 y `gzip, deflate, zstd` en 3.14.7, donde urllib3 usa [`compression.zstd`](https://docs.python.org/3.14/library/compression.zstd.html) de la biblioteca estándar. Instalar `brotli` añade br; en 3.13, `backports.zstd` añade zstd. Desde urllib3 2.6.0, el paquete aparte `zstandard` ya no activa zstd ([request.py](https://github.com/urllib3/urllib3/blob/2.8.0/src/urllib3/util/request.py#L22-L41); leído en el código fuente, no ejecutado). La parte del proxy está en [configurar Python Requests](/es/guides/python-requests-proxy).
- **urllib3 por sí solo**, mediante `urllib3.request()` o un `PoolManager`, no añade ninguna cabecera de compresión. Su ruta de petición pasa a http.client ([connection.py](https://github.com/urllib3/urllib3/blob/2.8.0/src/urllib3/connection.py#L525-L531)), lo que, según el código fuente, significa `identity`. Esto se ha leído en el código fuente; el laboratorio ejecutó Requests, no urllib3 solo.
- **aiohttp 3.14.3** envía `gzip, deflate` en 3.13.15 y `gzip, deflate, zstd` en 3.14.7; `Brotli` añade br, y en 3.13 `backports.zstd` añade zstd. Es el único cliente de Python de esta guía que lanza una excepción ante una codificación que no sabe decodificar; el mensaje exacto está más abajo.
- **Scrapy 2.19.0** envía `gzip, deflate, br, zstd` en ambas. Desde Scrapy 2.18.0, la compatibilidad con brotli y Zstandard es obligatoria, «así que `br` y `zstd` siempre se incluyen en la cabecera `Accept-Encoding` de las peticiones» ([notas de la versión](https://github.com/scrapy/scrapy/blob/2.19.0/docs/news.rst)).

### httpx y zstd en Python 3.14

httpx 0.28.1 envía `gzip, deflate` en las dos versiones de Python. Añade br con `brotli`, y zstd solo con el paquete de terceros `zstandard`; a diferencia de Requests y aiohttp, no usa el zstd de la biblioteca estándar de Python 3.14 ([_decoders.py](https://github.com/encode/httpx/blob/0.28.1/httpx/_decoders.py#L381-L393)). La [documentación de HTTPX](https://www.python-httpx.org/) instala ambos decodificadores con `pip install "httpx[brotli,zstd]"`. Nada de eso hace falta para la compresión en sí, porque gzip siempre se pide.

Esto importa cuando un servidor envía una codificación que httpx no sabe decodificar: httpx devuelve los bytes comprimidos y no lanza nada. En Python 3.14.7 sin `zstandard`, una respuesta zstd llegó a tu código como el flujo zstd de 19.298 bytes. La configuración del proxy está en la [guía de proxies async en HTTPX](/es/guides/httpx-async-proxy).

## Node.js: el fetch integrado comprime, node:http no

El `fetch` integrado (undici) elige la cabecera según el esquema de la URL y la línea de versiones ([código fuente, v26.10.0](https://github.com/nodejs/node/blob/v26.10.0/deps/undici/src/lib/web/fetch/index.js#L1562-L1576); [v24.21.0](https://github.com/nodejs/node/blob/v24.21.0/deps/undici/src/lib/web/fetch/index.js#L1514-L1528)):

- URL `https://`: `br, gzip, deflate` en Node.js 22.23.3 y 24.21.0, y `br, gzip, deflate, zstd` en 26.10.0.
- URL `http://`: `gzip, deflate` en las tres.
- Cualquier petición con cabecera `Range`: `identity` en las tres.

Las tres decodificaron gzip y br. Donde difieren es en zstd, incluido el zstd que un servidor envía sin que se lo pidan:

- El `fetch` de Node.js 22.23.3 no tiene decodificador de zstd y devolvió los bytes comprimidos sin ningún error ([código fuente](https://github.com/nodejs/node/blob/v22.23.3/deps/undici/src/lib/web/fetch/index.js#L2149-L2170)).
- El `fetch` de Node.js 24.21.0 decodificó zstd que no había pedido. Un cuerpo formado por cuatro tramas zstd concatenadas volvió solo con la primera trama: 25.033 de 100.129 bytes, sin ningún error. axios 1.20.0 y got 16.0.0 también devolvieron solo la primera trama en Node.js 22.23.3 y 24.21.0.
- Node.js 26.10.0 decodificó las cuatro tramas con `fetch`, axios y got.

`node:http` y `node:https` no envían `Accept-Encoding` en ninguna de las tres versiones y nunca decodifican; el cliente HTTP del núcleo de Node no pone la cabecera ([_http_client.js](https://github.com/nodejs/node/blob/v26.10.0/lib/_http_client.js)). La [documentación de zlib](https://nodejs.org/api/zlib.html#compressing-http-requests-and-responses) de Node muestra la vía manual: pon la cabecera tú mismo y pasa la respuesta por un descompresor. Cambiar a `fetch`, got o axios requiere menos código.

### El fetch integrado frente al paquete node-fetch

El paquete de npm `node-fetch` es un cliente aparte con sus propios valores por defecto ([node-fetch#1556](https://github.com/node-fetch/node-fetch/discussions/1556)). En Node.js 24.21.0, node-fetch 3.3.2 envió `gzip, deflate, br` y node-fetch 2.7.0 envió `gzip,deflate`, y ambos decodificaron la respuesta. axios 1.20.0 envía `gzip, compress, deflate, br` y solo añade zstd con `transitional: { advertiseZstdAcceptEncoding: true }` ([http.js](https://github.com/axios/axios/blob/v1.20.0/lib/adapters/http.js#L54-L57)). got 16.0.0 envió `gzip, deflate, br, zstd` en las tres versiones ([index.ts](https://github.com/sindresorhus/got/blob/v16.0.0/source/core/index.ts#L2248-L2258)). Ninguno necesita cambios. El dispatcher de proxy para el `fetch` integrado se explica en [usar un proxy con fetch de Node.js](/es/guides/nodejs-fetch-proxy).

## Go, Wget, PHP y Java

### Go net/http solo pide gzip

Go 1.27.1 envía `Accept-Encoding: gzip`, decodifica la respuesta y quita `Content-Encoding` y `Content-Length` de la respuesta que te entrega ([transport.go](https://github.com/golang/go/blob/go1.27.1/src/net/http/transport.go#L199-L207)). No envía la cabecera en peticiones `HEAD`, en peticiones con cabecera `Range` ni con `DisableCompression: true`. Dos consecuencias:

- Poner `Accept-Encoding` tú mismo desactiva la decodificación: «si el usuario pidió gzip explícitamente, no se descomprime automáticamente». Con `req.Header.Set("Accept-Encoding", "gzip")`, el cliente Go del laboratorio recibió el flujo gzip de 17.750 bytes.
- net/http no tiene decodificador de br ni de zstd. Un servidor que los envió sin que se los pidieran entregó bytes en bruto.

### GNU Wget 1.x envía identity

Wget 1.25.0 envía `Accept-Encoding: identity`. Sobre `--compression`, el manual dice de «none» que «es el valor por defecto» ([manual](https://www.gnu.org/software/wget/manual/wget.html#HTTP-Options)); la lista de `.wgetrc` de la misma página sigue llamando valor por defecto a `auto`, pero la ejecución del laboratorio coincide con `none`. Con `--compression=auto`, Wget envió `gzip` y decodificó gzip, y nada más: el br o el zstd enviados sin pedirlos llegaron en bruto. Wget2 se comporta de otra forma, como se indicó arriba.

### PHP: Guzzle, Laravel Http y ext-curl no envían Accept-Encoding

- **PHP ext-curl** (PHP 8.5.8 con libcurl 8.20.0) no envía nada, porque `CURLOPT_ACCEPT_ENCODING` «vale null por defecto» ([manual de PHP](https://www.php.net/manual/en/curl.constants.php)). `CURLOPT_ACCEPT_ENCODING => ''` pide todas las codificaciones que admite el libcurl enlazado, `deflate, gzip, br, zstd` en esta compilación, y decodifica. Una línea `Accept-Encoding` en `CURLOPT_HTTPHEADER` se envía, pero no se decodifica.
- **Guzzle 8.2.0** quita la cabecera a propósito. Su handler de cURL activa todos los decodificadores y después añade una línea `Accept-Encoding:` vacía para que curl no envíe ninguna, con el comentario de que esto «se interpretará como 'Accept-Encoding: *'» ([CurlFactory.php](https://github.com/guzzle/guzzle/blob/8.2.0/src/Handler/CurlFactory.php#L2609-L2622)). El mismo código está en todas las etiquetas revisadas, de la 6.5.0 a la 8.2.0 (leído en el código fuente). Si das a [`decode_content`](https://docs.guzzlephp.org/en/stable/request-options.html#decode-content) una cadena, como `'decode_content' => 'gzip'`, Guzzle la envía como cabecera ([Client.php](https://github.com/guzzle/guzzle/blob/8.2.0/src/Client.php#L1582-L1586)); `'gzip, deflate, br, zstd'` también funcionó con el handler de cURL. Por defecto, el handler de cURL sigue decodificando el gzip, br o zstd que un servidor envía sin que se lo pidan. El `StreamHandler` de Guzzle, que usa streams de PHP en lugar de ext-curl, decodificó gzip pero devolvió br y zstd en bruto.
- **Laravel Http** (illuminate/http 13.33.0) construye un cliente Guzzle normal y hereda todo esto ([PendingRequest.php](https://github.com/laravel/framework/blob/v13.33.0/src/Illuminate/Http/Client/PendingRequest.php)). Tanto `withOptions(['decode_content' => 'gzip'])` como `withHeaders(['Accept-Encoding' => 'gzip'])` enviaron `gzip` y decodificaron la respuesta.

### Java HttpClient nunca decodifica

`java.net.http.HttpClient` en Temurin 25.0.4.1 no envió `Accept-Encoding` y no decodificó una respuesta gzip que el servidor envió sin que se la pidieran. Para tener compresión, envía `Accept-Encoding: gzip` tú mismo y lee el cuerpo a través de `java.util.zip.GZIPInputStream` cuando la respuesta traiga `Content-Encoding: gzip`, como hace el [cliente Java](https://ipvolt.com/downloads/accept-encoding-defaults/harness/clients/php-java/JavaHttpClientLab.java) del laboratorio. No se ejecutó ningún otro cliente Java.

## Accept-Encoding: identity, sin cabecera y salida ilegible

El [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-encoding) distingue tres estados de la petición:

- **Sin cabecera** (curl, node:http, ext-curl, Guzzle, Java): «el agente de usuario considera aceptable cualquier codificación de contenido».
- **`identity`** (urllib, Wget 1.x): «un sinónimo de 'sin codificación'».
- **Un valor vacío**: «el agente de usuario no quiere ninguna codificación de contenido en la respuesta».

Así que el estándar permite a un servidor comprimir la respuesta a una petición sin cabecera, y «comprimir solo cuando se pide» es una práctica de los servidores, no una regla del RFC. La documentación de servidores y CDN describe esa práctica. Cloudflare elige gzip, Brotli, Zstandard o ninguna compresión «en función de» los valores de la cabecera `accept-encoding` de la petición, el plan y cualquier regla de compresión que coincida ([Cloudflare](https://developers.cloudflare.com/speed/optimization/content/compression/), página actualizada el 17 de abril de 2026). mod_deflate de Apache envía `Vary: Accept-Encoding` para que el contenido comprimido no se «envíe a un cliente que no lo entenderá» ([mod_deflate](https://httpd.apache.org/docs/2.4/mod/mod_deflate.html)). El comentario de Guzzle se apoya en la lectura del RFC; con servidores que siguen esa práctica, sin cabecera llega el cuerpo sin comprimir. El servidor del laboratorio siguió la misma práctica. No se midió con qué frecuencia los servidores reales comprimen sin que se lo pidan.

Algunos lo hacen. `gzip_static always` de nginx sirve el archivo comprimido con gzip «sin comprobar si el cliente lo admite» ([nginx](https://nginx.org/en/docs/http/ngx_http_gzip_static_module.html)), y curl#2836 documenta un host real que lo hacía. Cuando el servidor del laboratorio envió gzip sin que se lo pidieran, curl sin `--compressed`, Wget, urllib, `node:http`, PHP ext-curl, Java y Go con `DisableCompression` devolvieron todos el flujo gzip de 17.750 bytes tal como llegó; Guzzle y Laravel Http lo decodificaron. Si la respuesta de uno de esos clientes parece basura binaria, mira primero su `Content-Encoding`.

## Comprueba los bytes en tu propio destino

Si aquí no hay un fragmento que cuente los bytes de tu cliente, ejecuta la comparación de curl que sigue con la misma URL y el mismo proxy, y compara su `Content-Encoding` con el de tu cliente. Esto mide la respuesta de curl; una codificación o respuesta distinta del servidor no mide los bytes de tu cliente.

Los fragmentos de curl, Requests, httpx y `node-fetch-bytes.mjs` de abajo imprimen los bytes del cuerpo en la red: el cuerpo de la respuesta tal como cruzó la red, antes de cualquier decodificación. No incluyen las cabeceras de respuesta, los registros TLS, el intercambio CONNECT ni los reintentos, así que no es lo que factura un proveedor. En ocho pares del laboratorio ([tables.md](https://ipvolt.com/downloads/accept-encoding-defaults/tables.md) los enumera), el mismo cliente descargó la página de prueba por CONNECT con y sin compresión, y esos extras añadieron de 2.343 a 3.520 bytes por respuesta en el tramo entre el proxy y el cliente. Redujeron la relación de compresión, por ejemplo de 5,19× en el cuerpo a 4,56× en el tramo del proxy con curl 8.22.0, pero no el ahorro: en cada uno de esos pares, el tramo del proxy se redujo en la diferencia del cuerpo más 87 bytes. Para medir una ejecución completa a través de tu proxy y convertirla con tu propia tarifa, usa [Scrapescope](https://ipvolt.com/guides/scraper-bandwidth-report).

El fragmento de Go imprime el tamaño decodificado y `resp.Uncompressed`, que indica si Go decodificó la respuesta, y el `node-fetch-verify.mjs` enlazado imprime la cabecera que envió `fetch`; ninguno de los dos imprime bytes en la red. Cada fragmento se ejecutó sin cambios en el laboratorio, salvo que `https://example.com/` pasó a ser una URL de loopback, y las salidas que se muestran son de esas ejecuciones; además, el fragmento de Go se compiló con un archivo del laboratorio que confía en la CA del laboratorio, descrito en la sección de método. Pon tu proxy en `PROXY_URL`. Si necesitas confiar en una CA privada, usa el ajuste propio del cliente, como `--cacert`, `NODE_EXTRA_CA_CERTS` o `tls.Config.RootCAs` de Go; nunca desactives la verificación de certificados.

### Comprobar con curl

```sh
# Wire body bytes and the Content-Encoding the server chose (curl 7.84.0+ for %header{}).
curl -sS -o /dev/null -x "$PROXY_URL" \
  -w '%{size_download} %header{content-encoding}\n' https://example.com/
curl -sS -o /dev/null -x "$PROXY_URL" --compressed \
  -w '%{size_download} %header{content-encoding}\n' https://example.com/
```

Con el curl 8.22.0 de Homebrew contra la página de prueba del laboratorio, la primera línea imprimió `100129` y ninguna codificación, y la segunda `19298 zstd`; el curl 8.7.1 del sistema en macOS imprimió `17750 gzip` en la segunda línea. El `size_download` de curl es «el tamaño del cuerpo/los datos transferidos, sin incluir las cabeceras», y `%header{}` necesita curl 7.84.0 o posterior ([write-out](https://curl.se/docs/manpage.html#-w)). La relación de compresión de esa página es el primer número dividido por el segundo.

### Comprobar con Requests y httpx

```python
import os

import requests

proxies = {"http": os.environ["PROXY_URL"], "https": os.environ["PROXY_URL"]}
with requests.get("https://example.com/", proxies=proxies, stream=True) as r:
    wire = r.raw.read(decode_content=False)  # body bytes as sent, still encoded
    print(r.headers.get("content-encoding"), len(wire))
```

Requests imprimió `gzip 17750` en Python 3.13.15 y `zstd 19298` en 3.14.7. En httpx, `r.num_bytes_downloaded` cuenta los bytes tal como se leyeron y `len(r.content)`, el cuerpo decodificado:

```python
import os

import httpx

with httpx.Client(proxy=os.environ["PROXY_URL"]) as client:
    r = client.get("https://example.com/")
    # wire body bytes (as received) vs decoded bytes your code sees
    print(r.headers.get("content-encoding"), r.num_bytes_downloaded, len(r.content))
```

Imprimió `gzip 17750 100129` en Python 3.14.7, y `zstd 19298 100129` con `zstandard` instalado. urllib nunca decodifica, así que la longitud de lo que lees de él ya es el cuerpo en la red.

### Comprobar con Node.js

Para el `fetch` integrado, la entrada de resource timing que Node crea para la URL da el tamaño del cuerpo en la red:

```js
// Wire body bytes of a built-in fetch, from Node's resource timing entry for the URL.
// Run: NODE_USE_ENV_PROXY=1 HTTPS_PROXY="$PROXY_URL" node node-fetch-bytes.mjs
import { setImmediate } from 'node:timers/promises';

const url = new URL('https://example.com/');
const res = await fetch(url);
const body = await res.arrayBuffer(); // already decoded by fetch

// Node adds the entry after the body has been read, so give the event loop a turn.
let entry;
for (let turn = 0; !entry && turn < 100; turn++) {
  await setImmediate();
  entry = performance.getEntriesByType('resource').findLast((e) => e.name === url.href);
}
performance.clearResourceTimings(); // the buffer stops at 250 entries unless you clear it

// encodedBodySize: the body as it crossed the network, before decoding (no headers, TLS or CONNECT)
console.log('content-encoding', res.headers.get('content-encoding'),
  'wire body bytes', entry?.encodedBodySize, 'decoded bytes', body.byteLength);
```

A través del proxy CONNECT del laboratorio imprimió `content-encoding br wire body bytes 18451 decoded bytes 100129` en Node.js 22.23.3 y 24.21.0, y `content-encoding zstd wire body bytes 19298 decoded bytes 100129` en 26.10.0, en cada caso lo mismo que había enviado el servidor del laboratorio. En otras 18 celdas del laboratorio con las tres versiones (`http://` directo, `https://` directo y a través del proxy CONNECT, cada una con y sin compresión), `encodedBodySize` coincidió siempre con los bytes del cuerpo que envió el servidor; [tables.md](https://ipvolt.com/downloads/accept-encoding-defaults/tables.md) enumera las lecturas. La entrada nunca existía justo después de leer el cuerpo, solo tras una vuelta del bucle de eventos; por eso el fragmento espera. Después, el fragmento borra las entradas, porque el búfer de resource timing de Node solo guarda 250 por defecto ([observe.js](https://github.com/nodejs/node/blob/v26.10.0/lib/internal/perf/observe.js#L103)). No uses `transferSize` en su lugar: fue `encodedBodySize` más 300 bytes fijos en todas las celdas, mientras que, a través del proxy, el tramo del cliente transportó de 3.431 a 3.520 bytes más que el cuerpo.

[node-fetch-verify.mjs](https://ipvolt.com/downloads/accept-encoding-defaults/harness/snippets/node-fetch-verify.mjs) imprime la cabecera que envió `fetch` (`sent accept-encoding: br, gzip, deflate, zstd` en Node.js 26.10.0). Para `node:https`, que nunca decodifica, [node-http-verify.mjs](https://ipvolt.com/downloads/accept-encoding-defaults/harness/snippets/node-http-verify.mjs) cuenta el cuerpo a medida que llega, que es el cuerpo en la red.

### Comprobar con Go

```go
// Did Go ask for gzip and decode it for you? resp.Uncompressed says so.
// Run: go run go-verify.go   (with PROXY_URL set in the environment)
package main

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
)

func main() {
	proxy, err := url.Parse(os.Getenv("PROXY_URL"))
	if err != nil {
		panic(err)
	}
	t := http.DefaultTransport.(*http.Transport).Clone()
	t.Proxy = http.ProxyURL(proxy)
	resp, err := (&http.Client{Transport: t}).Get("https://example.com/")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	// true: Go sent "Accept-Encoding: gzip" itself, received gzip and decoded it,
	// then removed Content-Encoding and set ContentLength to -1.
	fmt.Printf("uncompressed=%v content-encoding=%q content-length=%d decoded-bytes=%d\n",
		resp.Uncompressed, resp.Header.Get("Content-Encoding"), resp.ContentLength, len(body))
}
```

Con Go 1.27.1 imprimió `uncompressed=true content-encoding="" content-length=-1 decoded-bytes=100129`. `resp.Uncompressed` solo indica que Go decodificó la respuesta ([response.go](https://github.com/golang/go/blob/go1.27.1/src/net/http/response.go#L93-L100)); la respuesta ya no lleva su tamaño en la red. Para contar los bytes en la red, pon la cabecera tú mismo, lo que deja el cuerpo comprimido, mídelo y después decodifícalo con `compress/gzip`.

## A través de un proxy: CONNECT frente a forma absoluta

Con una URL `https://`, el cliente abre un túnel CONNECT y envía su petición dentro de TLS. Con TLS de extremo a extremo y sin interceptación TLS, el proxy no puede leer ni cambiar `Accept-Encoding`: retransmite la respuesta cifrada. El registrador del laboratorio no analizó las cabeceras dentro de los túneles CONNECT. Con una URL `http://`, la mayoría de los clientes envían la propia petición al proxy en forma absoluta, como `GET http://host/path`, así que el proxy lee todas las cabeceras: el proxy del laboratorio vio el `deflate, gzip, br, zstd` de curl 8.22.0 con `--compressed`, el `identity` de Wget y el `gzip` de Go. Un intermediario en esa ruta también podría reescribir la cabecera o el cuerpo. El proxy del laboratorio no hizo ninguna de las dos cosas, y no se probó ningún proxy real.

Dos clientes se comportaron de otra forma con URL `http://`:

- El `fetch` integrado de Node.js 22.23.3 y 24.21.0, con `NODE_USE_ENV_PROXY=1`, envió las URL `http://` por un túnel CONNECT. El registrador del laboratorio no analizó la cabecera interior, pero esa petición HTTP seguía en texto claro y el proxy podía inspeccionarla: [CONNECT por sí solo no cifra el tráfico](https://www.rfc-editor.org/rfc/rfc9110.html#name-connect). Node.js 26.10.0 usó la forma absoluta.
- El `StreamHandler` de Guzzle envió al proxy una línea de petición en forma de origen salvo que se activara `request_fulluri`, y el proxy del laboratorio la rechazó con un 400.

Lo que cuenta para la factura depende de la regla de cada proveedor. Decodo, por ejemplo, documenta el tráfico de descarga como «cada byte recibido del sitio web de destino, incluidas las cabeceras de respuesta y las cookies», dice que en HTTPS «los bytes que pasan por el túnel del proxy también cuentan para el uso de tráfico del proxy», y da un ejemplo en el que su propio endpoint de prueba consumió 0,85 kB de descarga por `http://` frente a 3,96 kB por `https://` ([ayuda de Decodo](https://help.decodo.com/docs/proxy-traffic), consultada el 26 de septiembre de 2026, página modificada el 23 de abril de 2026). Decodo no dice si cuenta los bytes comprimidos o los decodificados. Para HTTPS con TLS de extremo a extremo dentro de CONNECT, el proxy cuenta los bytes cifrados que cruzan el túnel, incluida la respuesta comprimida y la sobrecarga de TLS; eso es una inferencia, no una afirmación de Decodo. Las API de scraping y los desbloqueadores que envían la petición al destino por ti eligen sus propias cabeceras y miden el tráfico a su manera, así que consulta la documentación de tu proveedor.

## Copiar el Accept-Encoding de un navegador

Puede que te tiente pegar en un scraper el `Accept-Encoding: gzip, deflate, br, zstd` de un navegador. Eso pide al servidor codificaciones que quizá tu cliente no sepa decodificar, y en varios clientes una cabecera puesta a mano además desactiva la decodificación. Con exactamente esa cabecera, el servidor del laboratorio eligió zstd:

- curl (las dos compilaciones), urllib, `node:http`, Go, PHP ext-curl, `HttpClient` de Java y el `StreamHandler` de Guzzle devolvieron los bytes comprimidos: una respuesta normal con un cuerpo ilegible.
- Requests y httpx hicieron lo mismo siempre que faltaba el decodificador correspondiente. En Python 3.13, Requests sin extras devolvió en bruto tanto zstd como br. En 3.14 decodificó zstd, pero devolvió br en bruto cuando el servidor envió br. httpx sin `zstandard` devolvió zstd en bruto en las dos versiones de Python, sin ninguna excepción.
- El `fetch` de Node.js 22.23.3 devolvió zstd en bruto; 24.21.0 y 26.10.0 lo decodificaron.
- El handler de cURL de Guzzle decodificó tanto zstd como br.
- aiohttp lanzó una excepción siempre que le faltaba el decodificador: br sin `Brotli`, y zstd en Python 3.13 sin `backports.zstd`. En 3.14 decodificó zstd.

La solución es dejar que el cliente escriba la cabecera. Si quieres br o zstd, instala el paquete decodificador que usa tu cliente, como `brotli`, `backports.zstd` en Python 3.13 o `httpx[brotli,zstd]`; el cliente añade entonces el token por sí mismo.

### aiohttp 3.14.3: `` Can not decode content-encoding: brotli (br). Please install `Brotli` ``

Cuando un servidor envía br y falta el paquete `Brotli`, aiohttp 3.14.3 lanza el primero de estos errores, tanto en Python 3.13.15 como en 3.14.7. El segundo es la versión para zstd, que se lanza en Python 3.13.15 sin `backports.zstd` (URL de loopback acortadas):

```text
aiohttp.client_exceptions.ClientResponseError: 400, message='Can not decode content-encoding: brotli (br). Please install `Brotli`', url='…'
aiohttp.client_exceptions.ClientResponseError: 400, message='Can not decode content-encoding: zstandard (zstd). Please install `backports.zstd`', url='…'
```

El 400 es el estado propio de aiohttp para una respuesta que no puede decodificar; el servidor del laboratorio había respondido 200 ([http_parser.py](https://github.com/aio-libs/aiohttp/blob/v3.14.3/aiohttp/http_parser.py#L1147-L1159)). Instalar `Brotli`, o `backports.zstd` para el segundo, lo resolvió en el laboratorio: aiohttp decodificó entonces br, y también zstd, incluido un cuerpo de cuatro tramas zstd. El mensaje más corto `Can not decode content-encoding: br` sale de otra ruta del mismo archivo: hay un decodificador instalado, pero el cuerpo no se pudo descomprimir.

### Servidores que gestionan mal un zstd anunciado

Anunciar una codificación puede, por sí solo, romper una petición. OpenSearch 2.19.0 se colgaba cuando el `Accept-Encoding` de una petición incluía zstd, como ocurre con las compilaciones de curl con soporte de zstd. Se corrigió para 2.19.1 y 3.0.0 ([OpenSearch#17339](https://github.com/opensearch-project/OpenSearch/issues/17339)). Si un host agota el tiempo de espera solo con clientes que admiten zstd, una petición que pida solo gzip mostrará si el problema es la negociación de zstd.

## Dónde la compresión no ayuda

- **Contenidos que ya vienen comprimidos**, como imágenes, vídeo y archivos comprimidos. «Los archivos multimedia que ya están comprimidos, como las imágenes, no se benefician de la compresión HTTP» ([Web Almanac 2021](https://almanac.httparchive.org/en/2021/compression)).
- **Respuestas que un servidor o una CDN dejan sin comprimir.** Cloudflare, por ejemplo, documenta que solo comprime las respuestas 200, 403 y 404, con un tamaño mínimo de 48 bytes para gzip y de 50 bytes para Brotli y Zstandard ([Cloudflare](https://developers.cloudflare.com/speed/optimization/content/compression/)).
- **Peticiones Range y HEAD.** Go no envía la cabecera en ninguna de las dos, y el `fetch` de Node.js envía `identity` siempre que hay una cabecera `Range`.
- **Subidas.** `--compressed` y sus equivalentes actúan sobre las descargas; para los cuerpos de las peticiones «no hay una forma estándar de comprimir» ([Everything curl](https://everything.curl.dev/http/modify/compression.html)).
- **Automatización de navegadores.** Los navegadores negocian la compresión por sí mismos: MDN da `gzip, deflate, br, zstd` como valor típico de un navegador ([MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Encoding)), y Chrome decodifica zstd por defecto desde la versión 123 ([Chrome Platform Status](https://chromestatus.com/feature/6186023867908096)), así que en Playwright o Puppeteer los bytes que recortar están en lo que descarga el navegador, no en esta cabecera.

Para las páginas que descargas una y otra vez, las peticiones condicionales son otra palanca: una respuesta `304 Not Modified` no lleva ningún cuerpo ([RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-304-not-modified); [monitorización con ETag](/es/blog/etag-monitoring-cache-control)).

## Cuánto se ahorra

Solo los clientes de la primera tabla tienen una compresión que activar. Para ellos:

ahorro en USD ≈ páginas × bytes de texto sin comprimir por página × (1 − 1/relación) ÷ 10⁹ × tu precio en USD por GB

Toma la relación de compresión del paso de comprobación en tus propias páginas, como bytes sin compresión divididos por bytes con compresión, y el precio de tu propio plan.

La compresión solo baja la factura cuando pagas por byte. En un plan de tarifa plana por Mbps o por hilos, el precio no cambia, aunque por el canal caben más páginas comprimidas. [Cuánto cuestan por GB los proxies residenciales ilimitados](/es/blog/unlimited-residential-proxies-cost-per-gb) compara las dos formas de facturar.

Un ejemplo inventado, no una medición: 100.000 páginas de 100 KB (100.000 bytes) de HTML son 10 GB sin comprimir. Con una relación de 4, la compresión ahorra 7,5 GB; con una relación de 10, ahorra 9,0 GB. Con un precio inventado de 3 a 8 USD por GB, eso supone unos 22,50 a 72 USD por un rastreo de esas páginas.

En un cliente que ya pide compresión, activarla no ahorra nada, porque ya está activada. Un decodificador opcional de br para Requests, httpx o aiohttp puede recortar algo más en algunas páginas: en las 23 páginas de documentación del laboratorio, los cuerpos br sumaron 277.665 bytes frente a 325.873 con gzip, alrededor de un 15 % menos, pero en la página sintética br ocupó más que gzip (18.451 frente a 17.750 bytes).

Las relaciones de compresión del propio laboratorio no son una previsión. La página sintética de prueba se redujo 5,64× con gzip y 5,19× con zstd. En este corpus de 23 páginas de la documentación de CPython 3.14.7, los cuerpos sin comprimir ocupaban 6,67× su tamaño en gzip, 6,61× su tamaño en zstd y 7,83× su tamaño en br. Esas cifras describen solo esos archivos.

La fórmula cuenta bytes del cuerpo; en el laboratorio, las cabeceras, TLS y el intercambio CONNECT apenas cambiaron con la compresión, como muestra la sección de comprobación. Cómo convierte un proveedor los bytes en una factura (GB o GiB, mínimos, peticiones fallidas) lo cubre [Scrapescope](https://ipvolt.com/guides/scraper-bandwidth-report). El resultado es una estimación, no la factura de un proveedor.

## Método, limitaciones y descargas

ipvolt ejecutó la matriz registrada de 567 celdas una sola vez, el 27 de septiembre de 2026, en macOS 15.7.4 (Apple M4 Pro, arm64): la preparación de 02:43 a 02:44 UTC y después toda la matriz de 02:44 a 02:45 UTC. Un servidor Python en 127.0.0.1 sirvió una página HTML sintética de 100.129 bytes, y en algunas celdas 23 páginas de la documentación de CPython 3.14.7, detrás de un proxy de reenvío en 127.0.0.1 que contaba bytes y atendía tanto peticiones en forma absoluta como CONNECT. Sin cabecera, el servidor enviaba la página sin comprimir; en caso contrario, elegía entre las codificaciones que el cliente enumeraba, con preferencia por zstd, después br, gzip y deflate. Los modos forzados enviaban gzip, br, zstd, cuatro tramas zstd o ninguna compresión, sin tener en cuenta la petición. Cada celda HTTPS confió en una CA local desechable mediante el ajuste propio del cliente, y las 21 celdas de control sin esa CA fallaron en la verificación del certificado. El fragmento de Go se compiló con un archivo del laboratorio que ponía esa CA en los `RootCAs` de `http.DefaultTransport` antes de que el fragmento lo clonara (su texto es `LAB_TRUST_GO` en [snippets.py](https://ipvolt.com/downloads/accept-encoding-defaults/harness/nodego/snippets.py)), para que la confianza no dependiera de `SSL_CERT_FILE`: go1.27.1 en macOS respeta esa variable salvo que se defina `GODEBUG=x509sslcertoverrideplatform=0`, como ocurre por defecto en un módulo que declara go 1.26. Una segunda ejecución a partir del archivo publicado, en un directorio vacío y con todas las descargas obtenidas de nuevo, coincidió en los 567 registros en todos los campos comparados (`compare.py --strict`).

Versiones: curl 8.7.1 (macOS, LibreSSL) y 8.22.0 (Homebrew, OpenSSL 3.6.4, brotli, zstd); GNU Wget 1.25.0; CPython 3.13.15 y 3.14.7 con Requests 2.34.2 (urllib3 2.8.0), httpx 0.28.1, aiohttp 3.14.3 y Scrapy 2.19.0; Node.js 22.23.3, 24.21.0 y 26.10.0 de nodejs.org, con las sumas de verificación comprobadas, con axios 1.20.0, got 16.0.0 y node-fetch 3.3.2 y 2.7.0; Go 1.27.1; PHP 8.5.8 (compilación estática, libcurl 8.20.0) con Guzzle 8.2.0 e illuminate/http 13.33.0; Temurin JDK 25.0.4.1.

El laboratorio no cubrió HTTP/2 ni HTTP/3 (el servidor solo ofrecía HTTP/1.1), compilaciones para Linux o Windows, navegadores, Wget2, urllib3 por sí solo, curl_cffi, Apache HttpClient, OkHttp, Symfony HttpClient, .NET HttpClient, reqwest de Rust, Net::HTTP o Faraday de Ruby, otras compilaciones de PHP o libcurl, ni versiones de Java posteriores a la 25. Los valores por defecto cambian de una versión a otra. Estas eran las versiones instaladas o vigentes cuando se ejecutó el laboratorio: el curl 8.7.1 del sistema en macOS es anterior a curl 8.22.0, la compilación estática de PHP iba tres versiones de parche por detrás de la 8.5.11 de php.net, y Java 25 es la versión LTS más reciente, no la versión de funcionalidades más reciente. Vuelve a ejecutar el laboratorio antes de fiarte de una fila.

Todos los archivos están bajo `https://ipvolt.com/downloads/accept-encoding-defaults/`:

- [accept-encoding-lab.zip](https://ipvolt.com/downloads/accept-encoding-defaults/accept-encoding-lab.zip) contiene todo lo de abajo; su suma de verificación está en [accept-encoding-lab.zip.sha256](https://ipvolt.com/downloads/accept-encoding-defaults/accept-encoding-lab.zip.sha256).
- [README.md](https://ipvolt.com/downloads/accept-encoding-defaults/README.md) explica cómo ejecutar el laboratorio, comparar una nueva ejecución y leer un registro.
- [results.json](https://ipvolt.com/downloads/accept-encoding-defaults/results.json) y [results.csv](https://ipvolt.com/downloads/accept-encoding-defaults/results.csv) contienen los 567 registros; [tables.md](https://ipvolt.com/downloads/accept-encoding-defaults/tables.md) tiene todas las tablas, incluida la matriz de decodificación y el texto exacto de los errores.
- [environment.txt](https://ipvolt.com/downloads/accept-encoding-defaults/environment.txt) registra el sistema operativo y la salida de cada versión; [corpus-manifest.txt](https://ipvolt.com/downloads/accept-encoding-defaults/corpus-manifest.txt), las páginas de documentación; y [SHA256SUMS](https://ipvolt.com/downloads/accept-encoding-defaults/SHA256SUMS), la suma de verificación de cada uno de los demás archivos del paquete.
- Los fragmentos de verificación: [curl-verify.sh](https://ipvolt.com/downloads/accept-encoding-defaults/harness/snippets/curl-verify.sh), [requests-verify.py](https://ipvolt.com/downloads/accept-encoding-defaults/harness/snippets/requests-verify.py), [httpx-verify.py](https://ipvolt.com/downloads/accept-encoding-defaults/harness/snippets/httpx-verify.py), [node-fetch-bytes.mjs](https://ipvolt.com/downloads/accept-encoding-defaults/harness/snippets/node-fetch-bytes.mjs), [node-fetch-verify.mjs](https://ipvolt.com/downloads/accept-encoding-defaults/harness/snippets/node-fetch-verify.mjs), [node-http-verify.mjs](https://ipvolt.com/downloads/accept-encoding-defaults/harness/snippets/node-http-verify.mjs) y [go-verify.go](https://ipvolt.com/downloads/accept-encoding-defaults/harness/snippets/go-verify.go).
- [render_tables.py](https://ipvolt.com/downloads/accept-encoding-defaults/render_tables.py) regenera las tablas a partir de `results.json` o del `harness/out/results.json` de tu propia ejecución, y [compare.py](https://ipvolt.com/downloads/accept-encoding-defaults/compare.py) compara tu nueva ejecución con la publicada.

Para reproducir toda la ejecución, descomprime el archivo, ejecuta `./setup.sh` y después `./run.sh` en `harness/`, y luego `python3 ../compare.py ../results.json out/results.json`. El README enumera lo que descarga la preparación y lo que necesita cada grupo de celdas. No se necesita ninguna cuenta de proxy.

ipvolt, que publica esta guía, es un servicio de proxies en desarrollo que tendrá precios por GB en su lanzamiento. Las mediciones usan un proxy de prueba local; los consejos sirven para cualquier proveedor, o para ninguno.

Si quieres enterarte de cuándo se abre el acceso a ipvolt, [únete a la lista de acceso anticipado](https://ipvolt.com/#waitlist-closing). Un correo cuando se abra el acceso. Nada más.

## Fuentes y lecturas adicionales

- [RFC 9110: CONNECT tunnels and the HTTPS scheme](https://www.rfc-editor.org/rfc/rfc9110.html#name-connect)
- [Python 3.14 gzip: gzip.decompress and multi-member streams](https://docs.python.org/3.14/library/gzip.html#gzip.decompress)
- [Node.js 26.10.0: resource timing buffer limit](https://github.com/nodejs/node/blob/v26.10.0/lib/internal/perf/observe.js#L103)
- [RFC 9110 section 12.5.3: Accept-Encoding (no header, identity, empty value)](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-encoding)
- [RFC 9110 section 15.4.5: 304 Not Modified (no content)](https://www.rfc-editor.org/rfc/rfc9110.html#name-304-not-modified)
- [curl man page: --compressed (the page describes 8.23.0; this text is unchanged since the tested 8.22.0)](https://curl.se/docs/manpage.html#--compressed)
- [curl man page: --max-filesize and decompression (since 8.20.0)](https://curl.se/docs/manpage.html#--max-filesize)
- [curl man page: -w, %{size_download} and %header{} (added in 7.84.0)](https://curl.se/docs/manpage.html#-w)
- [libcurl: CURLOPT_ACCEPT_ENCODING (default NULL, no header and no decoding)](https://curl.se/libcurl/c/CURLOPT_ACCEPT_ENCODING.html)
- [curl issue #11091: no Accept-Encoding by default, captured with nc](https://github.com/curl/curl/issues/11091)
- [curl issue #7516: curl can only ask for compression](https://github.com/curl/curl/issues/7516)
- [curl issue #2836: raw gzip output when a server compresses unasked](https://github.com/curl/curl/issues/2836)
- [Everything curl: HTTP compression applies to downloads](https://everything.curl.dev/http/modify/compression.html)
- [CPython 3.14.7 http.client: Accept-Encoding identity](https://github.com/python/cpython/blob/v3.14.7/Lib/http/client.py#L1296-L1299)
- [urllib3 2.8.0 util/request.py: gzip, deflate, br and zstd tokens](https://github.com/urllib3/urllib3/blob/2.8.0/src/urllib3/util/request.py#L22-L41)
- [urllib3 2.8.0 connection.py: the request path adds no compression header](https://github.com/urllib3/urllib3/blob/2.8.0/src/urllib3/connection.py#L525-L531)
- [Requests 2.34.2 utils.py: default Accept-Encoding](https://github.com/psf/requests/blob/v2.34.2/src/requests/utils.py#L91-L93)
- [httpx 0.28.1 _decoders.py: br needs brotli, zstd needs zstandard](https://github.com/encode/httpx/blob/0.28.1/httpx/_decoders.py#L381-L393)
- [HTTPX documentation: optional brotli and zstd decoders](https://www.python-httpx.org/)
- [aiohttp 3.14.3 http_parser.py: Can not decode content-encoding](https://github.com/aio-libs/aiohttp/blob/v3.14.3/aiohttp/http_parser.py#L1147-L1159)
- [Scrapy 2.19.0 release notes: br and zstd required since 2.18.0](https://github.com/scrapy/scrapy/blob/2.19.0/docs/news.rst)
- [Python 3.14: compression.zstd](https://docs.python.org/3.14/library/compression.zstd.html)
- [Node.js 26.10.0 bundled undici fetch: Accept-Encoding by scheme and Range](https://github.com/nodejs/node/blob/v26.10.0/deps/undici/src/lib/web/fetch/index.js#L1562-L1576)
- [Node.js 24.21.0 bundled undici fetch](https://github.com/nodejs/node/blob/v24.21.0/deps/undici/src/lib/web/fetch/index.js#L1514-L1528)
- [Node.js 22.23.3 bundled undici fetch: no zstd decoder](https://github.com/nodejs/node/blob/v22.23.3/deps/undici/src/lib/web/fetch/index.js#L2149-L2170)
- [Node.js 26.10.0 core HTTP client (no Accept-Encoding)](https://github.com/nodejs/node/blob/v26.10.0/lib/_http_client.js)
- [Node.js zlib: compressing HTTP requests and responses](https://nodejs.org/api/zlib.html#compressing-http-requests-and-responses)
- [node-fetch discussion #1556: default gzip, deflate, br](https://github.com/node-fetch/node-fetch/discussions/1556)
- [axios 1.20.0 http adapter: default Accept-Encoding and zstd opt-in](https://github.com/axios/axios/blob/v1.20.0/lib/adapters/http.js#L54-L57)
- [got 16.0.0 core: default accept-encoding](https://github.com/sindresorhus/got/blob/v16.0.0/source/core/index.ts#L2248-L2258)
- [Go 1.27.1 net/http transport.go: DisableCompression and automatic gzip](https://github.com/golang/go/blob/go1.27.1/src/net/http/transport.go#L199-L207)
- [Go 1.27.1 net/http response.go: Response.Uncompressed](https://github.com/golang/go/blob/go1.27.1/src/net/http/response.go#L93-L100)
- [GNU Wget manual: --compression (none is the default)](https://www.gnu.org/software/wget/manual/wget.html#HTTP-Options)
- [GNU Wget2 2.3.0 wget.c: default Accept-Encoding](https://gitlab.com/gnuwget/wget2/-/blob/v2.3.0/src/wget.c#L4306-4331)
- [Fedora change: Wget2 as wget](https://fedoraproject.org/wiki/Changes/Wget2asWget)
- [PHP manual: cURL constants, CURLOPT_ACCEPT_ENCODING](https://www.php.net/manual/en/curl.constants.php)
- [Guzzle 8.2.0 CurlFactory.php: Accept-Encoding suppressed by default](https://github.com/guzzle/guzzle/blob/8.2.0/src/Handler/CurlFactory.php#L2609-L2622)
- [Guzzle 8.2.0 Client.php: a string decode_content becomes the header](https://github.com/guzzle/guzzle/blob/8.2.0/src/Client.php#L1582-L1586)
- [Guzzle request options: decode_content](https://docs.guzzlephp.org/en/stable/request-options.html#decode-content)
- [Laravel 13.33.0 PendingRequest.php: a plain Guzzle client](https://github.com/laravel/framework/blob/v13.33.0/src/Illuminate/Http/Client/PendingRequest.php)
- [Cloudflare: Content compression](https://developers.cloudflare.com/speed/optimization/content/compression/)
- [Apache HTTP Server 2.4: mod_deflate and proxy servers](https://httpd.apache.org/docs/2.4/mod/mod_deflate.html)
- [nginx: ngx_http_gzip_static_module (gzip_static always)](https://nginx.org/en/docs/http/ngx_http_gzip_static_module.html)
- [OpenSearch issue #17339: REST API hangs with Accept-Encoding zstd](https://github.com/opensearch-project/OpenSearch/issues/17339)
- [MDN: Accept-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Encoding)
- [Chrome Platform Status: Zstd Content-Encoding (Chrome 123)](https://chromestatus.com/feature/6186023867908096)
- [Web Almanac 2021: Compression](https://almanac.httparchive.org/en/2021/compression)
- [Decodo help: how proxy traffic is calculated (vendor documentation)](https://help.decodo.com/docs/proxy-traffic)

## Guías relacionadas

- [Usar un proxy con curl: -x, variables de entorno, SOCKS5 y auth](https://ipvolt.com/es/guides/curl-proxy-setup.md)
- [Proxy en Python Requests: diccionario proxies, auth, SOCKS5](https://ipvolt.com/es/guides/python-requests-proxy.md)
- [Usar un proxy con fetch de Node.js](https://ipvolt.com/es/guides/nodejs-fetch-proxy.md)

## Sobre ipvolt

Los ejemplos usan configuraciones de proxy genéricas, con enlaces a la documentación técnica original. El comportamiento específico de cada producto debe comprobarse con tu proveedor. ipvolt sigue en desarrollo.

[Leer el original en inglés](https://ipvolt.com/guides/curl-compressed-accept-encoding.md)

## Entérate cuando se abra el acceso.

ipvolt · En desarrollo

Estamos construyendo infraestructura de proxies para desarrolladores y equipos de datos. Únete a la lista de interés para recibir un aviso cuando ipvolt esté listo.

Un correo cuando se abra el acceso. Nada más.

[Solicitar acceso anticipado](https://ipvolt.com/es/guides/curl-compressed-accept-encoding#waitlist-closing)

[Privacidad](https://ipvolt.com/privacy)

