DEV Community

Mohamed
Mohamed

Posted on

Laravel: the homepage worked, but every slug returned 404

The home page on a custom domain loaded. Every page at /<slug> returned 404.

The domain resolved, the root route worked, and the page records existed. Those symptoms can make a routing bug look like a missing-content problem.

The route was this shape:

Route::domain('{customDomain}')->group(function () {
    Route::get('/{slug}', [LandingPageController::class, 'customShow']);
});
Enter fullscreen mode Exit fullscreen mode

The missing detail was in the controller signature. {customDomain} is a route parameter too, and it arrives before {slug}. A method shaped like this leaves no argument for the domain:

public function customShow(Request $request, string $slug)
Enter fullscreen mode Exit fullscreen mode

For a request to https://client.example/services, the route supplies client.example and then services. With that signature, $slug receives client.example. Looking up a page with the host as its slug returns nothing, so the request ends in a 404.

The fix was to account for both parameters. Here's the relevant part of the method:

public function customShow(Request $request, string $customDomain, string $slug)
{
    $host = $request->getHost();

    $page = LandingPage::where('host', $host)
        ->where('slug', $slug)
        ->where('is_published', true)
        ->first();

    // Render the page or continue with the site's normal fallback.
}
Enter fullscreen mode Exit fullscreen mode

The method reads the host from the request because that's the value it uses for the lookup. The $customDomain argument still has to occupy its place so $slug receives the path segment.

What made this hard to notice was the passing home page. / uses a different controller method and has no slug to misplace. It proved the domain was reachable, but it didn't prove nested pages worked.

The regression test seeds a home page and a separate service page on the same host, then makes both requests:

$this->get('http://poolco.example.test/')
    ->assertOk()
    ->assertSee('Welcome To The Pool Co Home', false);

$this->get('http://poolco.example.test/deep-service-page')
    ->assertOk()
    ->assertSee('The Deep Service Landing Page', false);
Enter fullscreen mode Exit fullscreen mode

Checking the content matters too. A 200 response from a fallback page wouldn't prove that the requested slug reached the right page.

When a Laravel route is bound to Route::domain('{something}'), I now count the domain placeholder while matching route parameters to a controller signature. It is easy to forget because it isn't visible in the path.


I build and run SEOSellers. This issue came from its custom-domain page routing.

Top comments (0)