Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NextJS behind proxy with URL Rewrite

Context:

I've a NextJS deployment behind Nginx. The idea is to use NextJS to create several websites hosted in different domains. Each domain will have an entry in Nginx and it will be pointing to the specific path pages/cafes/[cafeId] in NextJS. There will be only one NextJs deployment for all websites and each domain will be routed using static proxy in nginx.

nginx.conf

   server {
        listen 80;
        server_name www.cafe-one.local;
        location = / {
            proxy_pass http://localhost:3000/cafes/cafe_id_1;
            ...
        }
        location / {
            proxy_pass http://localhost:3000/;
            ...
        }
    }
    
    server {
        listen 80;
        server_name www.cafe-two.local;
        location = / {
            proxy_pass http://localhost:3000/cafes/cafe_id_2;
            ...
        }
        location / {
            proxy_pass http://localhost:3000/;
            ...
        }
    }

pages/[cafeId]/index.js

export const getStaticPaths = async () => {
  return {
    paths: [], // no website rendered during build time, there are around 1000+ sites
    fallback: true
  };
};

export const getStaticProps = async context => {

  const cafeId = context.params.cafeId;
  const cafe = await ... // get data from server

  return {
    props: {
      cafe
    },
    revalidate: 10 // revalidate every 10 seconds
  };
};

export default function CafeWebsite(props) {
  const router = useRouter();

  // getStaticProps() is not finished
  if (router.isFallback) {
    return <div>Loading...</div>;
  }
  
  return <div>{props.cafe.name}</div>;
}

Issue:

When I access www.cafe-one.local, i get to the loading screen, but then NextJS throw a client-side error about the The provided as value (/) is incompatible with the href value (/cafes/[cafeId]). This is understandable because the current URL is not what NextJS is expecting.

enter image description here

Question:

How to fix the issue, such that NextJS could be used in front of Nginx reverse proxy?

Any help is appreciated.

Thanks in advance.

like image 701
sowdri Avatar asked Aug 01 '20 04:08

sowdri


2 Answers

I have been dealing with the same issue, but for mapping different subdomains to a dynamic route in the NextJS app.

I haven't been able to find a proper solution to the The provided as value (/) is incompatible with the href value error, but I found a somewhat hacky workaround.

First, you have to redirect the requests from my-domain.com to my-domain.com/path-to-dynamic-route. Then you have to reverse proxy all request from my-domain.com/path-to-dynamic-route to the same dynamic route in the NextJS app, like localhost:3000/path-to-dynamic-route.

You can do it manually from NGINX with a combination of return 301 and proxy_pass, or you can let NextJS do it automatically by passing the dynamic route in the proxy_pass directive with a trailing slash.

nginx.conf


server {
    listen 80;
    server_name www.cafe-one.local;
    location = / {
        # When a url to a route has a trailing slash, NextJS responds with a "308 Permanent redirect" to the path without the slash.
        # In this case from /cafes/cafe_id_1/ to /cafes/cafe_id_1
        proxy_pass http://localhost:3000/cafes/cafe_id_1/;
        # If you also want to be able to pass parameters in the query string, you should use the variable $request_uri instead of "/"
        # proxy_pass http://localhost:3000/cafes/cafe_id_1$request_uri;
        ...
    }
    location / {
        # Any other request to www.cafe-one.local will keep the original route and query string
        proxy_pass http://localhost:3000$request_uri;
        ...
    }
}

This should work, but now we have a problem with the url in the address bar. Any user visiting www.cafe-one.local will be redirected to www.cafe-one.local/cafes/cafe_id_1 and that doesn't look nice.

The only workaround I found to solve this issue was to use javascript to remove the path by rewriting the browsing history with window.history.replaceState().

pages/[cafeId]/index.js

...
export default function CafeWebsite(props) {
  if (typeof window !== "undefined") {
    window.history.replaceState(null, "", "/")
  }
...

if you don't want to remove the path for all domains, you can use window.location.hostname to check the current url.

...
export default function CafeWebsite(props) {
  if (typeof window !== "undefined") {
    const hostname = window.location.hostname
    const regex = /^(www\.my-domain\.|my-domain\.)/
    if (!regex.test(hostname)) {
      window.history.replaceState(null, "", "/")
    }
  }
...
like image 121
Ale Felix Avatar answered Sep 17 '22 04:09

Ale Felix


I've managed to handle the same issue by forcing my dynamic pages to use ONLY serverside rendering.

The fact is, Next tries to hydrate route params from the browser page URL which is outside of Next's context due to nginx.

like image 27
Sergey Pozhilov Avatar answered Sep 19 '22 04:09

Sergey Pozhilov