Implementing a Reverse Proxy with Vert.x

1. Introduction
After briefly using Pingora, I thought since Rust and Go can both write proxy-related code, why not use Java to write one?
After searching, I found that implementing a reverse proxy with Vert.x is so simple. Let's get started quickly.
If you want to download the sample code, you can find it here: demo-vertx-reverse-proxy
2. Create a Vert.x Project
Vert.x project creation can be found on the official website: Create a new Vert.x application
The dependency to select is Vert.x Http Proxy

3. Follow the Vert.x Tutorial
Official tutorial: Vert.x Http Proxy
3.1 Vert.x Http Proxy
Vert.x Http Proxy is a reverse proxy based on Vert.x, designed to implement reusable reverse proxy logic to focus on higher-level concerns.
This module has technical preview status, meaning the API may change between versions.
3.2 Using Vert.x Http Proxy
To use Vert.x Http Proxy, add to your pom.xml or other dependency configuration:
- Maven
- Gradle
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-http-proxy</artifactId>
<version>4.1.8</version>
</dependency>
dependencies {
compile 'io.vertx:vertx-http-proxy:4.1.8'
}
3.3 Basic Http Proxy
To complete a reverse proxy using Vert.x Http Proxy, you need the following:
- A proxy server using an HttpProxy instance to handle outbound requests and forward them to the origin server.
- An origin server to handle requests from the proxy server and process responses accordingly.
Now that you have the overall concept, let's dive into the implementation, starting with the origin server, then the proxy server with HttpProxy:
3.4 Origin Service
This can be skipped because we can directly use any website URL you can open as the origin service
Of course, I've kept the complete steps.
You just need to create an origin server listening on port 7070:
@Override
public void start(Promise<Void> startPromise) throws Exception {
HttpServer originServer = vertx.createHttpServer();
originServer.requestHandler(req -> {
req.response()
.putHeader("content-type", "text/html")
.end("<html><body><h1>I'm the target resource!</h1></body></html>");
}).listen(7070);
}
3.5 Proxy Server Using HttpProxy
Create a proxy server listening on port 8080 using an HttpProxy instance that handles the reverse proxy logic accordingly.
@Override
public void start(Promise<Void> startPromise) throws Exception {
HttpClient proxyClient = vertx.createHttpClient();
HttpProxy proxy = HttpProxy.reverseProxy(proxyClient);
proxy.origin(7070, "origin");
HttpServer proxyServer = vertx.createHttpServer();
proxyServer.requestHandler(proxy).listen(8080);
}
Finally, all outgoing requests will be conveniently forwarded to the origin server as a reverse proxy.
The official tutorial ends here, only proxying a service at a specific IP + port. But what if you want to proxy a website?
4. Proxying a Website
To proxy a website, we actually need to proxy that website's URL + port 443, and trust the website's certificate.
So our verticle implementation needs to be modified. Here I directly use my own blog URL - you can change it to any address:
@Override
public void start(Promise<Void> startPromise) throws Exception {
HttpClient proxyClient = vertx.createHttpClient(new HttpClientOptions().setSsl(true).setTrustAll(true));
// Create an HttpProxy instance
HttpProxy proxy = HttpProxy.reverseProxy(proxyClient);
// Configure target server and port
proxy.origin(443, "runnable.run");
// Set HttpServer to listen for requests
HttpServer proxyServer = vertx.createHttpServer();
proxyServer.requestHandler(req -> {
// Add Host header to the request here
req.headers().set("Host", "runnable.run");
// Forward request to target server via HttpClient
proxy.handle(req);
}).listen(8080, res -> {
if (res.succeeded()) {
System.out.println("Proxy server is running on port 8080, http://localhost:8080");
startPromise.complete();
} else {
startPromise.fail(res.cause());
}
});
}
Then add a main method to start the service:
public static void main(String[] args) {
VertxOptions vertxOptions = new VertxOptions();
Vertx vertx = Vertx.vertx(vertxOptions);
DeploymentOptions deploymentOptions = new DeploymentOptions();
vertx.deployVerticle(new MainVerticle(), deploymentOptions);
}
4.1 Complete Code
package run.runnable.demo_vertx_reverse_proxy;
import io.vertx.core.*;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.HttpServer;
import io.vertx.httpproxy.HttpProxy;
public class MainVerticle extends AbstractVerticle {
@Override
public void start(Promise<Void> startPromise) throws Exception {
HttpClient proxyClient = vertx.createHttpClient(new HttpClientOptions().setSsl(true).setTrustAll(true));
// Create an HttpProxy instance
HttpProxy proxy = HttpProxy.reverseProxy(proxyClient);
// Configure target server and port
proxy.origin(443, "runnable.run");
// Set HttpServer to listen for requests
HttpServer proxyServer = vertx.createHttpServer();
proxyServer.requestHandler(req -> {
// Add Host header to the request here
req.headers().set("Host", "runnable.run");
// Forward request to target server via HttpClient
proxy.handle(req);
}).listen(8080, res -> {
if (res.succeeded()) {
System.out.println("Proxy server is running on port 8080, http://localhost:8080");
startPromise.complete();
} else {
startPromise.fail(res.cause());
}
});
}
public static void main(String[] args) {
VertxOptions vertxOptions = new VertxOptions();
Vertx vertx = Vertx.vertx(vertxOptions);
DeploymentOptions deploymentOptions = new DeploymentOptions();
vertx.deployVerticle(new MainVerticle(), deploymentOptions);
}
}
After starting, open http://localhost:8080 in your browser and you'll find it's already showing the blog page. So if you deploy this reverse proxy on a Hong Kong server and replace the URL with Google, then...
