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

mrshu 352 points 164 comments June 16, 2026
mareksuppa.com · View on Hacker News

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...

Semantic search powered by Rivestack pgvector
10,715 stories · 100,765 chunks indexed