Yii2 cors filters error that No 'Access-Control-Allow-Origin' header is present
Finally I have answer for this and probably a fool proof one, though most of the things answered here are good, but nothing worked for me, unless I really understood the issue what is happening.
As some people correctly said, that browser tend to send options in pre-flight, so make sure each of your controller if customized are able to handle option request, if it is not you will keep wandering like me for ages, whats wrong, I have configured everything like what is described in documentations, implemented everything what everybody is saying over the net.
I am just giving you here few examples hope this will help, after implementing all the solutions mentioned above still nothing was working, unless I added a specific function to my controller actionOptions, something like this below
public function actionOptions() {
$header = header('Access-Control-Allow-Origin: *');
}
and modified behaviours simply as described in Yii documentation
public function behaviors() {
$behaviors = parent::behaviors();
// remove authentication filter
$auth = $behaviors['authenticator'];
unset($behaviors['authenticator']);
// add CORS filter
$behaviors['corsFilter'] = [
'class' => \yii\filters\Cors::class,
];
// re-add authentication filter
$behaviors['authenticator'] = $auth;
// avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)
$behaviors['authenticator']['except'] = ['options'];
return $behaviors;
}
And this worked like charm on my chrome browser.
Now, if you have some custome route set, like I had for one of my controller
Example:
GET v1/users/profile You must set another route as OPTIONS v1/users/profile too.
Last but not the least, there could be some instances where one of your action is further customized or just just using standard action like in my case actionIndex
I added the header specifically as following on the top in that action like below for actionIndex
public function actionProfile() {
$header = header('Access-Control-Allow-Origin: *');
$profile = new \app\modules\v1\models\Profile();
return $profile->prepare();
}
public function actionIndex() {
$header = header('Access-Control-Allow-Origin: *');
$profile = new \app\modules\v1\models\Profile();
return $profile->prepare();
}
I hope this will help some of you :).
The problem arises when the frontend runs in a port other than the default port (80).
Then in yii you need to specify the port to work. Example:
frontend runs at http: // localhost: 3000
in Yii cors set:
'Origin' => ['http: // localhost: 3000']
If you do not specify the port (3000), Yii interprets the port by default and the result is:
'Origin' => ['http: // localhost: 80']
Try This :
public static function allowedDomains()
{
return [
// '*', // star allows all domains
'http://localhost:3000',
'http://test2.example.com',
];
}
public function behaviors()
{
return array_merge(parent::behaviors(), [
// For cross-domain AJAX request
'corsFilter' => [
'class' => \yii\filters\Cors::className(),
'cors' => [
// restrict access to domains:
'Origin' => static::allowedDomains(),
'Access-Control-Request-Method' => ['POST'],
'Access-Control-Allow-Credentials' => true,
'Access-Control-Max-Age' => 3600, // Cache (seconds)
],
],
]);
}
Add This Function on your controller .
And One Thing angular2 use OPTION method at the first time For so allow OPTION method also
In case of any problems with CORS headers, I recommend to use following instruction:
Add cors configuration to your controller. For instance:
/** * List of allowed domains. * Note: Restriction works only for AJAX (using CORS, is not secure). * * @return array List of domains, that can access to this API */ public static function allowedDomains() { return [ // '*', // star allows all domains 'http://test1.example.com', 'http://test2.example.com', ]; } /** * @inheritdoc */ public function behaviors() { return array_merge(parent::behaviors(), [ // For cross-domain AJAX request 'corsFilter' => [ 'class' => \yii\filters\Cors::className(), 'cors' => [ // restrict access to domains: 'Origin' => static::allowedDomains(), 'Access-Control-Request-Method' => ['POST'], 'Access-Control-Allow-Credentials' => true, 'Access-Control-Max-Age' => 3600, // Cache (seconds) ], ], ]); }
The above code will add to response special http-headers. Check http-headers using browser debug tools:
Request http header should contain
Origin
. It will be added by browser automatically at Crossdomain AJAX. This http-header can be added also via your JS library. Without this http-headercorsFilter
won't work.POST /api/some-method-name HTTP/1.1 Host: api.example.com Connection: keep-alive Content-Length: 86 Accept: */* Origin: https://my-site.example.com User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Referer: https://my-site.example.com/ Accept-Encoding: gzip, deflate, br Accept-Language: en-GB,en;q=0.8,en-US;q=0.6,ru;q=0.4
Response http headers should contain
Access-Control-*
headers. This http-header will be added bycorsFilter
.HTTP/1.1 200 OK Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: https://my-site.example.com Content-Type: application/json; charset=UTF-8 Date: Fri, 24 Feb 2017 09:21:47 GMT Server: Apache Content-Length: 27 Connection: keep-alive
If you don't see these http headers in response, probably it means that
\yii\filters\Cors
doesn't work or conflicts with other filters.Check other behaviors/filters in controller. Try add
corsFilter
as first behavior. Probably some other behaviors prevents execution ofcorsFilter
.Try to disable CSRF validation for this controller (it may prevent external access):
/** * Controller for API methods. */ class ApiController extends Controller { /** * @var bool See details {@link \yii\web\Controller::$enableCsrfValidation}. */ public $enableCsrfValidation = false; // ... }
If you are using authenticator filter (for example, your controller extends
yii\rest\ActiveController
) the CORS filter has to be applied BEFORE authentication methods. Also authentication has to be disabled for the CORS Preflight requests so that a browser can safely determine whether a request can be made beforehand without the need for sending authentication credentials.use yii\filters\auth\HttpBasicAuth; public function behaviors() { $behaviors = parent::behaviors(); // remove authentication filter $auth = $behaviors['authenticator']; unset($behaviors['authenticator']); // add CORS filter $behaviors['corsFilter'] = [ 'class' => \yii\filters\Cors::className(), ]; // re-add authentication filter $behaviors['authenticator'] = $auth; // avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method) $behaviors['authenticator']['except'] = ['options']; return $behaviors; }
Additionally should be checked your web-server. Probably nginx may require additional configuration, apache can require restarting.
Access-Control-*
headers in response can be added using web-server (see for apache and nginx). But I don't recommend to use this way, because in this case you can't manage http-haders using application.Some useful information can be found here:
- Yii2: How to allow CORS on non-restfull API