vercel / next.js

The React Framework
https://nextjs.org
MIT License
124.3k stars 26.49k forks source link

IP address not obtainable in Next 13 with app dir? #47793

Closed xriter closed 8 months ago

xriter commented 1 year ago

Verify canary release

Provide environment information

Operating System:
      Platform: darwin
      Arch: arm64
      Version: Darwin Kernel Version 22.4.0: Mon Mar  6 21:00:41 PST 2023; root:xnu-8796.101.5~3/RELEASE_ARM64_T8103
    Binaries:
      Node: 16.15.0
      npm: 8.10.0
      Yarn: 1.22.15
      pnpm: 6.11.0
    Relevant packages:
      next: 13.2.4
      eslint-config-next: 13.0.4
      react: 18.2.0
      react-dom: 18.2.0

Describe the Bug

It seems to be undoable to get the users IP address when using Next 13 with the app dir.

How is one supposed to get the ip address on the server side in Next 13?

pdong commented 1 year ago

On NextRequest, geo and ip are empty/undefined unless provided by the hosting platform as documented here:

https://nextjs.org/docs/api-reference/next/server#nextrequest

ip: (string || undefined) - Has the IP address of the Request. This information is provided by your hosting platform.

geo - Has the geographic location from the Request. This information is provided by your hosting platform.

pogran commented 1 year ago

it doesnt work. my server send to headers to request X-Real-IP and X-Forwarded-For but ip underfined...

jmif commented 1 year ago

We're having this issue too. Is there a way to get the request IP when using app router?

JoshAllen95 commented 1 year ago

@jmif Did you find a workaround or a way of doing this?

Bitbbot commented 1 year ago

Still relevant problem

Bitbbot commented 1 year ago

what's the point of having fucking api routes if u can't get an ip?

fywk commented 1 year ago

You can still access the ip address in api routes (and middleware) of the page router, according to the doc. The issue here is that it's not available for the app router.

Bitbbot commented 1 year ago

You can still access the ip address in api routes (and middleware) of the page router, according to the doc. The issue here is that it's not available for the app router.

Cool! Rewriting the whole app on the old version is exactly what I want, ahahha. I'm quite pissed off frankly speaking

fywk commented 1 year ago

You can still access the ip address in api routes (and middleware) of the page router, according to the doc. The issue here is that it's not available for the app router.

Cool! Rewriting the whole app on the old version is exactly what I want, ahahha. I'm quite pissed off frankly speaking

Yes, api routes are the remaining bits that I've yet to move to route handlers in the app router because of this. It would be great if ip and geo properties would get supported soon.

As for workaround, I'm currently using middleware to add geo info to searchParams:

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { app } from "./lib/app-config";

export const config = {
  matcher: ["/"],
};

export function middleware(request: NextRequest) {
  const { nextUrl: url, geo } = request;

  const city = geo?.city ?? app.location.city;
  const country = geo?.country ?? app.location.country;

  url.searchParams.set("city", city);
  url.searchParams.set("country", country);

  return NextResponse.rewrite(url);
}

Then, in my app/page.tsx:


export default function Page({ searchParams }: SearchParams) {
  const city = searchParams.city;
  ...
}
mindaras commented 1 year ago

You can get the ip related headers in your server component by using headers() from "next/headers", convert it to object literal and pass it to your fetch request which is calling your api route. I'd suggest making an api client wrapper to do this automatically.

import { headers } from "next/headers";

...

const response = await fetch(`${baseUrl}/api${path}`, {
  ...options,
  headers: Object.fromEntries(headers())
});
Bitbbot commented 1 year ago

You can get the ip related headers in your server component by using headers() from "next/headers", convert it to object literal and pass it to your fetch request which is calling your api route. I'd suggest making an api client wrapper to do this automatically.

import { headers } from "next/headers";

...

const response = await fetch(`${baseUrl}/api${path}`, {
  ...options,
  headers: Object.fromEntries(headers())
});

Won't I be able to forge headers in a request like this? Pass not real ip for instance

mindaras commented 1 year ago

You can get the ip related headers in your server component by using headers() from "next/headers", convert it to object literal and pass it to your fetch request which is calling your api route. I'd suggest making an api client wrapper to do this automatically.

import { headers } from "next/headers";

...

const response = await fetch(`${baseUrl}/api${path}`, {
  ...options,
  headers: Object.fromEntries(headers())
});

Won't I be able to forge headers in a request like this? Pass not real ip for instance

This approach only applies to server components since headers util is only accessible from the server, so client wouldn't be able to tamper with it. When the request is coming from the client-side, the headers are already present. The issue is that when you call your own API on the server-side, the headers aren't being forwarded automatically.

raflymln commented 1 year ago

In some cases, "X-Real-IP" and "X-Forwarded-For" returns undefined, i think NextJS team should implement this on NextRequest, i don't understand what's their intention or plan on NextRequest.ip variable if we can't get the IP, kinda stupid enough

Bitbbot commented 1 year ago

"X-Real-IP" and "X-Forwarded-For"

If I'm not mistaken, there has to be some configuration on the server e.g. nginx to get an ip

raflymln commented 1 year ago

"X-Real-IP" and "X-Forwarded-For"

If I'm not mistaken, there has to be some configuration on the server e.g. nginx to get an ip

Yes, if you were using nginx, if not?

Cyral commented 1 year ago

Testing out the app router for the first time today and ran into this issue as I need to make external requests in my components during SSR. I have a backend written in another language and previously used getServerSideProps to call fetch, passing the user's IP, cookies, etc. so that the backend would see the request as coming from the end user and not localhost (nextjs).

The whole caching and data fetching story with the app router is a bit complicated, but I think I figured out how to do this. Using "next/headers" as mentioned above we can get the headers (similar to appContext with the pages router) and pass them to fetch, the problem occurs when this is used in a client component. (The file that imports headers to create the fetch request, among other custom fetch additions, is used for both client and server requests) You will get an error that you cannot use headers in client components. To work around this, we need to dynamically import the headers module.

Change import {headers} from 'next/headers' to const headers = import('next/headers'); and conditionally call it if the window is undefined:

let ssrHeaders = {};
if (typeof window === 'undefined') {
  ssrHeaders = Object.fromEntries((await headers).headers());
}

Then pass these headers to fetch to forward them to your backend. This is all behind nginx so x-forwarded-for is trusted.

Arunjenson-ss commented 11 months ago

is it fixed ?

hongz1 commented 11 months ago

No solution so far??

reginpv commented 11 months ago

waiting for this

OlegLustenko commented 11 months ago

It seems this is related PR

https://github.com/vercel/next.js/pull/54813

hongz1 commented 11 months ago

@OlegLustenko I tried 'v13.4.20-canary.31' and request.ip is still undefined and request.geo is empty object. I was able to get client ip from express with react via axios. My question is that there is no way to get client Ip in middleware or nextjs API route handler using fetch for app dir unless I have to use 3rd-party like ipify ?

larevuegeek commented 11 months ago

Hello, i have the same problem also. Inside the request, the IP value is undefined. I just start a new app, so for investigate I have test to create a fresh App With the old Pages Router, and i can get the IP now via getServerSideProps. I don't know if this information can help.

hongz1 commented 11 months ago

Hello, I tried this workaround and it's working for me. Inside the middleware, I use fetch to an external API (or Pages API) that will return the IP/geo.


  export async function middleware(req: NextRequest) {

        const res = NextResponse.next();

        let geoIp = await fetch("https://geolocation-db.com/json/").then((response) =>

          response.json(),

        );

        if (geoIp) {

          res.headers.set("x-forwarded-for", geoIp.IPv4);

          res.headers.set("x-real-ip", geoIp.IPv4);

          let geo = geoIp;

          delete geo.IPv4;

          res.headers.set("geo", JSON.stringify(geo));

        };

        return res;

}

That is not what we are discussing here. Yes you can get ip from 3rd party. When client side sends request, server side doesn't get ip.

Try create react and express and send request to express using axios. You gonna get ip even in local host. In nextjs, there is no way to get ip now unless your web server like nginx has configured or send another request to 3rd party to get ip.

vnevermore commented 11 months ago

The wierd thing is that the past few days i was using this aproach on a page.tsx

import { headers } from "next/headers";

const headersList = headers();
onst ip = headersList.get("x-forwarded-for");

and it was working just fine until today where "x-forwarded-for" is not included anymore on the headers array i've updated to latest from "13.4.12" and i am talking about running on localhost dev

gtokman commented 11 months ago

Any solutions to this yet?

ekatzenstein commented 10 months ago

My dudes, this needs to be fixed. It's a fundamental issue.

yasincandev commented 10 months ago

any solutions?

sam3d commented 10 months ago

The x-forwarded-for header seems to work in production (if you're querying it from any machine that isn't localhost).

sam3d commented 10 months ago

I think this may have been fixed with https://github.com/vercel/next.js/pull/56797?

Zerebokep commented 9 months ago

I think we still need a way to securely get the ip of the proxy (not the visitors ip) if a reverse proxy is used in front of nextjs, correct me if I'm wrong.

leerob commented 8 months ago

Hey everyone, I can confirm that was fixed with https://github.com/vercel/next.js/pull/56797. Further, heard on the feedback here that this was not clearly documented, especially around the usage of request.ip vs. reading the headers directly.

I've updated the docs based on this issue as well: https://github.com/vercel/next.js/pull/59719

Thank you 🙏

Zerebokep commented 8 months ago

@leerob I haven't found any update regarding request.ip, could you please clarify if it is possible to obtain the ip of the actual request without relying on headers? If this is not possible all non-enterprise vercel app's are basically prone to spoofing.

It seems the edge helper function also gets the ip from the headers:

https://vercel.com/docs/functions/edge-middleware/middleware-api#ipaddress

The ipAddress helper returns the IP address of the request from the headers.

Imho this issue shouldn't be closed until there is a solution for this, as it is a security issue.

leerob commented 8 months ago

@Zerebokep this issue is around getting the IP address with the App Router. This is possible and now more clearly documented. request.ip specifically is a helper method on top of reading the headers, so no, it is not possible to get this value on Vercel without using the headers.

You are correct that x-forwarded-for can be spoofed, and there's some good documentation here with more details. If you need Vercel to be a trusted reverse proxy, this does require an Enterprise plan.

github-actions[bot] commented 7 months ago

This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.