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

tlacaelelrl's avatar

302 redirect causes an error when using web and auth middlewares

Hello,

I have an app running in Laravel 7, in the app I have a custom routes file which is generated from the database and looks like this:

<?php

use Illuminate\Support\Facades\Route;

Route::middleware(['web'])->group(function() {
Route::get('some/public/page', 'My\PublicController@view');

Route::middleware(['auth'])->group(function() {
Route::get('some/private/page', 'My\Controllerr@view');
});
});

In the current app it works just fine, if I hit the some/private/page I am either displayed the page or redirected to the login page if I am not logged in.

Now I am updating the app to laravel 9, hitting the route some/public/page works fine but when I go to some/private/page I get this error:

Attempt to read property "headers" on string
vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php:191

After trying to figure out what was going on for some time, I made this change to the routes file:

<?php

use Illuminate\Support\Facades\Route;

Route::middleware(['web'])->group(function() {
Route::get('some/public/page', 'My\PublicController@view');
});

Route::middleware(['auth'])->group(function() {
Route::get('some/private/page', 'My\Controllerr@view');
});

This way the 'some/private/page route opens fine the login page, however now I can't login because now the route does not seem to be opening the session so submitting the login form takes me back again to the login form.

Then I also tried like this and the error came back

<?php

use Illuminate\Support\Facades\Route;

Route::middleware(['web'])->group(function() {
Route::get('some/public/page', 'My\PublicController@view');
});

Route::middleware(['web','auth'])->group(function() {
Route::get('some/private/page', 'My\Controllerr@view');
});

By the way, the redirect is happening like this:

HTTP/1.0 302 Found
Cache-Control: no-cache, private
Date:          Thu, 26 Jan 2023 12:47:33 GMT
Location:      https://domain.com/login

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="refresh" content="0;url='https://domain.com/login'" />

        <title>Redirecting to https://domain.com/login</title>
    </head>
    <body>
        Redirecting to <a href="https://domain.com">https://domain.com</a>.
    </body>
</html>

Which causes an unnecessary delay and I believe is the actual cause of the problem, before the login page was being rendered without such delay.

Has anyone experienced this issue? Is there something I am doing wrong in the new version? can't I use both web and auth middlewares together?

0 likes
7 replies
s4muel's avatar

what does your view(...) method in MyController from the private route return?

tlacaelelrl's avatar

@s4muel Hi, it returns a view in the form

return view('mypackage::my.view', ['my'=>'params']);

It is plainly not working, I even tried this:

Route::middleware(['auth'])->group(function() {
    Route::get('whatever', function(){
        return '1';
    });
});

And the same result, I was initially thinking my custom package may have something to do with this so I installed laravel in a new subdomain, tested the auth routes and they worked, I then added my package and it continued to work so right now I have two installs which appear identical (obviously there is something different) one works and the other does not.

I tried clearing the cache, nothing.

My best guess is that the laravel install is corrupted or the particular version has a problem with it, the installs are about 2 weeks apart though and the about command shows the exact same version for everything so I am sticking with corrupted files, maybe a file I worked on got corrupted during upload??

I will keep this open for some time to see if anyone has had a similar issue or in case my issue returns faster than expected.

Sinnbeck's avatar

@tlacaelelrl Any chance you can share the error page so we can look through the stack trace. In laravel 9 there is a share button

tlacaelelrl's avatar

@Sinnbeck this is it:

https://flareapp.io/share/17DqlOE5

The last routes file I tested with is this:

<?php

use Illuminate\Support\Facades\Route;

Route::middleware(['auth'])->group(function() {
    Route::get('whatever', function(){
        return '1';
    });
});
Route::get('login', function(){
    return '1';
})->name('login');
s4muel's avatar

@tlacaelelrl the return seems fine. since it fails having the right object to add headers in VerifyCsrfToken middleware only when the auth middleware is used, i would check there. App\Http\Middleware\Authenticate class, out of the box, is:

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;

class Authenticate extends Middleware
{
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return string|null
     */
    protected function redirectTo($request)
    {
        if (! $request->expectsJson()) {
            return route('login');
        }
    }
}

don't you override some methods in there, so it returns something suspicious? i would just debug around there and check what is returned. at first AuthenticationException should be thrown (in Illuminate\Auth\Middleware\Authenticate) and then "converted" to redirectResponse to the page defined in the redirectTo() method.

tlacaelelrl's avatar
tlacaelelrl
OP
Best Answer
Level 1

@s4muel The middleware is not modified but you made me think about the things I did modify, in this case

App\Exceptions\ExceptionHandler

I have some custom exceptions which are documented so the employees can easily fix, I have this:

<?php

namespace App\Exceptions;

use ...;

class Handler extends ExceptionHandler
{
    public function render($request, Throwable $e):Response|JsonResponse
    {
        /**
         * Custom error code handler
         */
        $exception = $this->prepareException($this->mapException($e));
        $message = json_decode($exception->getMessage());
        $code = method_exists($exception, 'getStatusCode') ? $exception->getStatusCode() : $exception->getCode();
        if(App::environment('production') && in_array($code, Handler::$HANDLED_CODES)){
            if($request->expectsJson()){
                $error = [
                    'error' => $code,
                    'message' => $exception->getMessage(),
                ];
                return  response()->json($error, $code);
            } else {
                return response()->view("errors.custom", [
                    'code'=> $code,
                    'message'=> __("error/codes.$code"),
                ], $code);
            }
        }
        return parent::render($request, $e);
    }
}

And what is causing the error is adding the return type to the method, since the parent is responding with \Symfony\Component\HttpFoundation\Response and my method expects Illuminate\Http\Response looks like an error is triggered and the response is somewhere converted to string.

Adding \Symfony\Component\HttpFoundation\Response to the return on my method fixed the problem.

public function render($request, Throwable $e):Response|JsonResponse|\Symfony\Component\HttpFoundation\Response {}
1 like
tlacaelelrl's avatar

I just want to update with some more context in case this is somehow related.

My setup is with docker

Containers:

nginx

mysql

php version 7.2 <- this one has containers x the amount of projects not yet updated

php version 8 <- this one has a one container with the project I am working on, note that this is a new project and decided to use the opportunity to also update the package to support the new laravel version.

Most projects I am running have laravel and they also share this custom package, they all work properly as of now and this issue is only present in the laravel version 9, see the about:

af6deac1206f:/var/www/domains/some.tld/app$ php artisan about

  Environment .......................
  Application Name ............SomeProject
  Laravel Version .................9.48.0
  PHP Version ........................8.0.26
  Composer Version ..........2.5.1
  Environment ...................... local
  Debug Mode .......................ENABLED
  URL ..........................................some.tld
  Maintenance Mode .........OFF

  Cache ....................................
  Config ....................................NOT CACHED
  Events ....................................NOT CACHED
  Routes ................................... NOT CACHED
  Views ..................................... CACHED

  Drivers ...................................
  Broadcasting ....................... log
  Cache ...................................... file
  Database ............................... mysql
  Logs .......................................... stack / single
  Mail ........................................... smtp
  Queue ...................................... sync
  Session ..................................... file

The custom package is shared across all containers, basically the package's folder is mounted in the container in /var/www/packages/my-vendor/my-package/. Then in the composer file I have this to include my package:

{
    "name": "laravel/laravel",
    "type": "project",
    "description": "The Laravel Framework.",
    "keywords": ["framework", "laravel"],
    "license": "MIT",
    "require": {
        "php": "^8.0.2",
        "g...": "...",
        "my-vendor/my-package": "dev-main"
    },
    "require-dev": {...},
    "autoload": {...},
    "autoload-dev": {...},
    "scripts": {...},
    "extra": {...},
    "config": {...},
    "minimum-stability": "stable",
    "prefer-stable": true,
    "repositories": [
        {
            "type": "path",
            "url": "/var/www/packages/my-vendor/my-package/"
        }
    ]
}

Just like the package is included, all files from laravel are shared across projects and the only difference is tha only project specific folders and files are unique to each project, such as the .env file or the bootstrap/cache folder.

After I reinstalled laravel and confirmed my package was working with it, I mounted the new laravel install in my project and it is working, I still have the files from the previous install (the one that caused the problem).

Please or to participate in this conversation.