TIL: You can make HTTP requests without curl using Bash /dev/TCP
- xenadu02 - 10974 sekunder sedanAs 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).
- basilikum - 18424 sekunder sedan> 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.
- mrshu - 21406 sekunder sedanI 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!
- simonw - 17953 sekunder sedanNeat, 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 ... - pickle-wizard - 3964 sekunder sedanAt 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.
- Sohcahtoa82 - 2812 sekunder sedanThis was something I learned about 10 years ago when earning my OSCP, useful during penetration tests and CTFs when you get a low-priv shell that's running a minimal OS (No curl, nc, python, etc.) but running a web server listening on localhost.
Using /dev/tcp was also handy in getting that initial low-priv shell.
- MisterTea - 2458 sekunder sedanTIL: bash and other shells try to copy Plan 9's /net directory and the kernel ip(3) file server. Too bad it's not a real file system. And a missed opportunity to call the root of the path /net.
- dennis16384 - 5314 sekunder sedanThis 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.
- pgtan - 8180 sekunder sedanIt is a KornShell feature since ca. 1997
https://github.com/ksh93/ast-open-archive/blame/master/src/c...
- sam_lowry_ - 16490 sekunder sedanA 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;"
- tzot - 3461 sekunder sedanI 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.
- nedt - 2905 sekunder sedanI actually have a couple of Dockerfiles that are using exactly this in the HEALTHCHECK. Less packages to install.
- timwis - 7983 sekunder sedanYou 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
- saidinesh5 - 12360 sekunder sedanFun 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...
- chaps - 6656 sekunder sedanOnce 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.
- washbasin - 10867 sekunder sedanThis 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.
- AndrewStephens - 18690 sekunder sedanThis 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.
- dchest - 15066 sekunder sedanIt'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.
- nesarkvechnep - 9520 sekunder sedanI find /dev/udp much more useful. I can create aliases for fire and forget commands to my daemons without actually writing *ctl program.
- Retr0id - 13258 sekunder sedanIt'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.
- orthogonal_cube - 16884 sekunder sedanIt 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.
- uberex - 1014 sekunder sedantelnet then?
- geoctl - 16682 sekunder sedanI 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.
- devsda - 17095 sekunder sedanYes, 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.
- sc68cal - 19050 sekunder sedanThat's pretty neat, thanks for sharing
- m3047 - 13418 sekunder sedanAt least on my systems there's also /dev/udp...
- alienbaby - 16440 sekunder sedanReminds me of telnetting to port 80 to make a get request years and years ago
- Steeeve - 15303 sekunder sedanbrb. recompiling bash in all my base images.
- black_knight - 3895 sekunder sedanWait until they hear about Plan 9!
- alienbaby - 15469 sekunder sedanReminds me of using telnet to port 80 to make get requests aeons ago
- michaeltm - 473 sekunder sedan[dead]
- animanoir - 2445 sekunder sedan[dead]
- phantasmat - 16175 sekunder sedan[flagged]
Nördnytt! 🤓