How to configure NextJS to proxy its requests
I found myself in a situation where I wanted to record the requests my NextJS application was making. Where for a straight client-side SPA I could use the network tab of the developer dev tools:
For an application that involves SSR and RSCs, it's not quite this simple - as some of those requests may be made on the server side - and be simply returning the rendered HTML to the browser.
The solution I've opted to investigate is using the tool mitmproxy, we proxy our requests through mitmproxy and and use that to record a HAR file.
Sample application
I have a sample application set up at this repository here, I'm request data in various forms from the jsonplaceholder API.
I have four kinds of requests:
Straight client side requests
Lines 1 to 15 in a734969
1"use client";
2
3import React from "react";
4
5export function ComponentFetch() {
6 const [data, setData] = React.useState(null);
7
8 React.useEffect(() => {
9 fetch("https://jsonplaceholder.typicode.com/posts/1").then((res) => res.json()).then((d) => setData(d))
10 }, [])
11
12 return <div>
13 {JSON.stringify(data)}
14 </div>
15}
This is the most basic, and this doesn't require special NextJS configuration - the request to jsonplaceholder is made directly from the browser, and if our browser is configured to use a proxy those requests will indeed be proxied.
Client side requests with rewrites
1"use client";
2import React from "react";
3
4export function ComponentFetchRelative() {
5 const [data, setData] = React.useState(null);
6
7 React.useEffect(() => {
8 fetch("/api/posts/2").then((res) => res.json()).then((d) => setData(d))
9 }, [])
10 return <div>
11 {JSON.stringify(data)}
12 </div>
13}
In this scenario we request the data via a relative path, and we configure NextJS to rewrite the request in the next.config.js
file:
Lines 7 to 16 in a734969
7const nextConfig = {
8 async rewrites() {
9 return [
10 {
11 source: '/api/:path*',
12 destination: 'https://jsonplaceholder.typicode.com/:path*',
13 },
14 ]
15 },
16};
In this case the requests won't be directed to our proxy, as our browser proxy configuration doesn't proxy the localhost requests.
Server side requests - with native fetch
Lines 5 to 16 in a734969
5async function getData() {
6 const result = await fetch("https://jsonplaceholder.typicode.com/posts/3")
7 return result.json();
8}
9
10export default async function Page3() {
11
12 const data = await getData();
13 return <div>
14 {JSON.stringify(data)}
15 </div>
16}
In this scenario the request is made on the server as a RSC. We need a way to instruct our application to proxy these requests via our proxy.
Server side requests - with node-fetch
Lines 1 to 15 in 0bb4f13
1import nodeFetch from"node-fetch";
2
3async function getData() {
4 const result = await nodeFetch("https://jsonplaceholder.typicode.com/posts/6", {
5 })
6 return result.json();
7}
8
9export default async function Page5() {
10
11 const data = await getData();
12 return <div>
13 {JSON.stringify(data)}
14 </div>
15}
This is the same scenario but we're using node-fetch instead of native fetch.
Solution - use a secondary proxy
The best solution I've found is to add your own secondary proxy, and have it proxy its traffic through your target proxy.
Create a proxy server with this script:
Lines 1 to 12 in 4f6ce76
1var http = require('http');
2var httpProxy = require('http-proxy');
3
4const {HttpsProxyAgent} = require('https-proxy-agent');
5
6const agent = new HttpsProxyAgent("https://127.0.0.1:8080")
7
8var proxy = httpProxy.createProxyServer({"agent": agent});
9
10http.createServer(function(req, res) {
11 proxy.web(req, res, {changeOrigin: true, target: 'http://jsonplaceholder.typicode.com'});
12}).listen(3001);
Update your requests, whether they're are server side requests or rewrites to reference an environment variable as the upstream.
Lines 2 to 3 in 4f6ce76
2 const result = await fetch(`${process.env.UPSTREAM_URL}/posts/3`)
3 return result.json();
Start both your secondary proxy and NextJS application with the NODE_EXTRA_CA_CERTS
env var provided.
Lines 7 to 8 in 4f6ce76
7 "dev:secure": "NODE_EXTRA_CA_CERTS=~/.mitmproxy/mitmproxy-ca-cert.pem next dev",
8 "proxy": "NODE_EXTRA_CA_CERTS=~/.mitmproxy/mitmproxy-ca-cert.pem node proxy.js",
Here you can see mitmproxy recording my requests!
Alternative solutions
If the secondary proxy approach doesn't work for you, you can configure the proxying of your http client (eg. fetch or node-fetch) directly. See my post here for details.
Questions? Comments? Criticisms? Get in the comments! 👇
Spotted an error? Edit this page with Github