81 comments

[ 4.2 ms ] story [ 99.2 ms ] thread
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!

That's pretty neat, thanks for sharing
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.
> 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.

Need to be clear that "full http server in pure bash" is incorrect. Bash cannot listen on a TCP/UDP socket for incoming connections.

bash-web-server project builds a C language socket listener [0] that is dynamically loaded at run-time as a "built-in" module that makes the functionality available.

[0] https://github.com/bahamas10/bash-web-server/tree/main/loada...

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!
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.
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.
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.
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;"

Reminds me of telnetting to port 80 to make a get request years and years ago
Reminds me of using telnet to port 80 to make get requests aeons ago
brb. recompiling bash in all my base images.
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.
At least on my systems there's also /dev/udp...
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.
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...

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

Last century I would read and send personal email from work using telnet to pop3 and smtp respectively.
When I was 12, I learned about open SMTP relays and how to spoof email this way. I once spoofed an email between two rivals on a community I was a part of and started a flame war.

Good times.

I must have tried to write the same "perfect" IRC client from scratch in C a dozen times growing up...
Me too! Writing Winsock and learning WinAPI on XP then Vista. It took me a while to realise Linux was better / OSX was my gateway drug haha
Yep! It’s all just text files. Lots of acronyms in top of lots of ways to generate, send, and read structured text files.

One day I realized even databases were just text files and I had to sit down.

Back in those days not only was there was no DKIM or SPF, most SMTP servers would accept email from anyone anywhere to anyone anywhere (i.e. 'open relay').
You can't do that with HTTP/2 (but thankfully every server still talks HTTP/1).

You also can't do that with TLS (and a lot of servers won't talk HTTP other than redirects). openssl s_client instead of telnet might allow you to tunnel text inside TLS, but that feels like a cheating.

And many other modern protocols, sadly, prefer binary encoding, which makes it impossible to tinker with it on wire level, not without specialized tools anyway.

I think people in the future will bother. I tried to make a fire with sticks once, I tried to burn a clay brick, these old things can be a lot of fun and sometimes of real use. If anything, AI actually makes tinkering a lot more easier. You don't need to dig into RFC to check your mail, you can just talk to LLM about it and it'll help you with most typical IMAP commands, for example.

I sent many an email from jacques.chirac@elysee.fr, the veneer of the terminal helping, my friends were quite impressed by how good a hacker I was. Good olde days when many DKIM/SPF weren't a thing yet and SMTP servers weren't even authenticated.
It was quite fun.

But at my first work (begining of 2000s) there was one person that made a fun email, using From of head of company (or was it head of that particular division) to his coworker with congratulations for pay increase and promotion. It would be all great, but that coworker didn't catch the joke and replied to it (person in the From wasn't amused). Author of the joke was fired (which is not easy thing to do in Europe), some people don't catch jokes.

Screwing with someones career isn't a joke. Shitcan-ee fully deserved it.
When I was working at the computing center at University of Illinois at Chicago in the 80s, we found a fairly simple route to spoofing emails from other users through batch jobs on the MVS side of our mainframe. It came crashing to a halt when someone sent a spoofed email from the director to one of the other employees saying that they were fired and to bring their keys to her office immediately. I think the person responsible nearly lost his job over that, but as I recall, the ability to do this was never closed.
I ran an EFnet server in the mid-90s and therefore could fake DNS entries for myself. I was often on IRC from "aliens.gov" which seemed funny at the time, but now that domain finally became an unfunny reality.
> Perhaps most people in the future won't bother. They'll just let agents do it all.

But can you imagine the look on some young teen’s face when they train their own GPT on their local computer for the first time?

It was also cool discovering the ATA commands to drive the modem. You could “war-dial” numbers, or manually initiate Internet connection, or connecting to a bbs
I never figured out you could do it with HTTP, but for some reason I did for FTP and IRC. I don't know why I first tried using a telnet client but I couldn't believe it when the server responded to me!
Isn't that the whole point of TCP? Creating a pair of two streams you can read out of and write to out of less reliable network primitives?

I am not sure why this is a revelation. Any college level networking course would cover this?!

HELO is for SMTP, EHLO for ESMTP. You could access some “advanced” features of the server if you told it you speak ESMTP.
Also memories of making printing work on Linux in the late 90s to some old beast of an HP Laserjet. CUPS exited but was a pain to configure, so I’d just convert whatever I wanted to print to postscript, then

    Cat homework.ps > /dev/lp0
Ah this one was so long ago I forgot about it.

The realization that (on DOS) "copy con file.txt" as the world's worst text editor or "copy file lpt1:" was treating physical devices as files. Everything on the computer was arbitrary so you could make anything behave like anything else (for some definition of behave like)!

Not a great insight I'll grant but a key one that everyone has to go through to be any good.

I was in the hospital at 13, 7 hours from home, and lonely. They had a councilor there who took pity on me and agreed to let me use her computer to check my email. Only provision was that I couldn't install anything, and couldn't change any settings.

She stood behind me and watched bemused as I fired up telnet, connected to my ISP's pop server and started reading emails from friends. I think I did manage to send some emails back via SMTP but I was not as good with that protocol.

If you could bottle the creativity and enthusiasm of a bored teen, I'm pretty sure you could take over the world

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.
I find /dev/udp much more useful. I can create aliases for fire and forget commands to my daemons without actually writing *ctl program.
You could also use nsenter if curl is installed on the host, eg

docker inspect -f '{{.State.Pid}}' container-name

# let's imagine that outputs 814538

nsenter -t 814538 -n curl example.com

Once had a coworker tell me to never to use this because "you never know when the customer doesn't have bash installed; use python instead" even though our contract required that the customer had bash. I'm still laughing at that.
This is the kind of content we all deserved in 2026, and this is still why I ask during interviews to explain how cookies are represented in HTTP protocol.
At a past job the security team wouldn't let us have netcat or curl on our systems. So I just used /dev/TCP to get around that. The ergonomics were not as nice as using netcat or curl, but it got the job done.