morgoth84
morgoth84

Reputation: 1140

Routing Apache ErrorDocument to a Laravel route

I'm using Laravel 5.1. I have this in my .htaccess file (these are only changes in the file, compared to the default):

Options -Indexes
ErrorDocument 403 /error-403

I also have a js/ directory in my public/ directory.

When I try to access the /js/ directory in the browser, I don't get the /error-403 route opened, as I'd expect. Instead I get Laravel's 404 HTTP error handler triggered (I get a 404 page from Laravel) and I don't understand why.

When I open /error-403 directly in the browser, the corresponding view opens fine. Here are some interesting server variables which I see in PHP for both of these requests.


/error-403:

"REDIRECT_STATUS" => "200"
"REDIRECT_URL" => "/error-403"
"REQUEST_METHOD" => "GET"
"REQUEST_URI" => "/error-403"

/js/:

"REDIRECT_REDIRECT_REQUEST_METHOD" => "GET"
"REDIRECT_REDIRECT_STATUS" => "403"
"REDIRECT_STATUS" => "403"
"REDIRECT_URL" => "/error-403"
"REQUEST_METHOD" => "GET"
"REQUEST_URI" => "/js/"

To me it seems like Apache's redirecting the request to Laravel, but Laravel, instead of picking up the URL /error-403, picks up the URL /js/ for which no route exists and then throws a 404.

Has anyone else encountered similar problems? What am I missing here? How can I set up Laravel so that Apache (and its ErrorDocument statement) redirects to a route defined in Laravel? And by "how", I mean how can I do it in a proper way, so it's not a hack. I guess I could create a custom middleware which would examine these server variables and correct the route, but this seems dirty and I don't know if I'm missing something here.

Upvotes: 0

Views: 979

Answers (1)

morgoth84
morgoth84

Reputation: 1140

I managed to overcome this problem, if anyone is interested.

I noticed the problem was in Symfony\Component\HttpFoundation\Request::prepareRequestUri(). In this method only REQUEST_URI is taken into account, REDIRECT_URL isn't ever looked at.

Laravel uses Illuminate\Http\Request which is a child class of the previously mentioned one. If you look at the service container, you will see an instance of this class. So what I needed to do was to extend this class and override this method. So I did this:

<?php

namespace App\Http;

use Illuminate\Http\Request as LaravelRequest;

class Request extends LaravelRequest
{
    protected function prepareRequestUri()
    {
        if ((int) $this->server->get('REDIRECT_STATUS', '200') >= 400 && $this->server->has('REDIRECT_URL')) {
            $requestUri = $this->server->get('REDIRECT_URL');
            $this->server->set('REQUEST_URI', $requestUri);
            return $requestUri;
        }

        return parent::prepareRequestUri();
    }
}

So, if the REDIRECT_STATUS is over 400 and REDIRECT_URI exists, it will be used, otherwise it will fallback to framework default logic.

Finally, I had to instruct the framework to use my implementation and not its own. I did this by editing public/index.php and replacing Illuminate\Http\Request::capture() with App\Http\Request::capture().

And now everything works as it should, at least from what I can tell. Apache error documents are being properly rendered by Laravel.

Upvotes: 1

Related Questions