I’m experiencing an issue with my setup where the client’s IP address isn’t forwarded correctly during the first SSR load of a page.
Backend: Laravel hosted on a VPS
Frontend: Nuxt 3 hosted on Vercel
Problem:
When a page is loaded for the first time with Server-Side Rendering (SSR), Vercel sends its own IP address instead of the client’s IP. This causes issues with my Laravel API, including:
429 errors due to rate limiting based on Vercel’s IP.
Incorrect geolocation since I use the client’s IP for location-based pricing and user experience.
However, this issue only happens on the first SSR load. For example, when a form request is made later, the correct client IP is sent.
What I’ve Tried:
I use the following plugin to make an initial request and load global values:ç
export default defineNuxtPlugin(async () => {
const { products } = useProducts();
const { limits } = useLimits();
const route = useRoute();
const sanctumClient = useSanctumClient();
const { data, error } = await useAsyncData("globals", () =>
sanctumClient("/globals"),
);
if (error.value) {
if (error.value.statusCode === 503 && route.path !== "/maintenance") {
await navigateTo("/maintenance");
}
if (route.path !== "/maintenance") {
throw createError(error.value);
}
}
if (data.value) {
products.value = data.value.products;
limits.value = data.value.limits;
if (route.path === "/maintenance") {
await navigateTo("/");
}
}
});
This issue also occurs in any other page that makes SSR calls, but only on the first load.
I enabled trustproxies in Laravel and set it to *, but it didn’t fix the problem.
In Nuxt, I tried forwarding the X-Forwarded-For header like this:
interceptors: {
onRequest: (
_app: NuxtApp,
ctx: FetchContext,
_logger: ConsolaInstance,
) => {
const headers = useRequestHeaders();
ctx.options.headers = {
...ctx.options.headers,
"X-Forwarded-For": headers["x-forwarded-for"],
};
},
},
However, this triggers Cloudflare’s security mechanisms, flagging the request as potentially harmful.
Question:
Is there a better way to ensure the real client IP is sent to the backend during the first SSR load? Should I pass the IP in a different header, or is there a configuration I’m missing in Vercel to forward the client’s IP correctly?
Any suggestions are welcome!