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:
| Field | What to check |
|---|---|
http | HTTP version used for the request |
tls | Negotiated TLS version |
sni | Whether the server name was sent in plaintext or encrypted |
uag | User-Agent header received by Cloudflare |
ip | Client IP address as seen by Cloudflare; a proxy's exit IP when using one |
colo | Cloudflare data center serving the request |
loc | Country 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.