TIL: You can make HTTP requests without curl using Bash /dev/TCP

Hacker News Top Tools

Summary

Learn how to use bash's built-in /dev/tcp feature to make HTTP requests from containers without curl or wget, useful for lightweight debugging in restricted environments.

No content available
Original Article
View Cached Full Text

Cached at: 06/16/26, 05:34 PM

# Making HTTP requests from a container that has no curl, using bash /dev/tcp Source: [https://mareksuppa.com/til/bash-dev-tcp-http-without-curl/](https://mareksuppa.com/til/bash-dev-tcp-http-without-curl/) I needed to check that one container could reach another over an internal Docker network: a plain`GET /health`against a service on a shared network\. The obvious move is`curl http://service:8642/health`\. But this app image was stripped right down, with no`curl`or`wget`and nothing else around that I could use to open a socket\. As it turns out,`bash`can speak HTTP by itself\. Opening a connection to a host and port and writing the request by hand needs nothing beyond the shell that’s already there: bash ``` exec 3<>/dev/tcp/service/8642 printf 'GET /health HTTP/1.1\r\nHost: service\r\nConnection: close\r\n\r\n' >&3 cat <&3 ``` `service`here is just the hostname of whatever you’re talking to\. It has to resolve and be reachable from wherever you run this, so it needs to be set up first: a container or service name on a Docker network you’ve configured, or any DNS name that resolves\. Swap in your own host and port\. That prints the whole response: the status line, the headers, the blank line, and the body\. To add a header, such as an`Authorization: Bearer`token, put another`\\r\\n`\-terminated line before the blank line that ends the request: bash ``` exec 3<>/dev/tcp/service/8642 printf 'GET /v1/models HTTP/1.1\r\nHost: service\r\nAuthorization: Bearer %s\r\nConnection: close\r\n\r\n' "$API_KEY" >&3 cat <&3 ``` What caught me out the first time is that`/dev/tcp`isn’t a real device file\. There’s no such path on disk;`ls /dev/tcp`finds nothing, and`cat /dev/tcp/\.\.\.`from another shell just errors\. It’s a redirection that`bash`handles internally\. From the[Bash manual](https://www.gnu.org/software/bash/manual/bash.html#Redirections): > `/dev/tcp/host/port`– If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open the corresponding TCP socket\. The names were[picked because](https://www.gnu.org/software/bash/manual/bash.html#Redirections)no real Unix has a`/dev/tcp`or`/dev/udp`hierarchy, so there’s nothing to collide with\. Bash does the DNS lookup and the`connect\(2\)`for you, and`exec 3<\>`hands the socket a file descriptor \(`3`\) you read from and write to like any other\. A few things worth knowing: - The`Connection: close`header matters\. Without it the server keeps the connection open after it responds, which is the HTTP/1\.1 default, and`cat <&3`then waits forever for bytes that never arrive\. Asking the server to close means`cat`reaches EOF and returns\. Wrapping the call in`timeout 6 bash \-c '\.\.\.'`covers you either way\. - There’s no TLS\.`/dev/tcp`opens a raw socket, so this only works for plaintext HTTP\. For`https`you’d need`openssl s\_client`, and by then you may as well have the proper tools\. - This is a`bash`feature, not POSIX\.`dash`\(Debian’s`/bin/sh`\) and`zsh`don’t have it, so a`\#\!/bin/sh`script can’t use it\. Call`bash`directly\. - It’s a compile\-time option, switched on when`bash`is built with`\-\-enable\-net\-redirections`\. Most mainstream builds enable it, and it worked without any fuss in the Debian\-based image I was in, but Debian[shipped it disabled for years](https://bugs.launchpad.net/ubuntu/+source/bash/+bug/215034), so on an old or very minimal system it’s worth checking first\. For day\-to\-day work`curl`is still the right tool\. But inside a deliberately small container where you can’t install anything, this gets a quick check done without adding a package\.

Similar Articles

Show HN: Mezz, a curl-able WiFi sandbox for IoT pentesting

Hacker News Top

Mezz is a self-contained WiFi sandbox tool for inspecting IoT device traffic, providing an isolated network with DNS logging and optional MITM proxy, all deployable via Docker on a Linux host with AP-capable WiFi.

tail CI logs over SSH

Lobsters Hottest

This article describes a new feature that allows users to tail CI logs over SSH using a terminal user interface, requiring no installation and handling ANSI codes correctly.

Tracing HTTP Requests with Go's net/HTTP/httptrace

Hacker News Top

This article explains how to use Go's net/http/httptrace package to trace HTTP request phases (DNS, connection, TLS, etc.) via context-based hooks, and demonstrates building a CLI trace tool and a RoundTripper logger.