Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Laravel Inertia apps doesn't support subdomain redirects #431

Closed
MooseSaeed opened this issue Jul 12, 2022 · 15 comments
Closed

Laravel Inertia apps doesn't support subdomain redirects #431

MooseSaeed opened this issue Jul 12, 2022 · 15 comments

Comments

@MooseSaeed
Copy link

MooseSaeed commented Jul 12, 2022

I'm working on a Laravel 9, InertiaJS and vue project where a user can submit a new product through products/create route and this sends to ProductController@store which does all what it's supposed to do and then supposedly redirect the user to the newly created product on a subdomain route like this http://username.example.test/my-new-product

The issue I'm having is that I keep getting the below error and the redirect doesn't work:

Access to XMLHttpRequest at 'http://user.example.test/my-first-product' (redirected from 'http://example.test/products/create') from origin 'http://example.test' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

The code works as follows:

1- The form:

const form = useForm("createProduct", {
    webshop_id: null,
    title: null,
    price: null,
    type: null,
    currency: null,
    description: null,
    thumbnail: null,
});
const submit = () => {
    form.post(route("products.create"), {
        onSuccess: () => form.reset(),
    });
};

2- Products create router:

Route::middleware([
    'auth:sanctum',
    config('jetstream.auth_session'),
    'verified',
])->group(function () {

    Route::post('products/create', [ProductController::class, 'store'])->name('products.create');

});

3- The controller - Storing the new product with user details and redirecting (which doesn't work) to show the product

public function store(StoreProductRequest $request)
    {
		
		// Code
		
        return redirect()->route('products.show', [$user, $product]);
    }

4- The show route and controller@show

Route::domain('{user:username}.' . env('APP_URL'))->group(function () {
    Route::get('{product:slug}', [ProductController::class, 'show'])->name('products.show');
});
    public function show(User $user, Product $product)
    {

        return Inertia::render('Products/Show', [
            'user' => $user,
            'product' => $product,
            'thumbnails' => $product->productimages
        ]);
    }

I saw @reinink explain in this Issue that we can use now Inertia::location

The only way I could get this to work is returning this is the ProductController@store:

 return Inertia::location('http://' . $user->username . '.example.test/' . $validated['slug']);

But It causes a full page refresh which defeats the whole concept of having a single page application and I will have to remember changing 'http' to 'https' and domain to the real domain when I'm going to put the app in production. I was hoping that I was doing something wrong but after hours of searching for a solution I began to think that Laravel Inertia apps doesn't support subdomain redirects

@MooseSaeed
Copy link
Author

Solved the issue by making the below changes to cors.php

    'paths' => ['api/*', '*', 'sanctum/csrf-cookie'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => ['x-inertia'],

    'max_age' => 0,

    'supports_credentials' => false,

No cors issues now but I'm having a different issue where the desired page rendered by Inertia on the incorrect route. Raised the Issue for Laravel Framework

@MooseSaeed
Copy link
Author

This is driving me crazy! Subdomains isn't working even if I hard set the redirect like this redirect()->to('http://username.example.test/productSlug') it gives me in the browser url example.test/productSlug only. I even used Inertia to redirect from the frontend to a url and did the same. The only thing that works is using Inertia::location() but this causes a full page refresh.

@MooseSaeed
Copy link
Author

Same behavior occurs with Inertia's Link.

If you are on a subdomain route like {subdomain}.example.test/profile and you have a link like this <Link href="http://example.test">Click</Link> It will redirect you to {subdomain}.example.test. The only workaround is having a normal HTML anchor tag like this <a href="http://example.test">Click</a>

@laserhybiz
Copy link

Javascript's pushState cannot change the domain

@reinink
Copy link
Member

reinink commented Jul 21, 2022

@MooseSaeed Hey I honestly don't think this is possible to make Inertia requests across subdomains — sorry!

@MooseSaeed
Copy link
Author

@reinink I agree! After days of research I'm sure now that it is impossible to route redirect to a subdomain route from root domain without page refresh. Any change to the host will require the page to reload which is out of Laravel and JetStream scope and out of InertiaJS hands. As far as I understand that this is for security purposes.

Sorry for wasting your time and thanks a million for your efforts

@Tofandel
Copy link

Yes inertia works with browser history, but it seems inertia is missing a check, so that redirects to a different domain are handled correctly and reload the page

@Tofandel
Copy link

Tofandel commented Aug 22, 2022

@MooseSaeed As a workaround you can add to your HandleInertiaRequests middleware

public function version(Request $request): string
{
    return md5($request->host) . parent::version($request);
}

And if you have CORS working, the request will be followed and inertia will reload the page but you'll have one more request than what is strictly necessary

Cross domains redirects are not very frequent so you'd be better doing it explicitely without inertia using a window.location and it would prevent all the CORS issues that goes with it

If you need the domain to be dynamic, instead of a redirect then send the url as an inertia prop and use that in the success handler for your window.location

@riabiy-denis
Copy link

@reinink sorry to bother, but question, that is not covered in docs/thread and related to current issue.

Is using <a> tag to get to the subdomain route a viable/good practice for Inertia? E.g. little js component, that checks, if subdomain changes and renders <InertiaLink> or <a>.

It's the same app, same cookie name, subdomains due to SEO-stuff.

Cause I don't see any issues with that, but would like not to face any long-term.

@NinjaJake86
Copy link

Just found this issue as i was having issues with the same thing, is there any chance sub domain redirects can be added to inertia? Would be a great help!

@jessarcher
Copy link
Member

Hey there,

We're closing this issue because it's inactive, already solved, old or not relevant anymore. Feel to open up a new issue if you're still experiencing this problem.

@boris-glumpler
Copy link

boris-glumpler commented Feb 29, 2024

Just came across this issue and, like @riabiy-denis, I solved it via a small component that either outputs an a tag or an Inertia Link component by comparing the current host with the host of the link. Works really well and was easy to implement!

@ibrahimdolas
Copy link

I understand that inertia prevents cross-subdomain redirects for security purposes. But I'm having a similar issue right now. I have a landing page on example.test and an admin panel on crm.example.test

This two layouts are completely different and don't redirect to each other. On the landing page, there's not a "go to admin panel" link or something like that. There's no link between subdomains. But still, I'm having the "pushState" error. I'm trying to redirect from crm.example.test/login to crm.example.test/dashboard and have pushState error. When I add replace:true option to router, redirect happens and dashboard page renders but still have the console error and url stands still as /login. Any ideas?

@RobertBoes
Copy link
Contributor

@ibrahimdolas Don't think that has anything to do with this issue. You say you're not navigating between different (sub)domains, so there wouldn't be any conflict. Inertia only pushes the path to the history state (which also is the only thing that's allowed) so that in itself wouldn't trigger pushstate errors.

@eliofery
Copy link

I am using Laravel 11 and faced the same issue.

// bootstrap/app.php

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
        then: function () {
            Route::domain(config('app.url'))
                ->middleware('web')
                ->group(base_path('routes/web.php'));

            Route::domain(config('app.admin_url'))
                ->middleware(['web', 'auth'])
                ->name('admin.')
                ->group(base_path('routes/admin/web.php'));
        }
    )
...

After authorization, I redirect to the admin panel.

// app/Http/Controllers/Auth/AuthenticatedSessionController.php

public function store(LoginRequest $request)
{
    $request->authenticate();

    $request->session()->regenerate();

    // return redirect()->intended(route('admin.test')); // don't work

    return Inertia::location(route('admin.test')); // work
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests