'Axios getting blocked by laravel 7 cors. No "access-control-allow-origin-header"

I've a backend app working with Laravel 7 and a frontend which works with VueJs. My Laravel app is running on laradock (nginx, postgres etc...) Using Postman the API (Laravel 7) works properly.

This API replies by dns or by ip. http://192.168.0.3:80/api/mobile or http://laraapi.com/api/mobile

Once I'm still developing the VueJs app I'm running it with "npm run serve" which provides two ways to access my app, first by localhost and the second one by IP address. Both of them running on port 8081.

When Axios consume the API which uses the GET verb, everything works fine. When Axios consumes a POST verb than a get error.

Access to XMLHttpRequest at 'http://larapi.com/api/mobile/startorder/' from origin 'http://192.168.0.3:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

By default laravel 7 already have a pre-done configuration for CORS which is provided by "Fruitcake"

So my kernel.php is like that:

    protected $middleware = [
    \Fruitcake\Cors\HandleCors::class,
    \App\Http\Middleware\TrustProxies::class,
    \App\Http\Middleware\CheckForMaintenanceMode::class,
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \App\Http\Middleware\TrimStrings::class,
    \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];

Fruitcake was moved in to be the first one, the tip from another StackOverflow which didn't help anyway.

My cors configuration:

'paths' => ['*'],

'allowed_methods' => ['*'],

'allowed_origins' => ['*'],

'allowed_origins_patterns' => [],

'allowed_headers' => ['*'],

'exposed_headers' => false,

'max_age' => false,

'supports_credentials' => true,

See that supports_credentials and allowed_origins were changed. Anyway, changing allowed_origins to "*" does not work.

I've created another route file named "mobile" and I'm using this one instead of "api.php", the content is:

    Route::group(['middleware' => 'auth:api'], function(){
    Route::namespace('Mobile')->group(function () {
        Route::post('startorder',
            ['as' => 'startorder', 'uses' => 'PRC\PurchaseController@startOrder']);
    });
});

This new file was created because the idea is use api.php for another purpose.

I've tried already to set a proxy on VueJs side but unfortunately, the result was the same one.

Axios call

import { http } from "./config";
    startOrder: (order, user, token) => {
        var data = {
          "order": order,
          "user": user,      
        }
        return http.post("startorder/",data, {
          headers: {
            Authorization: "Bearer " + token,
            "Content-Type": "application/json",
          },
          withCredentials: true,
        });
      }

my config.js

import axios from "axios";

export const http = axios.create({
  baseURL: "http://192.168.0.3:80/api/mobile/",
  withCredentials: true
});

vue.config.js

module.exports = {
    devServer: {
        proxy: "http://192.168.0.3:80/api/mobile/",
        open: process.platform === 'darwin',
        host: '0.0.0.0',
        port: 8081, 
        https: false,
        hotOnly: false,
      },
    chainWebpack: config => {
        config
            .plugin('html')
            .tap(args => {
                args[0].title = 'LaraAPi'
                return args
            })
    }
}

For sure something is missing but actually I don't know which side is wrong anymore after a lot of tries.

I would appreciate it a lot if someone would help with that issue.



Solution 1:[1]

I don't know about this '*' stuff. Remember, this is very bad practice in production!

Access to XMLHttpRequest at 'http://larapi.com/api/mobile/startorder/' from origin 'http://192.168.0.3:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Try to put the failed origin inside cors.php:

'paths' => ['api/*'],

'allowed_origins' => ['http://192.168.0.3:8081', 'http://192.168.0.3:80', 'http://laraapi.com'],

All three origins above are allowed to make requests to this endpoint.

It's always recommended to create an environment variable to better control this type of configurations. If you make it work in development, it will automatically work in production too!

'paths' => ['api/*'],

'allowed_origins' => env('CORS_ALLOWED_ORIGINS'),

.env

CORS_ALLOWED_ORIGINS=http://192.168.0.3:8081,http://192.168.0.3:80,http://laraapi.com

Update your production .env file accordingly.

Solution 2:[2]

cors.php I strongly suggest you change paths

<?php

return [

/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/

'paths' => ['*'],

'allowed_methods' => ['*'],

'allowed_origins' => ['*'],

'allowed_origins_patterns' => [],

'allowed_headers' => ['*'],

'exposed_headers' => false,

'max_age' => false,

'supports_credentials' => false,

];

Kernel.php

    <?php

namespace App\Http;

use App\Http\Middleware\cors;
use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \App\Http\Middleware\TrustProxies::class,
        \App\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];
/**
 * The application's route middleware groups.
 *
 * @var array
 */
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        // \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    'api' => [
        'throttle:60,1',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

/**
 * The application's route middleware.
 *
 * These middleware may be assigned to groups or used individually.
 *
 * @var array
 */
protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
    'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];

}

mobile.php (similar to api.php)

    <?php
header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token, Authorization, Accept,charset,boundary,Content-Length');
header('Access-Control-Allow-Origin: http://192.168.0.4:8081');
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::post('login', 'Mobile\Login\UserController@login');
Route::post('register', 'Mobile\Login\UserController@register');

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});

VueJs Side

//const configureAPI = require('./src/server/configure')

module.exports = {
    devServer: {
        proxy: "http://192.168.0.4:80/api/mobile/",
        open: process.platform === 'darwin',
        host: '0.0.0.0',
        port: 8081, // CHANGE YOUR PORT HERE!
        https: false,
        hotOnly: false,
      }
}

config.js

import axios from "axios";

export const http = axios.create({
  baseURL: "http://192.168.0.4:80/api/mobile/",
  withCredentials: false
});

service.js (consumes the API)

   start: (parameter, token) => {
    var data = {
      parameter: parameter,
      user: user,
    };
    return http.post("start/", data, {
      headers: {
        Authorization: "Bearer " + token,
        "Content-Type": "application/json",
      },
      withCredentials: false,
    });
  },

@Keith Gulbro I hope this helps you to fix that nightmare. Let me know if you need something else.

Solution 3:[3]

Folks, seems the issue has been solved at least for now. I will keep looking for a better solution.

Below, the details how this was solved.

1- remove the \Fruitcake\Cors\HandleCors::class from protected middleware on kernel.php

2 - On the header of api routes file you must set those lines below:

header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token, Authorization, Accept,charset,boundary,Content-Length');
header('Access-Control-Allow-Origin: http://192.168.0.3:8081');

In my case, I removed the wildcard * and put my valid origin. The wildcard is insecure.

3 - I've changed my Axios post method to send withCredentials as false.

export default {
login: data => {
    return http.post("login",data, {
      headers: {
        "Content-Type": "application/json",
      },
      withCredentials: false,
    });
  },

4 - Config and cache were cleared.

php artisan config:clear
php artisan cache:clear

Now the response header is fulfilled correctly and the Access-Control-Allow-Origin' error disappeared. Anyway, might have a better solution using FruitCake, otherwise would make no sense at all to provide an inefficient package.

If someone has a better solution, please share it!

Thank's

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 romulos
Solution 3 romulos