Next.js Discord

Discord Forum

self-hosting nextjs alongside a backend on the same domain using nginx

Answered
Birman posted this in #help-forum
Open in Discord
BirmanOP
hello forum,
i have a small linux VPS server which hosts both a nextjs build and an all-in-one backend (pocketbase).
a domain name is routed to the VPS's IP.
what i used to do, which worked, was to have the next app on the root of the domain, and a sub-domain for the admin dashboard provided by the backend.

i am now trying to use only one domain for both services.
the backend has two paths: /api and /_. with this knowledge, i tried to simply add a location to my nginx configuration for this domain, as follows:
location ~ ^/(api|_)/ {
    add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive";
    proxy_set_header Connection '';
    proxy_http_version 1.1;
    proxy_read_timeout 360s;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://localhost:<port>;
}

location / {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    proxy_pass http://localhost:3000;
}

i have removed the rest of the file because it's mostly certbot boilerplate that is functional.

my issues is this. with the above configuration, the root domain / successfully redirects to the nextjs app. backend data calls to /api seem to work as well. i cannot access the dashboard via the browser however. when i try, a 404 is returned from the nextjs app (not nginx) as if it were a route. i know about the trailingSlashes option, but i suspect i can solve this via pure nginx configuration without forcing all other paths to have a trailing slash, hopefully anyway.
Answered by Giant panda
    location ~ ^/(api|_(?!next)) {
      return 200 "api backend";
    }

    location / {
      return 200 "nextjs backend";
    }
View full answer

16 Replies

BirmanOP
i have tried basically all variations of the backend nginx locations (=, ~ ...), changing the order of the location blocks, adding/removing trailing slashes to urls and paths, and adding a rewrite rule to append a slash again, but to no avail so far.

although, i have succeeded in making the backend dashboard available, but it breaks static content and nextjs api calls, a reversal of the main problem.

i realise this may be more of an nginx question, or even a pocketbase question as well, but i'm giving it a shot here anyway.

thanks in advance
Giant panda
So at what path should the dashboard be?
BirmanOP
domain.com/_
Giant panda
Have you tried domain.com/_/? Your rule specifies it needs a trailing slash
Also https://nginx-playground.wizardzines.com/ can be useful to test rules
you could add a simplified version like
    location ~ ^/(api|_)/ {
      return 200 "api backend";
    }

    location / {
      return 200 "nextjs backend";
    }
Changing to location ~ ^/(api|_) (without trailing slash) makes it work I think
Let me know if that does the trick for you
BirmanOP
thank you for the resource, i was looking around for something like this. better than entering the same commands over and over again ha.

i have tried domain.com/_/, but i suspect because of the default behaviour of nextjs the trailing slash is removed after i enter the url.

i'll do some more testing and come back here as well though. in any case, many thanks!
BirmanOP
after removing the trailing slash, i just realised, does the underscore also unintentionally catch _next? because according to an nginx test it does, explaining why when i do the opposite the dashboard works, html works, but not static content from _next.
Giant panda
Possibly, you could either add this explicitly into the location block to next or use a regex lookahead clause. This will only match the underscore if it's not followed by "next"
Giant panda
    location ~ ^/(api|_(?!next)) {
      return 200 "api backend";
    }

    location / {
      return 200 "nextjs backend";
    }
Answer
Giant panda
using the playground testing http --pretty format get http://localhost:80/_next/thing then routes to the nextjs backend
I prefer the above, but you could also do something along the lines of
    location ~ ^/(.*|_next) {
      return 200 "nextjs backend";
    }
    
    location ~ ^/(api|_) {
      return 200 "api backend";
    }
Or, just use a different path for your admin dashboard that doesn't overlap and avoid the whole problem 😉
@Giant panda Or, just use a different path for your admin dashboard that doesn't overlap and avoid the whole problem 😉
BirmanOP
hello, a bit delayed in my response sorry.

i ended up going back to separate domains for the backend and the frontend, as i realised only after starting to go down the rabbit hole of nginx that i use api routes with nextjs, so my original plan never would have worked any way.

your answer is correct though, and if not for the conflicts between api routes, this nginx setup works fine.

thank you for your help in any case.