

Join the discussion
Write your take first — we'll ask for email only when you're ready to publish.
- Hacker News
- For the next level unlock try to make a HTTP/3 request over /dev/udp.by mlhpdx
- I would use HTTP/1.0 without a need for Connection: close. Unless 1.0 is not generally supported anymore, but this is not the case in my experience.by tzot
- Yep. I also learned that too when watching Bauhinia team members' using this to solve a CTF challenge :p It is a multi-series CTF that you get shell from first a ROP chain to system, but you are effectively jailed from running anything but bash, so the only thing you can use is read and cat, and they used the cat /dev/tcp, then redirected it to a pseudo-tty, and read the content of the pseudo-tty in order to get the URL to the inner system. The flag, there it is.by stevefan1999
- 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...
by saidinesh5 - 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;"
by sam_lowry_ - Note that this is not what the article is about. Bash has a fake /dev/tcp path that opens sockets. What you have there is just perl opening a socket normally. Great solution, but the interesting bit is that fake path.by hn92726819
- Note that this didn't work historically on Debian, and presumably Debian-derived distros, where the virtual file TCP access was disabled by default. The position was reversed (and the capability enabled) in 2009, AFAIU. There's discussion and links in Bug #146464:
<https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=146464#37>
As others have mentioned, there are numerous other ways to directly access network features using shell tools, including curl (noted in TFA's title), wget, the HEAD and GET commands (from Perl), netcat (nc), socat, telnet, and I'm quite sure others.
by dredmorbius - 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:
Where `service` is just the hostname of whatever you’re talking to and 8642 is the port you are trying to talk HTTP to.exec 3<>/dev/tcp/service/8642 printf 'GET /health HTTP/1.1\r\nHost: service\r\nConnection: close\r\n\r\n' >&3 cat <&3Pretty cool!
by mrshu - It seems pretty cool, but I am wondering if there's any drawback on just using images that support curl? I can't think of any and to me it's kinda a must have, even on production imagesby sevenzero
- > 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.
Zsh has its own zsh/net/tcp and zsh/zftp modules.
https://zsh.sourceforge.io/Doc/Release/TCP-Function-System.h...
https://zsh.sourceforge.io/Doc/Release/Zsh-Modules.html#The-...
https://zsh.sourceforge.io/Doc/Release/Zftp-Function-System....
by Coelacanthus - > 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.
by basilikum - Nice parameter expansion examples in that bash-web-server. It uses the $_ parameter in ways I hadn’t thought to before, often preceded by a single : ${x} line for pre-processing of the variable.by andelink
- There's even a Rails-like framework for Bash: https://github.com/jneen/ballsby tombert
- Someone did a Minecraft server in pure bash.
https://sdomi.pl/weblog/15-witchcraft-minecraft-server-in-ba...
by sgjohnson - it's not that insane. i've been manually typing http requests in since before http/1.1 and the mandatory host header.
it is insane to use it for anything serious (also the opposite, implementing webservers in bash), but for quick testing it's pretty great!
by a-dub - >No, you can't write 10 lines of code, you have to import a 100k LOC dependency
Common misconception, if you want to replace a dependency on a swiss knife you don't need to implement a swiss knife, sometimes you can just implement the last helix of the corkscrew.
by TZubiri - > No, it can not. Bash lets you open TCP sockets.
Very fair pushback -- I did get carried away and will update the article to be more precise. Thanks for raising it!
> For less insane, non-bash shells there is always nc which is usually probably the wiser choice.
For completeness, `nc` or any netcat equvialent I could think of was not available in the image I was trying this with. It would certainly be a better option though.
by mrshu - 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...
by iam-TJ - In Plan 9 you did have a real (synthetic) /net, and could do that and more from any program. You could even mount /net from another machine via 9P protocol and have an instant VPN...
9front lets you play with that on Linux.
Some Plan 9 like /net things are visible in Go libraries... (Rob Pike legacy)
by gatestone - > You could even mount /net from another machine via 9P protocol and have an instant VPN...
This is awesome.
- Neat, works against example.com
Outputs: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
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!HTTP/1.1 200 OK Date: Tue, 16 Jun 2026 17:37:45 GMT Content-Type: text/html ...by simonw - This works too
You can even take out the \r though they should be thereexec 3<>/dev/tcp/example.com/80 printf 'GET / HTTP/1.1\r Host: example.com\r Connection: close\r \r ' >&3 cat <&3by gabrielsroka - example.com is also great for that reason when something fails about a captive portal on a public WiFi.
I open my web browser and go to http://example.com and get redirected to the captive portal page again and retry completing what they need from me to get internet access.
- 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).
by xenadu02 - 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.
by jazz9k - 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.
by vbezhenar - Last century I would read and send personal email from work using telnet to pop3 and smtp respectively.by kps
- 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/lp0by TylerE - 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.
by eqmvii - 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
by wing-_-nuts