Can I get the IP address of my users in a SvelteKit project?
Asked Answered
S

5

7

I'm interested in capturing the Internet Protocol (IP) address of my users at their initial submit to my website. I was under the impression that a SvelteKit submit would be similar to other node.js server solution.

I've been all over this posting, and I'm just not seeing anything remotely like req.headers['x-forwarded-for'] or req.socket.remoteAddress in my request submit to API server in SvelteKit.

Here's what I do see in the request (via a console log in the API submit):

arg1:
    body: (... data ... )
    Headers:
        accept:'*/*'
        accept-encoding:'gzip, deflate'
        accept-language:'en-US,en;q=0.9'
        connection:'keep-alive'
        content-length:'141'
        content-type:'text/plain;charset=UTF-8'
        cookie:'userid=c03efd10-3085-46cb-bacd-9eaeb605426e'
        host:'192.168.1.22:3000'
        origin:'http://192.168.1.22:3000'
        referer:'http://192.168.1.22:3000/learn'
        user-agent:'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
    locals:
        userid: '....'
    method: 'POST'
    params: {}
    path: '/api/user_form'
    query: ...
    rawBody: ... 

Definitely no socket, no header for ['x-forwarded-for']

Note, I'm working this on local network. Host on one machine, client on another machine in the same network. In fact, host = 192.168.1.22:3000, client = 192.168.1.4

Note2, I'm running the program via svelte-kit dev --host 0.0.0.0

What am I not seeing? Is there a way to obtain a client user's IP address from a websubmit using Sveltekit?

Siouxie answered 28/12, 2021 at 20:34 Comment(1)
Perhaps a better way to access a more 'pure' request object would be to use middleware. In production this would depend on your adapter, but in dev mode see here.Neutrality
C
6

Just to let ppl know... that's now possible, using event.clientAddress 🎉

Citrine answered 1/4, 2022 at 9:45 Comment(1)
@OliverDixon you need to use ADDRESS_HEADER and XFF_DEPTH probably (depending on your adapter), see kit.svelte.dev/docs/…Holpen
B
4

Everything below is for production / build code.

> npm run build
> node build

Running your app in dev mode is different due to VITE.


The Svelte Adapter receives a Node Request which also includes the remote IP. The IP could be extracted with the methods mentioned here.

The Adapter however only forwards the values required for the Svelte IncomingRequest interface to the Svelte Render. This interface is then further extended by Svelte to a Request interface and includes the following values:

export interface Request<Locals = Record<string, any>, Body = unknown> {
    url: URL;
    method: string;
    headers: RequestHeaders;
    rawBody: RawBody;
    params: Record<string, string>;
    body: ParameterizedBody<Body>;
    locals: Locals;
}

Therefore, I see four possible ways to tackle this issue:

  1. Create an issue to update the Svelte IncomingRequest interface to include the remote IP. (This would also require an update in all Adapters)
  2. Create your own Middleware as an entry point for your application. The downside is, that you still wont get the remote IP anywhere in your Svelte handle or routes. You could however log the IP or block certain paths.
  3. Run your app behind a proxy, the proxy would populate ['x-forwarded-for'] in the request header and as shown in the interface above, the header is available in your Svelte Request object.
  4. Modify the Svelte IncomingRequest interface and Adapter Middleware on your own. (This is dirty and a mess to maintain)

Since 2. is the most applicable way, here is an example on how to set it up with polka - you could of course also use express or any other HTTP server.

/src/server.js file which is our Middleware in this case:

import polka from 'polka'
import { assetsMiddleware, prerenderedMiddleware, kitMiddleware } from '../build/middlewares.js'

const { PORT, DOMAIN } = process.env

polka()
  .use((req, res, next) => {
      console.log(req.socket.remoteAddress);
    next()
  })

  // Load the SvelteKit build
  .use(assetsMiddleware, prerenderedMiddleware, kitMiddleware)

  .listen(PORT)

/svelte.config.js to configure our Middleware as the entryPoint - minimal example using the default adapter-node:

import preprocess from 'svelte-preprocess';
import adapter from '@sveltejs/adapter-node';

/** @type {import('@sveltejs/kit').Config} */
const config = {
    preprocess: preprocess(),

    kit: {
        adapter: adapter({ entryPoint: './src/server.js' }),
        target: '#svelte'
    }
};

export default config;
Bullyboy answered 30/12, 2021 at 12:4 Comment(2)
Thx for your reply. Its going to take me a few days to digest and test what you have written here. I didn’t understand the whole VITE and dev thing. I have been looking at Nax-ipware library.Siouxie
@Siouxie an implementation of Nax-ipware should be doable in your own middleware as shown in 2.) above. However, i explained how you can add middleware in front of the default adapter-node which only runs in prod/build. If you want to add middleware in dev mode, you should look into VITE PluginsBullyboy
R
2

Here is an updated example for the current version of SvelteKit.

When running in production, you should always run your server adapter, node.js adapter or similar, behind a reverse proxy. The reverse proxy usually terminates HTTPS traffic and caches the static assets. Most reverse proxies (not Cloudflare), needs to be configured to handle the original client IP address forwarding securely. You cannot read client IP address securely on the client side.

Popular reverse proxies include Cloudflare, or self-hosted Caddy or Nginx.

hooks.server.js

/**
 * Hook to record the raw IP address of the node.js server connection.
 * 
 * See ip.js how to read the true IP.
 */

// Expose this global so we can read inside load(), etc.
export let requestIp;

export const handle = async ({ event, resolve }) => {
  // IP from the connection proxy or client if running local dev
  requestIp = event.getClientAddress(); 
  console.log('IP Address from Client Request: ', requestIp);
  return await resolve(event);
};


export default handle;

Then lib/ip.js with the exported function:

import {requestIp} from "../hooks.server";

/**
 * Read the client IP address in reverse proxy compliant manner.
 * 
 * Only applicable to Node.js adapter, others, server-side rendering.
 * 
 * @param {*} request Request object from page load.
 */
export function getClientTrueIp(request) {
    // Read about spoofing https://kit.svelte.dev/docs/adapter-node#environment-variables-addressheader-and-xffdepth
    // Prefer CloudFlare's unspoofable header
    return request.headers.get("CF-Connecting-IP") || request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || requestIp;
}

Then in +page.server.js:

import { getClientTrueIp} from "$lib/ip.js";

export async function load({ params, cookies, parent, request }) {
    
    await parent();
    let ip = getClientTrueIp(request);

    console.log("ip is", ip);
}

Also you cannot read client IP address securely on the client side.

Rayford answered 1/4, 2024 at 10:31 Comment(0)
C
1

There's a ticket in Sveltekit that might address this feature, hopefully soon https://github.com/sveltejs/kit/pull/3993

Citrine answered 22/2, 2022 at 11:38 Comment(0)
R
0

Here are the steps I took.

  1. npm install @auth/sveltekit
    
  2. In src folder, Create a hooks.server.ts file as below:

    import { SvelteKitAuth } from "@auth/sveltekit";
    
    export let requestIp: string;
    
    export const handle = async ({ event, resolve }) => {
      requestIp = event.getClientAddress(); // IP from Client Request
      console.log('IP Address from Client Request: ', requestIp);
    
      // Return the requestIp along with the authentication result
      const authResult: any = await SvelteKitAuth({
        resolve,
      })({ event, resolve });
    
      return authResult;
    };
    
    export default handle;
    
  3. import requestIp into any server-side file such as +page.server.ts

    import { requestIp } from "../hooks.server";
    
Runkle answered 20/9, 2023 at 9:20 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.