Skip to main content

Fetching Webpages from the Command Line

The Outline SDK is a library for building network connections with configurable strategies, such as proxies and TLS fragmentation. Its command-line tools let you try those strategies without writing an application: keep the request the same, change how it connects, and compare the result.

Start with fetch to inspect a connection to getoutline.org, then try HTTP/3 over QUIC, encrypt the TLS server name with Encrypted Client Hello (ECH), and experiment with custom transports. You only need Go and an internet connection for this guide; no Outline server or account is required.

For other tasks, see DNS Lookups, Local HTTP Proxy, Testing Outline Servers, and Remote Measurement.

Get Started​

Install Go, then run:

go run golang.getoutline.org/sdk/x/tools/fetch@latest https://getoutline.org/cdn-cgi/trace

go run downloads and runs the tool; the first run may take longer while Go compiles it. These examples use a POSIX shell, such as Bash or Zsh.

The response is a short connection report from Cloudflare. Find http to see the HTTP version and ip to see your public IP address. Later, you will change the protocol and enable ECH, then see the differences in this same report. To fetch the site's HTML instead, use https://getoutline.org as the URL.

Use -help to see all flags and their defaults:

go run golang.getoutline.org/sdk/x/tools/fetch@latest -help

Put flags before the URL. To repeat a measurement with the same tool version, replace @latest with a specific release, such as @v0.2.2.

Inspect the Response​

Enable verbose output with -v. Use -method HEAD to request headers without downloading the response body:

go run golang.getoutline.org/sdk/x/tools/fetch@latest -v -method HEAD https://getoutline.org

Verbose output includes the HTTP version, status, and response headers. The tool writes diagnostics to standard error and the response body to standard output, so you can save them separately:

go run golang.getoutline.org/sdk/x/tools/fetch@latest -v https://getoutline.org \
> page.html 2> fetch.log

The default request timeout is five seconds. Use -timeout 15 for a slower connection. fetch does not follow redirects: inspect the status and Location header, then request the new URL if needed. A completed request can return an HTTP error status, so check the status as well as the tool's exit code.

Inspect the Connection with Cloudflare Trace​

getoutline.org uses Cloudflare, whose /cdn-cgi/trace endpoint returns connection details in a small text response:

go run golang.getoutline.org/sdk/x/tools/fetch@latest -v https://getoutline.org/cdn-cgi/trace

Look for these fields in the response body:

FieldWhat to check
httpHTTP version used for the request
tlsNegotiated TLS version
sniWhether the server name was sent in plaintext or encrypted
uagUser-Agent header received by Cloudflare
ipClient IP address as seen by Cloudflare; a proxy's exit IP when using one
coloCloudflare data center serving the request
locCountry associated with the client IP address

Use a GET request here (the default): HEAD omits the body containing these fields. Remove the ip value before sharing output if you do not want to disclose that address.

You can set a User-Agent with -H and check that it appears in uag:

go run golang.getoutline.org/sdk/x/tools/fetch@latest \
-H 'User-Agent: outline-cli-experiment' https://getoutline.org/cdn-cgi/trace

Compare HTTP Versions​

Select the protocol with -proto. The default is h1 (HTTP/1.1):

go run golang.getoutline.org/sdk/x/tools/fetch@latest -v -proto h1 https://getoutline.org/cdn-cgi/trace
go run golang.getoutline.org/sdk/x/tools/fetch@latest -v -proto h2 https://getoutline.org/cdn-cgi/trace
go run golang.getoutline.org/sdk/x/tools/fetch@latest -v -proto h3 https://getoutline.org/cdn-cgi/trace

HTTP/1.1 and HTTP/2 use TCP for these HTTPS requests. HTTP/3 uses QUIC over UDP. Check the reported HTTP version and the trace's http field. If HTTP/3 fails while HTTP/2 succeeds, check UDP reachability and any proxy's UDP support; a timeout alone does not establish blocking.

With -v, an HTTP/3 request also reports the negotiated QUIC version. QUIC versions are distinct from HTTP versions; the HTTP version here is HTTP/3.

Use Encrypted Client Hello (ECH)​

ECH encrypts the part of the TLS ClientHello that carries the destination server name. It requires a configuration published by the site. fetch accepts this configuration through -ech-config; it does not retrieve it automatically.

Use dig to query the site's HTTPS DNS record. On Debian and Ubuntu, dig is provided by the dnsutils package.

dig +short getoutline.org HTTPS

Find the base64 value of the ech parameter. Retrieve a fresh value rather than reusing one from an old example, since configurations can change. This shell snippet extracts the first advertised value and runs the request only if it found one:

ECH_CONFIG=$(dig +short getoutline.org HTTPS | sed -nE 's/.* ech="?([^"[:space:]]+)"?.*/\1/p' | head -n 1)
if [ -n "$ECH_CONFIG" ]; then
go run golang.getoutline.org/sdk/x/tools/fetch@latest \
-v -proto h2 -ech-config "$ECH_CONFIG" https://getoutline.org/cdn-cgi/trace
else
printf '%s\n' 'No ECH configuration found in the HTTPS record.' >&2
fi

For a quick experiment, this one-liner looks up the configuration and tries ECH over HTTP/3. Change DOMAIN to test another ECH-enabled site with a Cloudflare trace endpoint:

(DOMAIN=getoutline.org; ECH_CONFIG=$(dig +short "$DOMAIN" HTTPS | sed -nE 's/.* ech="?([^"[:space:]]+)"?.*/\1/p' | head -n 1); go run golang.getoutline.org/sdk/x/tools/fetch@latest -v -proto h3 -ech-config "${ECH_CONFIG:?No ECH configuration found}" "https://$DOMAIN/cdn-cgi/trace")

The ${ECH_CONFIG:?...} check stops the command if the lookup returns no ECH configuration. The parentheses keep the variables local to this one-liner.

Check for sni=encrypted in either trace response. If the configuration is rejected, retrieve the HTTPS record again and retry. An empty lookup may mean the resolver does not return HTTPS records or the site is not advertising ECH.

Inspect the Outer Server Name​

The ECH configuration is base64-encoded binary data. Its public name, used for the visible outer SNI, is stored as text inside it. Using ECH_CONFIG from the multiline example above, decode it and display the printable strings:

printf '%s' "$ECH_CONFIG" | base64 -d | strings -a

Look for cloudflare-ech.com, Cloudflare's shared public name. The inner server name, getoutline.org, is encrypted; observers can still see the outer name. strings is a quick inspection aid and may also print unrelated bytes from the binary configuration. It does not parse or validate the ECH structure, and decoding a configuration does not prove the server accepted it: check sni=encrypted in the response.

On macOS, use base64 -D if -d is unavailable. On Debian and Ubuntu, strings is provided by the binutils package.

ECH does not hide the destination IP address or encrypt DNS lookups. See DNS Lookups for resolver experiments and Cloudflare's ECH documentation for how Cloudflare uses ECH.

Try a Transport Configuration​

The -transport flag accepts a config URL. A config URL describes a connection strategy, such as a proxy or TLS fragmentation. Components separated by | build on the transport to their left. Quote the entire value so the shell does not interpret | or &.

For example, tlsfrag:1 splits the initial TLS ClientHello into two TLS records: the first carries one byte of the original record's payload, and the second carries the rest. Try it with HTTP/2:

go run golang.getoutline.org/sdk/x/tools/fetch@latest -v -proto h2 \
-transport 'tlsfrag:1' https://getoutline.org/cdn-cgi/trace

Compare success or failure with the direct HTTP/2 request above. On a working connection, the response can look identical: you changed the handshake's structure, not the HTTP request. On a network that interferes with TLS handshakes, that change may affect whether the request succeeds.

For HTTP/3, quicprelude sends extra datagrams before packets that may carry a QUIC ClientHello. Compare the result with the direct HTTP/3 request above:

go run golang.getoutline.org/sdk/x/tools/fetch@latest -v -proto h3 \
-transport 'quicprelude' https://getoutline.org/cdn-cgi/trace

Transport components must support the kind of connection being tested: tlsfrag operates on TCP streams, while quicprelude operates on UDP packets. The config URL reference lists available components and their parameters.

To test these strategies from another network, continue with Remote Measurement.