94

I have created a basic RESTful service with the SLIM PHP framework and now I'm trying to wire it up so that I can access the service from an Angular.js project. I have read that Angular supports CORS out of the box and all I needed to do was add this line: Header set Access-Control-Allow-Origin "*" to my .htaccess file.

I've done this and my REST application is still working (no 500 internal server error from a bad .htaccess) but when I try to test it from test-cors.org it is throwing an error.

Fired XHR event: loadstart
Fired XHR event: readystatechange
Fired XHR event: error

XHR status: 0
XHR status text: 
Fired XHR event: loadend

My .htaccess file looks like this

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ /index.php [QSA,L]
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods: "GET,POST,OPTIONS,DELETE,PUT"

Is there something else I need to add to my .htaccess to get this to work properly or is there another way to enable CORS on my server?

Devin Crossman
  • 7,454
  • 11
  • 64
  • 102

9 Answers9

112

Since I had everything being forwarded to index.php anyway I thought I would try setting the headers in PHP instead of the .htaccess file and it worked! YAY! Here's what I added to index.php for anyone else having this problem.

// Allow from any origin
if (isset($_SERVER['HTTP_ORIGIN'])) {
    // should do a check here to match $_SERVER['HTTP_ORIGIN'] to a
    // whitelist of safe domains
    header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
    header('Access-Control-Allow-Credentials: true');
    header('Access-Control-Max-Age: 86400');    // cache for 1 day
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {

    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
        header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");         

    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
        header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");

}

credit goes to slashingweapon for his answer on this question

Because I'm using Slim I added this route so that OPTIONS requests get a HTTP 200 response

// return HTTP 200 for HTTP OPTIONS requests
$app->map('/:x+', function($x) {
    http_response_code(200);
})->via('OPTIONS');
Devin Crossman
  • 7,454
  • 11
  • 64
  • 102
  • 2
    You save four lives so far, even thought I only needed ```header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");``` . Thank you ! – raph77777 Oct 10 '16 at 16:58
  • 2
    Having reviewed the other answer, I have a concern about your solution. The author slashingweapon included a comment that you removed that prompted you to add in some logic to decide if the origin was a trusted origin. Here you've blindly removed it, accepting any origin. Not good practice. – Joe Jun 15 '18 at 22:18
  • 1
    @Joe good point. I updated the answer with a comment about checking the origin – Devin Crossman Jun 16 '18 at 00:00
105

Should't the .htaccess use add instead of set?

Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Methods: "GET,POST,OPTIONS,DELETE,PUT"
markmarijnissen
  • 5,569
  • 2
  • 28
  • 34
  • 1
    Thank you! this solved my problem! I was using `set` and it didn't work, changing it with add fixed it. For what it's worth, this was done on a wordpress blog, with some other stuff in the .htaccess file as well – BBog Jan 06 '14 at 19:40
  • 1
    The documentation here says "set" http://enable-cors.org/server_apache.html . Probably caused a lot of issues! – Ryan How Jan 01 '17 at 13:02
  • I changed from set to add and still get Response for preflight has invalid HTTP status code 400 ..pls suggest..have described my full post here: https://magento.stackexchange.com/questions/170342/magento-htaccess-response-for-preflight-has-invalid-http-status-code-400 – Sushivam Apr 16 '17 at 06:02
  • For me the first line sufficed. Also, I guess the second line should not have a colon? – kslstn Feb 13 '20 at 10:07
  • Apache doc says is "set" https://httpd.apache.org/docs/2.4/mod/mod_headers.html ? – DrLightman Sep 04 '20 at 11:31
  • @kslstn works with colon as well – Moritz Schmitz v. Hülst Nov 04 '22 at 12:34
37

This is what worked for me:

Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
Shade
  • 9,936
  • 5
  • 60
  • 85
Jack Leon
  • 481
  • 4
  • 3
16

As in this answer Custom HTTP Header for a specific file you can use <File> to enable CORS for a single file with this code:

<Files "index.php">
  Header set Access-Control-Allow-Origin "*"
  Header set Access-Control-Allow-Methods: "GET,POST,OPTIONS,DELETE,PUT"
</Files>

Instead of "*" you can put specific origin (protocol + domain+ optional port).

jcubic
  • 61,973
  • 54
  • 229
  • 402
13

Will be work 100%, Apply in .htaccess:

# Enable cross domain access control
SetEnvIf Origin "^http(s)?://(.+\.)?(1xyz\.com|2xyz\.com)$" REQUEST_ORIGIN=$0
Header always set Access-Control-Allow-Origin %{REQUEST_ORIGIN}e env=REQUEST_ORIGIN
Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Header always set Access-Control-Allow-Headers "x-test-header, Origin, X-Requested-With, Content-Type, Accept"

# Force to request 200 for options
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* / [R=200,L]
levin
  • 149
  • 1
  • 6
  • Would just like to add that it is necessary to edit the SetEnvIf statement, defining which remotes (1xyz.com, 2xyz.com) are allowed CORS. – user1298923 Oct 15 '19 at 17:30
9

It's look like you are using an old version of slim(2.x). You can just add following lines to .htaccess and don't need to do anything in PHP scripts.

# Enable cross domain access control
SetEnvIf Origin "^http(s)?://(.+\.)?(domain_one\.com|domain_two\.net)$" REQUEST_ORIGIN=$0
Header always set Access-Control-Allow-Origin %{REQUEST_ORIGIN}e env=REQUEST_ORIGIN
Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE"
Header always set Access-Control-Allow-Headers: Authorization

# Force to request 200 for options
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* / [R=200,L]
Santanu Brahma
  • 121
  • 1
  • 4
5

Thanks to Devin, I figured out the solution for my SLIM application with multi domain access.

In htaccess:

SetEnvIf Origin "http(s)?://(www\.)?(allowed.domain.one|allowed.domain.two)$" AccessControlAllowOrigin=$0$1
Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header set Access-Control-Allow-Credentials true

in index.php

// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {

    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
        header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");         

    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
        header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
}
// instead of mapping:
$app->options('/(:x+)', function() use ($app) {
    //...return correct headers...
    $app->response->setStatus(200);
});
YakovL
  • 7,557
  • 12
  • 62
  • 102
Karl Adler
  • 15,780
  • 10
  • 70
  • 88
2

For ubuntu users:

You have to activate first the headers module by using the following command:

sudo a2enmod headers

Then restart apache by:

sudo service apache2 restart

Then add the following headers to your htaccess:

Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header set Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
Abdulwehab
  • 1,356
  • 2
  • 10
  • 12
1

I tried @abimelex solution, but in Slim 3.0, mapping the OPTIONS requests goes like:

$app = new \Slim\App();
$app->options('/books/{id}', function ($request, $response, $args) {
    // Return response headers
});

https://www.slimframework.com/docs/objects/router.html#options-route

Rocío García Luque
  • 3,597
  • 31
  • 31