Skip to content
Java networking 6 min read

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. Many getXxx() 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 0 is the HTTP status line (null key in the map). It is a handy way to check the status without casting to HttpURLConnection.

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 URLConnection to HttpURLConnection, which exposes setRequestMethod() and getResponseCode().

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

MethodPurpose
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:

  1. Pre-connect phase — set headers, timeouts, and flags. The socket does not exist yet. This is cheap and never touches the network.
  2. 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

FeatureURLConnectionHttpURLConnection
ProtocolAny (http, ftp, file, jar…)HTTP/HTTPS only
Response codeVia getHeaderField(0)getResponseCode()
HTTP methodsetDoOutput() for POST onlysetRequestMethod("GET/POST/PUT…")
RedirectsNot handledConfigurable with setFollowRedirects()
Error bodygetInputStream()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.

  • 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 InputStream for 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
Last updated June 13, 2026
Was this helpful?