Next.js app router and 404 / not found page.

404 - Not found

Usually if you visit a website, there has to be a defined page there. If you visit the root of a page, such as google.com or alexanderpressfelt.tech, it will bring you to the defined "index.html" page. Similarly, if you visit alexanderpressfelt.tech/projects, it will take you to the page that is defined for that path. However, if a page does not exist, such as alexanderpressfelt.tech/not-a-real-page (hopefully this remains unused), the web server will instead show a page that says that it couldn't find that page. This is usually called a 404 page, a not found page, etc.

This is not something new if you've been on the internet before. It's a simple solution to show that the website exists, and that the server is working, but that specific page doesn't exist, either it was removed, or it never existed to begin with. Sometimes pages are removed or change their location without updating all the links to that page, and sometimes there's typos or mistakes. Either way, they're common, and pretty much every website has them.

Next.js documentation Not found

First off, that's a lie. It exist, although it is very lackluster, feel free to look into it: https://nextjs.org/docs/app/api-reference/file-conventions/not-found. However, you will notice it doesn't say a lot. It tells you where the file should be placed, what it should be called, and what it might consist of. It also specifies it is a server component.

Normally, it shouldn't need a lot more than that, really. A 404 page is a rather simple concept. There might be some room for a few notes such as it not using layouts or templates, and that it will randomly load the 404 page when you visit existing pages.

How I noticed

While implementing logging on my website, I wanted to accomplish two things:

  • -> First, I want to log what page is being visited.

  • -> I want to log when a page that doesn't exist is visited.


To accomplish this, I originally wanted to add logging into my middleware. It made sense as a catch-all location. However, logging to file is not possible there. I am using the winston npm package to do logging to file as well as to an external source. However, Next.js does not allow logging to files inside of their middleware(as well as many other limitations). The backup was to do it in the root layout file, however because that is only loaded once, it doesn't actually log anything except for the first visit. So I ended up putting my logging into every page. The alternative would probably be to do a Route handler, or a server action that is called on every render in a client component, which seems awful.

Either way, this forced me to handle the 404 page, so naturally, I followed the documentation and put in app/not-found.js file, which was really easy, and initially, it seemed like a good solution. It now logged on every page visit, and I can make a special case for 404 pages as well!

Well, it seemed to at least, until I noticed it was now randomly logging several page visits as "not found", and showing the paths to pages that does exist. These logs came in combination with the normal logs, creating doubles. Initially I thought it was just some quirk of development environment, so I just pushed it to live and behold, it was also appearing there! This is troublesome as I now get 2 logs roughly 50% of the time when I just want one, and the second one is incorrectly stating that the path is not found. If I had tagged it as an error log, my mail would have blown up within minutes after the bot crawlers did their quarterly check.

The solution

I will not take credit for this, I got the solution from an article written by Shabin-k over at Dev.to. However, they wrote it because before Next.js 13.3, the functionality didn't exist in app router. However, I ended up benefiting from it even though I use Next.js 14.2.

The solution was to create a dynamic route with a catch-all segment. So by creating a folder called "[...not_found]"(or any name, as long as it has the square brackets and ellipsis), it will render this page whenever it can not find another page, essentially working like a Not Found page. This properly avoids it being rendered or functions being called in it randomly for no reason!

All of this made me more sure that App router is not quite ready for real big production applications, however it also made me realize how you can think outside of the box a bit to find a nifty solution. I am looking forward to seeing how Next.js develops over the future. I couldn't find anything in their changelogs related to this issue, but I will be doing a few more checks before opening an issue for it.

Hopefully you found this useful or interesting, I have found a few of these type of weird quirks when developing in Next.js, and I am sure there will be more!