Getting 404 Not Found error when refreshing dynamic page in DirectAdmin deployment

386 Views Asked by At

I'm encountering a "404 Not Found" error when trying to refresh a dynamic page in my Next.js application deployed on DirectAdmin. The error occurs only when I attempt to refresh the page, but navigating to it from a different route works fine.

Here are the details of my setup:

  • I'm using Next.js for my application.
  • I have a dynamic page with a route like /products/[id], where [id] represents the unique identifier for each product.
  • I've deployed my application on DirectAdmin, and I'm using the default configuration for routing.

When I land on a dynamic page, let's say /products/123, everything works as expected. However, if I refresh the page or directly access the URL /products/123, I receive the following error:

Not Found The requested URL was not found on this server.

Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.

I suspect there might be an issue with the routing configuration in DirectAdmin, but I'm not sure how to resolve it. I've checked my Next.js pages and routing setup, and everything seems to be in order.

I tested it on other types of servers and it worked well, but I think there is a problem with Directadmin!

Here's my code:

pages/index.jsx

import Link from 'next/link'
import { useEffect, useState } from 'react'

export default function HomePage() {

const [todos, setTodos] = useState([]);

useEffect(() => {
   async function fetchData() {
     const res = await fetch('https://jsonplaceholder.typicode.com/todos?_sort=id&_order=ascs&_limit=5');
     const data = await res.json();
     setTodos(data);
    }
    fetchData();
  }, []);

return (
  {todos && (
    <div>
      <h1>Todos</h1>
      {
        todos.map(todo => (
          <Link
            href={`/todo/${todo.id}`}
            className="block p-2"
          >
          {todo.title}
        </Link>
      ))}
    </div>
  )}
 )
}

pages/todo/[slug].jsx

import { useRouter } from 'next/router';
import { useState, useEffect } from 'react';

export default function Todo() {
    const router = useRouter();
    const { id } = router.query;
    const [todo, setTodo] = useState(null);

    useEffect(() => {
        async function fetchTodo() {
           const res = await      fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);
           const data = await res.json();
          setTodo(data);
}
if (id) {
  fetchTodo();
}
}, [id]);

if (!todo) {
   return <div>Loading...</div>;
 }

return (
   <div>
     <p>{todo.id}</p>
     <h1>{todo.title}</h1>
   </div>
);
}

next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
    output: 'export',
    trailingSlash: true,
}

module.exports = nextConfig
3

There are 3 best solutions below

2
On BEST ANSWER

The reason for this is because of next export.

Next export builds static HTML files. If you look at your output folder you probably see something like this.

folder output

This is what the browser sees. So since there is no HTML file is in the static output that matches /todo/1.html it produces a 404.

Solutions

Configure a rewrite

When the server gets a request for /todo/1 you want to serve the user /todo/[slug]/index.html.

Rewrite are normal pretty easy to set up

Don't use next export

If you turn off next export it will work as you expect

0
On

While navigating to a route you're probably passing some parameters through Link component like this:

<Link
  href={{
    pathname: '/about',
    query: { name: 'test' },
  }}
>
  About
</Link>

But while refreshing the page nextjs is probably not getting the path data. Check your rewrite rules in next.config.js

    const nextConfig = {
      async rewrites() {
        return [
          {
            source: "/products/:id",
            destination: "/products/:id",
            locale: false
          },
       ]}
    }
module.exports = nextConfig;
0
On

I got it working as follows on DirectAdmin control panel. Create a file named .htaccess in the public_html folder then paste the following into it.

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.html$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.html [L]
</IfModule>