URLConnection
URLConnection is the abstract bridge between a URL and the actual bytes behind it. While URL.openStream() gives you a raw input stream, URLConnection lets you control request headers, set timeouts, inspect response metadata, and even write data — all before a single byte crosses the network.
Getting a URLConnection
You never construct a URLConnection directly. Instead, you call openConnection() on a URL object. The runtime returns a protocol-specific subclass — usually HttpURLConnection for http/https.
import java.net.URL;
import java.net.URLConnection;
public class OpenConnection {
public static void main(String[] args) throws Exception {
URL url = new URL("https://example.com");
URLConnection connection = url.openConnection();
// Must call connect() before reading metadata
connection.connect();
System.out.println("Content-Type : " + connection.getContentType());
System.out.println("Content-Length : " + connection.getContentLength());
System.out.println("Last-Modified : " + connection.getLastModified());
}
}
Output:
Content-Type : text/html; charset=UTF-8
Content-Length : -1
Last-Modified : 0
Note:
connect()opens the actual socket. ManygetXxx()methods call it implicitly, but it is best practice to call it explicitly so exceptions happen at a predictable place.
Setting Request Headers
Use setRequestProperty(key, value) to add HTTP request headers. You must set headers before calling connect() or reading any data.
import java.net.URL;
import java.net.URLConnection;
public class RequestHeaders {
public static void main(String[] args) throws Exception {
URL url = new URL("https://httpbin.org/get");
URLConnection connection = url.openConnection();
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("User-Agent", "MyJavaApp/1.0");
connection.setRequestProperty("Authorization", "Bearer my-token");
connection.connect();
System.out.println("Connected with custom headers.");
}
}
To append a value to an existing header (useful for Cookie, for example), use addRequestProperty(key, value) instead — it adds rather than replaces.
connection.addRequestProperty("Cookie", "session=abc123");
connection.addRequestProperty("Cookie", "theme=dark"); // both cookies sent
Reading Response Headers
After connecting, you can inspect every response header the server returned.
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
public class ResponseHeaders {
public static void main(String[] args) throws Exception {
URLConnection connection = new URL("https://example.com").openConnection();
connection.connect();
Map<String, List<String>> headers = connection.getHeaderFields();
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
System.out.println(entry.getKey() + " => " + entry.getValue());
}
}
}
You can also fetch a single header by name with getHeaderField(String name) or by index with getHeaderField(int n).
String contentType = connection.getHeaderField("Content-Type");
String statusLine = connection.getHeaderField(0); // e.g. "HTTP/1.1 200 OK"
Tip: The header at index
0is the HTTP status line (nullkey in the map). It is a handy way to check the status without casting toHttpURLConnection.
Configuring Timeouts
By default, a URLConnection will wait forever for a connection or a response. Always set both timeouts in production code.
import java.net.URL;
import java.net.URLConnection;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class TimeoutDemo {
public static void main(String[] args) throws Exception {
URLConnection connection = new URL("https://example.com").openConnection();
connection.setConnectTimeout(5_000); // 5 s to establish the TCP connection
connection.setReadTimeout(10_000); // 10 s to wait for data after connecting
connection.connect();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()))) {
System.out.println(reader.readLine());
}
}
}
Warning: If either timeout expires, Java throws a
java.net.SocketTimeoutException. Wrap your network code in a try-catch and handle this gracefully rather than letting your application hang.
Reading the Response Body
Once connected, getInputStream() hands you the response body as a plain InputStream. Wrap it in a BufferedReader for text, or read raw bytes for binary content.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
public class ReadBody {
public static void main(String[] args) throws Exception {
URLConnection connection = new URL("https://example.com").openConnection();
connection.setConnectTimeout(5_000);
connection.setReadTimeout(10_000);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
}
}
Sending Data (Output Mode)
To POST data, enable output mode with setDoOutput(true) before connecting, then write to getOutputStream().
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
public class PostData {
public static void main(String[] args) throws Exception {
URLConnection connection = new URL("https://httpbin.org/post").openConnection();
String body = "name=Java&version=21";
connection.setDoOutput(true); // enables POST mode
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length",
String.valueOf(body.length()));
try (OutputStream out = connection.getOutputStream()) {
out.write(body.getBytes(StandardCharsets.UTF_8));
}
// Now read the response
try (var reader = new java.io.BufferedReader(
new java.io.InputStreamReader(connection.getInputStream()))) {
reader.lines().forEach(System.out::println);
}
}
}
Tip: For anything more complex than a simple POST — custom HTTP methods (PUT, DELETE, PATCH), redirect handling, or response codes — cast the
URLConnectionto HttpURLConnection, which exposessetRequestMethod()andgetResponseCode().
Caching and the useCaches Flag
URLConnection respects HTTP caching by default. If the JVM has a ResponseCache installed, responses may be served from cache without a real network request.
connection.setUseCaches(false); // always fetch fresh data
You can also control caching globally with URLConnection.setDefaultUseCaches(false).
Common Convenience Methods
| Method | Purpose |
|---|---|
getContentType() | MIME type of the response (e.g., text/html) |
getContentLength() | Body size in bytes (-1 if unknown) |
getContentLengthLong() | long version for files > 2 GB |
getLastModified() | Last-modified timestamp as epoch millis |
getExpiration() | Cache expiry as epoch millis |
getDate() | Server’s Date header as epoch millis |
getInputStream() | Response body |
getOutputStream() | Request body (requires setDoOutput(true)) |
setConnectTimeout(int) | TCP connect timeout in milliseconds |
setReadTimeout(int) | Data read timeout in milliseconds |
setRequestProperty(k,v) | Set a request header |
addRequestProperty(k,v) | Append to a request header |
getHeaderField(String) | Get a response header by name |
getHeaderFields() | All response headers as a Map |
Under the Hood
When you call url.openConnection(), the URL’s URLStreamHandler creates a concrete subclass of URLConnection. For http and https, that subclass is sun.net.www.protocol.http.HttpURLConnection (which itself extends java.net.HttpURLConnection).
The connection lifecycle works in two phases:
- Pre-connect phase — set headers, timeouts, and flags. The socket does not exist yet. This is cheap and never touches the network.
- Connected phase — once
connect()is called (or any I/O method triggers it implicitly), the JVM establishes a TCP socket, performs the TLS handshake for HTTPS, sends the HTTP request line and headers, and waits for the server’s status line and response headers. Only after this can you read response metadata.
Under the covers, HttpURLConnection reuses a pool of persistent HTTP/1.1 connections managed by sun.net.www.http.HttpClient. This is why you should always close the input stream — not just stop reading — so the underlying socket is returned to the pool rather than closed and discarded.
For high-throughput or modern applications, consider the java.net.http.HttpClient API added in Java 11. It supports HTTP/2, asynchronous requests with CompletableFuture, and WebSocket, and is now the preferred networking API for new code.
URLConnection vs HttpURLConnection
| Feature | URLConnection | HttpURLConnection |
|---|---|---|
| Protocol | Any (http, ftp, file, jar…) | HTTP/HTTPS only |
| Response code | Via getHeaderField(0) | getResponseCode() |
| HTTP method | setDoOutput() for POST only | setRequestMethod("GET/POST/PUT…") |
| Redirects | Not handled | Configurable with setFollowRedirects() |
| Error body | getInputStream() | getErrorStream() on 4xx/5xx |
Use URLConnection when you need protocol-agnostic code (e.g., reading a jar: or file: URL). For any real HTTP work, cast to or use HttpURLConnection directly.
Related Topics
- URL Class — parse and create URL objects before opening a connection
- HttpURLConnection — full HTTP control with response codes, custom methods, and error handling
- BufferedReader — wrap the response
InputStreamfor efficient line-by-line reading - Socket Programming — go lower-level with raw TCP sockets when you need more control
- InetAddress — resolve hostnames to IP addresses used under the hood
- Networking Basics — overview of Java’s networking package and concepts