Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

isimmons's avatar

How does Laravel.com add a trailing slash to the api docs link?

Hi,

I have autogenerated api documentation with Doctum

I went through the code and scripts at github/laravel.com to learn how.

I successfully generated the docs, copied them over to /public/docs and can now view them at the following route

Route::get('docs', function () {
	return  File::get(public_path() . '/docs/index.html');
});

However, none of the css and js files for doctum load unless I have a trailing slash in the url http://tickets-please.test/docs/

This has to do with doctum using relative paths for everything.

I don't see any such route in laravel.com source but somehow the api docs loads without having index.html in the url and it automatically adds the trailing slash if you leave it off.

I have tried all suggestions I found. The only two that work to add a trailing slash are

  1. middleware that redirects to a version with the slash
  2. a rewrite rule in the Nginx site config file

But both of these methods cause a redirect loop because Laravel strips the trailing slash and then the two methods above add it back and it just goes in a loop.

So somehow Laravel.com is doing something to make it work.

2 things laravel.com has

  1. no route to the api directory
  2. automatic add the trailing slash but only on the api directory

Any ideas?

Thanks

0 likes
4 replies
isimmons's avatar

More info: I'm on another PC without Herd now, running on artisan serve I get the exact same behavior.

LaryAI's avatar
Level 58

To handle the issue of adding a trailing slash to your API documentation links in Laravel without causing a redirect loop, you can use a combination of middleware and a careful check to ensure that the trailing slash is only added when necessary. Here’s a step-by-step solution:

  1. Create a Middleware: First, create a middleware that checks if the request URL is targeting the documentation and lacks a trailing slash, then redirects to the URL with the slash.

    You can generate a middleware using the Artisan command:

    php artisan make:middleware EnsureTrailingSlash
    
  2. Implement the Middleware: In the EnsureTrailingSlash middleware, add the following code:

    namespace App\Http\Middleware;
    
    use Closure;
    
    class EnsureTrailingSlash
    {
        public function handle($request, Closure $next)
        {
            $path = $request->getRequestUri();
    
            // Check if the path is for 'docs' and lacks a trailing slash
            if (substr($path, 0, 5) === '/docs' && substr($path, -1) !== '/') {
                return redirect($path . '/', 301);
            }
    
            return $next($request);
        }
    }
    
  3. Register the Middleware: Add the middleware to the $routeMiddleware array in your app/Http/Kernel.php file:

    protected $routeMiddleware = [
        // other middleware
        'trailing.slash' => \App\Http\Middleware\EnsureTrailingSlash::class,
    ];
    
  4. Apply the Middleware to Routes: Apply this middleware to your documentation route. You can either apply it globally or just to specific routes. Here’s how you can apply it to a specific route:

    Route::get('docs', function () {
        return File::get(public_path() . '/docs/index.html');
    })->middleware('trailing.slash');
    
  5. Testing: Make sure to test this setup in your development environment. Try accessing http://yourdomain.com/docs and ensure it redirects correctly to http://yourdomain.com/docs/ without entering a redirect loop.

This solution ensures that the trailing slash is added specifically for the documentation directory, avoiding the issues with relative paths in your Doctum-generated documentation. It also confines the behavior to the /docs path, preventing unintended effects on other routes in your application.

isimmons's avatar

Thank you Larry!

While this does not work in Laravel 11 (Laravel strips the trailing slash back off), it at least does not cause a redirect loop because it is specifically for the docs directory only.

I was able to resolve this in another way. Instead of trying to fix the missing slash, I fix the relative urls created by Doctum. I created and registered a global middleware to check all requests and compare against known directories and extensions in the docs directory. Then if one is found I redirect with the appended 'docs'. I also added a check for exclusions such as rss.xml because one of doctums files is an xml file. It could use some refactoring but it is working for now.

FixDocsUrls global middleware

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class FixDocsUrls
{
    public function handle(Request $request, Closure $next): Response
    {
        $path = $request->getRequestUri();

        $subDirs = ['/js', '/css', '/fonts', '/App'];
        $extensions = ['.js', '.xml', '.html'];
        $exclusions = ['/rss.xml']; // example exclude

        foreach ($subDirs as $dir) {
            if(str_starts_with($path, $dir)) {
                return redirect('docs' . $path, 301);
            }
        }

        foreach ($extensions as $ext) {
            if(str_ends_with($path, $ext) &&  !in_array($path, $exclusions)) {
                return redirect('docs' . $path, 301);
            }
        }

        return $next($request);

    }

}
isimmons's avatar

Of course it would be great to come up with a rewrite rule for both apache and nginx to achieve the same behavior but I don't know how yet.

Please or to participate in this conversation.