RecipesGetting the Client's IP

Reading the client IP address

Getting the client IP address in Kaito differs depending on what server runtime you’re using and also if you are using a reverse proxy or not.

For example, if you are using a reverse proxy like Cloudflare, you’ll need to use the cf-connecting-ip header to get the client IP address.

Cloudflare proxy example

export const myRouter = router().get('/ip', async ({ctx}) => {
	const ip = ctx.req.headers.get('cf-connecting-ip'); // you must pass `.req` in your context object
 
	if (!ip) {
		throw new KaitoError(400, 'Probably not behind Cloudflare!');
	}
 
	return ip;
});

Other reverse proxies will have different headers, nginx for example uses x-forwarded-for. Consult the docs of your reverse proxy to see how to get the client IP address.

In most cases, apps ARE behind reverse proxies, and it’s rare that you’ll want the IP address of the connection itself. If you actually do need it, you’ll have to consult the docs of your server runtime. Below are a couple snippets that may be helpful, though.

Bun

With Bun.serve(), you can access the client IP address with the server’s .remoteIP method. We recommend storing this in AsyncLocalStorage so you can access it in your route handlers.

Bear in mind that you can move the ip property to your context object to make it cleaner to access. Below is just a lightweight example.

import {AsyncLocalStorage} from 'node:async_hooks';
 
const ipStore = new AsyncLocalStorage<string>();
 
const app = router().get('/ip', async ({ctx}) => {
	const ip = ipStore.getStore()!;
	return ip;
});
 
const handle = createKaitoHandler({
	router: app,
	// ...
});
 
const server = Bun.serve({
	port: 3000,
	fetch: async (request, server) => {
		const ip = server.remoteIP(request);
		return ipStore.run(ip, () => handle(request));
	},
});

Node.js

Since you’ll be using @kaito-http/uws with Node.js, the client IP address is available through the context parameter passed to your fetch handler. If you need to access it in nested functions or route handlers, you can use AsyncLocalStorage to make it available throughout the request lifecycle.

import {AsyncLocalStorage} from 'node:async_hooks';
import {KaitoServer} from '@kaito-http/uws';
 
const ipStore = new AsyncLocalStorage<string>();
 
const app = router().get('/ip', async ({ctx}) => {
	const ip = ipStore.getStore()!;
	return ip;
});
 
const handle = createKaitoHandler({
	router: app,
	// ...
});
 
const server = await KaitoServer.serve({
	port: 3000,
	fetch: async (request, context) => {
		return ipStore.run(context.remoteAddress, () => handle(request));
	},
});

If you’re not using Bun or Node.js, you should consult the docs of your server runtime to see how to get the client IP address. We’ll happily accept a PR to this docs page to add snippets for other server runtimes.

Kaito v1 & v2

Previous versions of Kaito were built on top of Node.js only, so you could just access the req.socket.remoteAddress property to get the client’s IP address. The Web Fetch API doesn’t have a .socket property on requests, so most server implementations let you access the remote ip address or socket instance (if any) through other means.