TIL: You can make HTTP requests without curl using Bash /dev/TCP
mrshu
352 points
164 comments
June 16, 2026
Related Discussions
Found 5 related stories in 104.2ms across 10,715 title embeddings via pgvector HNSW
- purl: a curl-esque CLI for making HTTP requests that require payment bpierre · 16 pts · March 21, 2026 · 46% similar
- curl > /dev/sda: How I made a Linux distro that runs wget | dd astralbijection · 140 pts · March 24, 2026 · 45% similar
- Using the internet like it's 1999 joshuablais · 132 pts · April 23, 2026 · 41% similar
- The inner workings of TCP zero-copy mfrw · 49 pts · March 02, 2026 · 39% similar
- What’s on HTTP? elixx · 48 pts · March 18, 2026 · 39% similar
Discussion Highlights (20 comments)
mrshu
I ran into this while checking connectivity between containers on an internal Docker network where the image had neither curl nor wget. The main surprise was that Bash has /dev/tcp which lets you do the equivalent of an HTTP request with a bit of shell magic, for instance: 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 Where `service` is just the hostname of whatever you’re talking to and 8642 is the port you are trying to talk HTTP to. Pretty cool!
sc68cal
That's pretty neat, thanks for sharing
AndrewStephens
This is pretty neat if all you need is to ping a local server but please use curl (or something equivalent) for contacting remote services. HTTP1.1 seems like such a simple protocol but in the real world you need to deal with proxies, different encodings, and redirects. Curl takes care of that (and a host of other annoying stuff) for you.
basilikum
> As it turns out, bash can speak HTTP by itself. No, it can not. Bash lets you open TCP sockets. What you are doing here is trying to speak HTTP yourself, which is fine for testing and debugging, and hella cool for fun to do by hand, but you will shoot yourself in the foot if you try to use this pseudo http client unattended in reality. This toy code does not parse HTTP properly and will break. You could of course write a full http/1.1 client in bash, you can even do a full http server in pure bash: https://github.com/bahamas10/bash-web-server For less insane, non-bash shells there is always nc which is usually probably the wiser choice.
simonw
Neat, works against example.com exec 3<>/dev/tcp/example.com/80 printf 'GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n' >&3 cat <&3 Outputs: HTTP/1.1 200 OK Date: Tue, 16 Jun 2026 17:37:45 GMT Content-Type: text/html ... I always end up on example.com for this kind of thing because there are so few domains these days that don't enforce https!
devsda
Yes, it used to be my goto few times when some devices tried to lockdown everything with bare minimum core utils and no network capable tools like curl etc.
orthogonal_cube
It was fun exploring this to make a native-shell-only peer-to-peer file transfer utility at work for some automation scripts. At least, it was until trying to replicate it in Powershell was somehow triggering Crowdstrike and the corporate Cybersecurity team thought I was writing malware.
geoctl
I discovered this bash trick by chance when I was once trying to healthCheck the Envoy's official OCI image container which didn't include curl or wget while forcing the envoy admin interface to listen on localhost which breaks the traditional k8s httpGet checks.
sam_lowry_
A few years ago I had to do this for a SpringBoot health check from a Docker container: FROM openjdk:11-jre-slim HEALTHCHECK --start-period=10s --timeout=3s --retries=5 \ CMD perl -e "use IO::Socket; $sock = IO::Socket::INET->new(Proto => 'tcp', PeerAddr => 'localhost', PeerPort => '8888') or die $@; $sock->autoflush(1); print $sock 'GET /actuator/health HTTP/1.1' . chr(0x0a) . chr(0x0d) . 'Host: localhost:8888' . chr(0x0a) . chr(0x0d) . 'Connection: close' . chr(0x0a) . chr(0x0d) . chr(0x0a) . chr(0x0d); while (my $line = $sock->getline ) { if ($line =~ /UP/) {exit;} }; close $sock; exit 1;"
alienbaby
Reminds me of telnetting to port 80 to make a get request years and years ago
alienbaby
Reminds me of using telnet to port 80 to make get requests aeons ago
Steeeve
brb. recompiling bash in all my base images.
dchest
It's interesting that most of the comments here are about using this feature to bypass security restrictions (whether valid or not). It says a lot about the attack surface of GNU utilities caused by featuritis.
m3047
At least on my systems there's also /dev/udp...
Retr0id
It's a fun trick, but I really don't like that bash does this. It's such an un-clean interface, and I'm not aware of any use cases beyond trying to exfiltrate data from a badly locked-down shell.
saidinesh5
Fun story: A few years ago, I worked for a small company that customized off the shelf routers to enable businesses provide Wifi Hotspots. The routers were very basic model with very limited flash memory (~4MB?). I was brought in to build firmware for those routers. I ended up customising openwrt - removed all kinds of packages to make their packages fit on those routers. At the end, I had less than 4KB space, And I needed to implement a "heart beat" service. A lot of routers were behind firewalls that only allowed http, https and a couple of other protocols. Libcurl was too heavy. So I ended up writing a shell script that used this feature of bash to send out heart beats. Fun times...
xenadu02
As a kid in the late 90s my mind was blown when I realized I could telnet to port 80, 25, or 110 and interact with the servers manually. Simple get: GET / HTTP/1.1 Content-Type: text/html User-Agent: l33t hax0rs lol X-Funny-Monkey: farts For sending a mail message on port 25: HELO mail-from: whoever@whatever.com mail-to: sysadmin@yaya.com <other headers> <blank line> Body of the message yay. <two blank lines to end> POP3 was so long ago I forgot but you could list the mailboxes then get individual messages and so on. This revelation was the beginning of "there is no magic" for me. The realization that every part of the computer was built by human beings and was at some level understandable if one undertook the effort. Perhaps most people in the future won't bother. They'll just let agents do it all. I'm sure that will leave some interesting holes in various systems for people willing to actually learn how they work without the filter of a model (or its safety rails).
washbasin
This is an old post-compromise trick used when an attacker needs to download a payload or make a network connection and curl, wget and nc are all not available.
nesarkvechnep
I find /dev/udp much more useful. I can create aliases for fire and forget commands to my daemons without actually writing *ctl program.
pgtan
It is a KornShell feature since ca. 1997 https://github.com/ksh93/ast-open-archive/blame/master/src/c...