Search code examples
dockerapachenext.jsnext.js14

Using apache proxy to Next.js app running in Docker container


I'm sure there's a simple answer to this... I've got a Next.js v14 app running in a container which is exposed on port 6502. I can reach it using http://localhost:6502 and everything works. Now I want to use Apache's reverse proxy to serve up the site but when I do, I get all kinds of problems with the wrong URLs showing up (i.e. paths missing).

Here's an excerpt of my next.config.mjs file (isProd is already set to true):

  output: "standalone",
  assetPrefix: isProd ? "http://mydomain/myapp" : undefined,

  // Optional: Change links `/me` -> `/me/` and emit `/me.html` -> `/me/index.html`
  trailingSlash: true,

And in my apache config file:

    ProxyPreserveHost On
    ProxyRequests Off
    ProxyPass "/myapp" "http://localhost:6502/"
    ProxyPassReverse "/myapp" "http://localhost:6502/"

The docker container is running on the same server that apache is running on.

With this config, when I go to http://mydomain/myapp the page is rendered but without images (which are saved under /public/images in my Next.js project). When I click on a link in that page it should take me to another page in the same app (i.e. /src/app/update/page.tsx) but instead it gets redirected to http://mydomain/update and not http://mydomain/myapp/update. I've played around with assetPrefix and even basePath in next.config.mjs to no avail. Instead of continuing to waste hours troubleshooting this I figured I'd ask here.


Solution

  • The main solution to this issue was to remove the domain name from assetPrefix in next.config.mjs and export it as an environment variable:

    const isProd = process.env.NODE_ENV === "production";
    assetPrefix: isProd ? "/myapp" : "http://localhost:3000";
    
    const nextConfig = async (phase, { defaultConfig }) => {
      return {
        env: {
          ASSET_PREFIX: assetPrefix, // Assigned at BUILD time
        },
        assetPrefix: assetPrefix,
        ...
      };
    };
    
    export default nextConfig;
    

    Then pass it to my client components as a prop from the page.tsx file which runs on the server side:

    "use server";
    
    export default async function Page() {
      return Promise.resolve(
        <MyComponent assetPrefix={process.env.ASSET_PREFIX} />
      };
    }
    

    Finally, prepend the prefix to all my hyperlinks in MyComponent:

    <Link href={`${props.assetPrefix}/mypage`}`>Click</Link>`