skip to content

TLS, Proxies and Trust

REST Assured's HTTPS switches: relaxed validation that turns certificate and hostname checking off, key and trust stores, a client certificate, and routing a call through a proxy.

part ofREST Assuredoverview, primer and where to startread it →
on this pageshow

questions

4

In REST Assured, how do you send a request through a proxy that requires a username and password?

level: middleimportance: should knowfreq 44%

answer

  1. only one overload carries credentials
  2. ProxySpecification statics host, port, auth
  3. withAuth after withScheme, not before
  4. host defaults to port 8888

basics

~20 s

REST Assured's short proxy overloads take no credentials. Build a ProxySpecification instead, chaining host, withPort, withScheme and finally withAuth, and hand it to given().proxy(...). Order matters: withScheme rebuilds the specification and silently drops any username and password already set.

solid answer

~50 s

Five of REST Assured's six `proxy(...)` overloads only fill in a host, port and scheme; the credentials-carrying one is `proxy(ProxySpecification)`. Build it with the statics on `io.restassured.specification.ProxySpecification` — `host("proxy.bakery.test")` defaults to port `8888` and scheme `http`, `port(3128)` defaults the host to `127.0.0.1`, and `auth(user, password)` gives you both defaults plus credentials — then refine with `withHost`, `withPort`, `withScheme` and `withAuth`. The trap is that `withScheme` delegates to the three-argument constructor, which resets username and password to null, so call it **before** `withAuth` or lose them silently. REST Assured turns the credentials into Apache `UsernamePasswordCredentials` under an `AuthScope` keyed by the proxy host's resolved IP address and port. The `proxy(URI)` overload reads only host, port and scheme, so user-info in the URI is discarded without a warning. Set the specification per request rather than on the static, so one suite can reach one target through a proxy and another directly.

code

java · 15 lines
java
import static io.restassured.RestAssured.given;
import static io.restassured.specification.ProxySpecification.host;
import static org.hamcrest.Matchers.equalTo;

given()
    .proxy(host("proxy.bakery.test")
        .withPort(3128)
        .withScheme("http")
        .withAuth("ci-runner", "s3cret"))
    .baseUri("https://orders.bakery.example")
.when()
    .get("/preorders/8412")
.then()
    .statusCode(200)
    .body("status", equalTo("READY"));

go deeper

for a junior

Be able to route a call through a local capture proxy with given().proxy(8888) and to say that the shorter overloads accept only host, port and scheme.

for a middle

Explain that ProxySpecification is the only credential-carrying route, name its statics and their defaults, and describe the withScheme rebuild that drops the username and password.

for a senior

Diagnose a 407 that appears only in CI: check the call order on the specification, whether the proxy was set statically or per request, and whether the JRE proxy selector is supplying a host nobody configured.

for a principal

Decide where proxy configuration lives across a suite — per specification versus a shared static — and how proxy credentials are supplied and rotated without landing in source control or a request log.

## The six proxy overloads, and what each one can express `RequestSpecification` and the `RestAssured` statics both declare **six** `proxy(...)` overloads: `(int port)`, `(String host)`, `(String host, int port)`, `(String host, int port, String scheme)`, `(URI uri)` and `(ProxySpecification spec)`. Five of them are conveniences that fill in defaults; only the last can carry a username and password. | Overload | Host | Port | Scheme | Credentials | |---|---|---|---|---| | `proxy(8888)` | `127.0.0.1` | as given | `http` | none | | `proxy("proxy.bakery.test")` | as given | `8888` | `http` | none | | `proxy(host, port)` | as given | as given | `http` | none | | `proxy(host, port, scheme)` | as given | as given | as given | none | | `proxy(URI)` | from the URI | from the URI | from the URI | none | | `proxy(ProxySpecification)` | from the spec | from the spec | from the spec | **yes** | The `URI` overload is the one people expect to work for credentials, and it does not: REST Assured reads only `getHost()`, `getPort()` and `getScheme()` off the URI, so any user-info part is dropped without complaint. ## Building the specification `io.restassured.specification.ProxySpecification` is immutable, with three static factories and four `with...` methods that each return a new instance: - `ProxySpecification.host(String)` — the usual entry point, defaulting to port `8888`, scheme `http`. - `ProxySpecification.port(int)` — defaults the host to `127.0.0.1`. - `ProxySpecification.auth(String username, String password)` — defaults to `127.0.0.1:8888` over `http` with credentials attached. - `withHost`, `withPort`, `withScheme`, `withAuth` — each copies the specification with one field changed, plus `and()` as syntactic sugar. - A port below `1` is defaulted from the scheme, `80` for `http` and `443` for `https`, and any other scheme with no usable port throws `IllegalArgumentException` from the constructor. - Host and scheme are both trimmed to null and then asserted non-null, so a blank host fails immediately rather than producing a silently unroutable specification. A typical authenticated call therefore reads `given().proxy(host("proxy.bakery.test").withPort(3128).withAuth("ci-runner", "s3cret"))`, with `host` statically imported from `ProxySpecification`. ## The ordering trap in withScheme `withHost`, `withPort` and `withAuth` all copy every field including the credentials. `withScheme` does not: it delegates to the **public three-argument constructor** `ProxySpecification(host, port, scheme)`, which fills username and password with its own nulls. So this chain arrives at the wire with no credentials at all: ```java host("proxy.bakery.test").withAuth("ci-runner", "s3cret").withScheme("https") // credentials lost ``` while the same calls in the other order keep them: ```java host("proxy.bakery.test").withScheme("https").withAuth("ci-runner", "s3cret") // credentials kept ``` Nothing throws. The failure surfaces later as a `407` from the proxy on a request to `/preorders/8412` that you were sure was authenticated. Two details make it worth remembering: the port survives the rebuild, so a specification started with `host(...)` is still on `8888` rather than dropping to `443`; and the constructor only defaults a port from the scheme when the port it was handed is below `1`. ## What the credentials actually become When the specification has a username or a password, REST Assured builds an Apache `BasicCredentialsProvider`, registers `UsernamePasswordCredentials` under an `AuthScope`, and sets that provider on the HTTP client. The `AuthScope` is keyed by the proxy host's **resolved IP address** and port, not by the hostname you typed — the internal proxy selector works in addresses, so the library converts before registering. That is why a proxy host that resolves to more than one address, or that fails to resolve at all, produces a confusing failure rather than a clean authentication error. The scheme on the specification is applied to the proxy `HttpHost` itself, so `withScheme("https")` means the hop **to the proxy** is encrypted; it says nothing about the scheme of the target you are calling. ## What happens when you set no proxy Leaving the proxy unset is not the same as disabling proxying. REST Assured always installs its own route planner, and when no `ProxySpecification` is present that planner delegates to the JRE's default `ProxySelector`. Ordinary JVM proxy system properties therefore still apply, which explains the occasional report that a suite routes through a corporate proxy nobody configured in code. Three practical habits follow: 1. Set the proxy on a `RequestSpecification` or `RequestSpecBuilder` rather than on the static, so a single suite can call one target through a proxy and another directly. 2. Call `withScheme` before `withAuth`, or skip `withScheme` entirely when the default `http` hop is what you want. 3. Keep the proxy password out of the source file and out of the request log — `LogConfig`'s default sensitive-header blacklist covers `Proxy-Authorization` in the printed request.

  • What does given().proxy(3128) resolve to, and when is that form useful?
    It becomes `ProxySpecification.port(3128)`, which defaults the host to `127.0.0.1` and the scheme to `http`. It is the shorthand for a capture proxy running on the same machine as the suite — handy while debugging a request locally, and useless in CI where the proxy is on another host.
  • Your suite sets no proxy at all, yet requests still traverse one. Why?
    REST Assured always installs its own route planner and, with no `ProxySpecification` present, that planner delegates to the JRE's default `ProxySelector`. Standard JVM proxy system properties therefore still take effect. Setting an explicit `ProxySpecification` overrides the JRE selection for that request.

saying these in an interview costs you the question

  • Puts credentials in the proxy URI and expects them to be sent
  • Thinks proxy(host, port) accepts a username and password
  • Calls withScheme after withAuth and loses the credentials
  • Assumes withScheme sets the scheme of the target, not the proxy hop
  • Believes no proxy call means no proxy is ever used
open as a page

In REST Assured, what does given().relaxedHTTPSValidation() actually switch off?

level: middleimportance: should knowfreq 52%

basics

~20 s

It replaces REST Assured's SSL socket factory with one built from a trust-all X509TrustManager and Apache HttpClient's allow-all hostname verifier, so both certificate-chain validation and hostname checking stop. The SSLContext protocol defaults to SSL. Nothing else on the request changes.

open as a page

Your REST Assured suite pins a CA with RestAssured.trustStore(...) but accepts a certificate issued for another host. Why?

level: seniorimportance: should knowfreq 34%

basics

~20 s

The path-and-password trustStore and keyStore shortcuts finish by applying allowAllHostnames() for backward compatibility, replacing the strict verifier. Your chain is checked against the pinned authority, but the certificate's subject is never compared with the host you called.

open as a page

In a REST Assured suite, when is relaxedHTTPSValidation() an acceptable choice, and what replaces it?

level: principalimportance: should knowfreq 30%

basics

~20 s

Only where no authority exists to trust: an ephemeral container's self-signed certificate, a local capture proxy, a sandbox you cannot influence. Everywhere else, a trust store with strictHostnames keeps both checks alive and trusts exactly your own issuer.

open as a page