Nuxt 3 SSR app with proxy: 'x-forwarded-for' and 'x-real-ip' headers

211 Views Asked by At

I am migrating a Nuxt 2 app to Nuxt 3. This app uses a proxy middelware to proxy all calls to /api to the actual backend. One of those calls is a GET request that returns an auth token when the client IP is whitelisted (it checks the headers 'x-real-ip' and 'x-forwarded-for')

In the Nuxt 2 app, this was implemented using Axios and the library http-proxy-middleware:

const apiProxyMiddleware = createProxyMiddleware(config.public.apiBasePath) {
    target: config.proxyBaseUrl,
    changeOrigin: true, 
    pathRewrite: { [`^${config.public.apiBasePath}`]: '' },
    xfwd: true, // <- this sets the 'x-forwarded-for' header
    onProxyReq(proxyReq, req) {
        const token = getTokenFromSomeWhere();
        proxyReq.setHeader('token', token);
    }
});

In the Nuxt 3 app, this I got rid of the library, and I implemented a catch-all server middleware using H3's proxyRequest (Edit 1: so apparently it's possible to set xfwd in the proxy options, but it does not solve my problem):

export default defineEventHandler((event) => {
    const config = useRuntimeConfig();
    const cookies = parseCookies(event);
    const options = {
        xfwd: true, // <- see edit
    } as ProxyOptions;
    const myToken = cookies?.[COOKIE_NAME.TOKEN];
        options.headers = {
            ...options.headers,
            token: myToken,
        };
    const path = event.path.replace(/^\/api\//, '');
    const target = joinURL(config.backendUrl, path);
    return proxyRequest(event, target, options);
});

Now for some reason, calling the GET endpoint does not give me a token. That means that the client's IP does not reach the server.

  • Where are those headers normally set?
  • Am I supposed to set them myself manually?
  • How does the client IP reach the backend if the GET request is originating from the nitro server?

Any pointers are welcome

Edit2: this is my Axios config:

export default defineNuxtPlugin(() => {
    const config = useRuntimeConfig();
    const axiosOptions: AxiosRequestConfig = {
        baseURL: process.server ? config.baseUrl : '/',
        withCredentials: true,
    };

    const axiosInstance = axios.create(axiosOptions);    
    if (process.server) {
        axiosInstance.defaults.headers.common['accept-encoding'] = 'gzip, deflate';
    }

    const requestHeaders = useRequestHeaders();
    if (process.server && requestHeaders) {
        axiosInstance.defaults.headers.common = {
            ...(requestHeaders as AxiosRequestHeaders),
            ...axiosInstance.defaults.headers.common,
        };
    }
}
1

There are 1 best solutions below

2
DengSihan On

If you don't use a reverse proxy for your API server, which means your API server is connected with the users directly.

Then, the requests sent to your API server will come from both the front-end server and the users' devices.

When users first visit your site, if there is some data needed from the API on the page, the front-end server will send the request to the API, and render the full page with the response before the page is sent to users.

After users have opened the site, the API requests are sent from users directly.


As you wish to pass the real user's IP to the API server, the key problem is how to pass the real user's IP in the requests sent from the front-end server.

First, you may need to use a wrapped useFetch instead of the bare useFetch to set the requests and handle the responses uniformly.

To pass the X-Forwarded-For to the API server,

// the requests sent from the front-end server
if (process.server) {
    const rawHeaders = useRequestHeaders();
    headers['X-Forwarded-For'] = rawHeaders['x-forwarded-for-nuxt'];
}

In your front-end server, add a new header in your nginx conf (keep the previous X-Forwarded-For setting)

proxy_set_header X-Forwarded-For-Nuxt $remote_addr;

Here is the answer showing how to handle the response uniformly: Nuxt 3 useFetch data possibly null errors when using Typescript

Here is a full example in js: laravel-nuxt3-web

Here is an online example: ruti.page