Make HTTP Requests Without Curl: Bash /dev/tcp Guide
Learn how to perform raw HTTP requests using Bash /dev/tcp without needing curl or wget in minimal Linux environments.

- NV Trends
- 9 min read

Imagine you are logged into a remote server, perhaps a highly stripped-down production environment or a minimal Docker container. You need to verify if an internal API is responding or if a specific port is open, but you quickly realize that the usual suspects—curl, wget, and even telnet—are missing. In the world of modern DevOps and system administration, we often take these tools for granted. However, there is a powerful, “hidden” feature built directly into the Bash shell that allows you to communicate over the network using nothing but standard shell commands.
Today I Learned (TIL) that you can actually make full HTTP requests and interact with network services using Bash’s /dev/tcp and /dev/udp virtual files. This isn’t just a party trick; it is a fundamental understanding of how Linux treats everything as a file and how the Bash shell interprets specific paths to facilitate socket programming. For developers and sysadmins in India working on cloud-native applications or optimizing edge computing resources, mastering this technique can be a lifesaver when debugging in restricted environments.
In this comprehensive guide, we will dive deep into the mechanics of Bash networking. We will explore how to open sockets, send raw HTTP headers, and receive data from a remote server without installing a single extra byte of software. Whether you are curious about the inner workings of the TCP/IP stack or need a fallback for your automation scripts, understanding /dev/tcp is an essential skill for any serious technologist.

The Mystery of /dev/tcp: Is it a File?
If you try to find /dev/tcp on your Linux filesystem by running ls -l /dev/tcp, you will likely be met with an error message stating “No such file or directory.” This is because /dev/tcp is not a physical file, nor is it a standard device file created by the kernel. Instead, it is a feature of the Bash shell itself.
When Bash sees a redirection to a path starting with /dev/tcp/host/port, it doesn’t look for a file on the disk. Instead, it interprets this path and opens a network socket to the specified host and port. This feature was introduced in Bash to provide a simple way for scripts to interact with network services without needing external binaries.
It is important to note that this feature is specific to Bash (and a few other shells like Ksh and Zsh). If you try to use this in a standard POSIX sh or dash (which is the default /bin/sh on Ubuntu), it will fail. For Indian developers working with various Linux distributions, knowing which shell you are in is the first step to using this power.
How Bash Handles the Network Redirect
When you execute a command like cat < /dev/tcp/google.com/80, Bash performs the following steps:
- It parses the filename and recognizes the
/dev/tcpprefix. - It extracts the hostname (
google.com) and the port (80). - It uses the standard C library functions (like
getaddrinfoandconnect) to establish a TCP connection. - It creates a file descriptor that represents this connection.
- It redirects the input or output of the command to that file descriptor.
The Anatomy of a Raw HTTP Request
To use /dev/tcp to make an HTTP request, we must understand what tools like curl do under the hood. HTTP is a text-based protocol. When you visit a website, your browser sends a specific block of text to a server, and the server sends back another block of text.
A minimal HTTP/1.1 GET request looks like this:
GET / HTTP/1.1
Host: example.com
Connection: close
Note the blank line at the end; this is crucial as it signals the end of the request headers. If we can send this exact text to a server on port 80, the server will respond with the HTML content of the page.
Why port 80 and not 443?
One major limitation of the /dev/tcp method is that it handles raw TCP segments. It does not handle the SSL/TLS handshake required for HTTPS (port 443). To make an HTTPS request, you would need to wrap the connection in an encryption layer, which usually requires openssl or gnutls. Since our goal is to work without external tools, we will focus on port 80 for this demonstration. In an internal network environment (common in many Indian corporate data centers), many microservices still communicate over plain HTTP or specific TCP ports where this technique remains highly effective.
Step-by-Step: Making Your First Request
Let’s walk through the process of making a request to a public website using only Bash. We will use a special Bash command called exec to manage our file descriptors.
1. Opening a File Descriptor
First, we open a file descriptor for both reading and writing. We’ll use descriptor 3 because 0, 1, and 2 are reserved for stdin, stdout, and stderr.
exec 3<>/dev/tcp/google.com/80
This command tells Bash to open a read/write socket to google.com on port 80 and assign it to file descriptor 3.
2. Sending the Request
Now, we send our HTTP headers into that file descriptor using echo.
echo -e "GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n" >&3
-e: Enables interpretation of backslash escapes (needed for\r\n).\r\n: Carriage return and line feed, the standard line endings for HTTP.>&3: Redirects the output ofechointo file descriptor 3.
3. Reading the Response
Finally, we read the response from the file descriptor using cat.
cat <&3
<&3: Redirects the input ofcatfrom file descriptor 3.
If everything went correctly, your terminal will be flooded with the HTML source of Google’s homepage. Once the server finishes sending the data and closes the connection (because we sent Connection: close), cat will terminate.
4. Cleaning Up
It is good practice to close the file descriptor when you are done.
exec 3<&-
exec 3>&-
Practical Use Cases for Indian Developers
While curl is almost always better for complex tasks, there are several scenarios where /dev/tcp shines in the Indian tech ecosystem.
Testing Microservices Connectivity
In a Kubernetes cluster or a complex microservice architecture, you might find yourself inside a “sidecar” container that is extremely minimal for security reasons. If you need to check if the “Payments” service is reachable from the “Order” service, a quick Bash line is faster than trying to install iputils-ping or curl.
(echo > /dev/tcp/payments-service/8080) && echo "Service is Up" || echo "Service is Down"
This is an incredibly efficient way to perform a “port scan” or a “health check” without any overhead.
Automating Simple Monitoring Scripts
Suppose you are running a small startup and want to monitor your server’s health without spending thousands of Rupees (Rs.) on premium monitoring tools. You can write a tiny cron job script that checks if your main database port is open and sends an alert if it isn’t.
#!/bin/bash
# Simple Port Checker
DB_HOST="localhost"
DB_PORT=5432
if timeout 2 bash -c "echo > /dev/tcp/$DB_HOST/$DB_PORT" 2>/dev/null; then
echo "Database at $DB_HOST:$DB_PORT is accepting connections."
else
# Imagine a notification logic here
echo "ALERT: Database is DOWN!"
fi
Grabbing Metadata in Cloud Environments
If you are working with AWS, Azure, or Google Cloud, you often need to fetch metadata about the current instance (like its public IP). These services usually provide a local HTTP metadata server at 169.254.169.254. If curl is missing, /dev/tcp can fetch your instance ID or IAM role details directly.
Beyond HTTP: Interacting with Other Protocols
The beauty of /dev/tcp is that it isn’t limited to HTTP. Since it provides a raw TCP socket, you can interact with any text-based protocol.
Sending an Email via SMTP
You can manually talk to an SMTP server to test mail flow. This is a classic “old school” sysadmin trick that is still relevant today.
exec 3<>/dev/tcp/mail.example.com/25
echo "HELO mydomain.com" >&3
# ... follow SMTP protocol steps ...
Talking to Redis
If you need to clear a cache or check a key in a Redis instance and don’t have redis-cli, you can send Redis Serialization Protocol (RESP) commands directly.
exec 3<>/dev/tcp/127.0.0.1/6379
echo -e "PING\r\n" >&3
head -n 1 <&3
The server will respond with +PONG.
Security Considerations
As with any powerful tool, /dev/tcp has security implications. Because it is built into the shell, it can be used by attackers to exfiltrate data from a compromised system where they might not have the permissions to install new tools. Many security hardening guides for Linux recommend disabling the /dev/tcp feature in Bash if it isn’t strictly necessary.
For developers, the takeaway is to use this tool for debugging and lightweight automation, but always ensure that your production scripts are robust and handle errors gracefully. If a script depends heavily on networking, it might be time to move from Bash to a more robust language like Python or Go.
Troubleshooting Common Issues
Using /dev/tcp can be finicky. Here are some common hurdles you might encounter:
- Permission Denied: Some Linux distributions (like RHEL or CentOS in certain configurations) disable this feature at compile-time for security. If it doesn’t work, check if your version of Bash supports it.
- Timeouts: Bash doesn’t have a built-in timeout for the
/dev/tcpredirection. If the host is unreachable, the command might hang indefinitely. It is always better to wrap your command in thetimeoututility as shown in the examples above. - Read/Write Sync: When reading from a socket with
cat, the command will wait until the server closes the connection. If the server keeps the connection open (HTTP Keep-Alive), your script might hang. Always useConnection: closein your HTTP headers to avoid this.
Conclusion
The ability to make HTTP requests and open TCP sockets using nothing but Bash is a testament to the flexibility of the Linux environment. It reminds us that underneath the polished interfaces of our modern tools lie simple, text-based protocols that govern the internet.
For the Indian tech community—from students in Bengaluru learning the ropes of systems programming to seasoned engineers in Mumbai managing global cloud infrastructures—this “TIL” is more than just a trivia point. It is a practical fallback that empowers you to troubleshoot, automate, and understand your systems at a deeper level.
Next time you find yourself on a “broken” server without curl, don’t panic. Remember that you have a powerful networking tool already built into your shell. Experiment with /dev/tcp, learn the raw protocols, and you’ll find yourself becoming a more versatile and capable engineer.
