Introduction

When using Cloudflare to proxy requests to your Javalin application, it’s important to configure Javalin to correctly handle Cloudflare’s headers, and to restrict your environment so that only Cloudflare can reach your application.

Configuring Javalin to resolve real IP addresses

You can use the contextResolver.ip configuration option to provide a function that extracts the real client IP from Cloudflare’s CF-Connecting-IP header:

  • Java
  • Kotlin
Javalin.create(config -> {
    config.contextResolver.ip = ctx -> {
        String cfHeader = ctx.header("CF-Connecting-IP");
        return cfHeader != null && !cfHeader.isBlank() ? cfHeader : ctx.req().getRemoteAddr();
    };
}).start();
Javalin.create { config ->
    config.contextResolver.ip = { ctx ->
        ctx.header("CF-Connecting-IP")?.takeIf { it.isNotBlank() } ?: ctx.req().remoteAddr
    }
}.start()

After this, calling ctx.ip() will return the actual client IP address.

Only allowing traffic from Cloudflare

It’s important to only allow traffic to your application from Cloudflare; otherwise, the CF-Connecting-IP header could be forged.

There are a few ways to do this:

Using UFW firewall rules

You can find instructions and a script to automatically configure Cloudflare IP addresses with UFW in this GitHub repository.

Docker and Cloudflare Tunnels

You can bind Javalin’s port to a local interface within Docker, then use a Cloudflare Tunnel to proxy requests to that port so that only Cloudflare can reach it.

You can also configure Javalin to only allow traffic from Cloudflare IPs by checking the remote address before resolving the header; however, this would still leave your application exposed to DDoS attacks since the remote address check happens at the application level rather than at the network level.