CORS headers working for GET but not for POST

Hello,

I’ve used the following snippet suggested in the User Guide to enable CORS headers on all responses:

$app->options('/{routes:.+}', function ($request, $response, $args) {
    return $response;
});

$app->add(function ($req, $res, $next) {
    $response = $next($req, $res);
    return $response
            ->withHeader('Access-Control-Allow-Origin', 'http://localhost')
            ->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
            ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
});

This works fine when sending GET requests from my frontend (in AngularJS) but as soon as I try a POST request, I get the following error in the browser console:

XMLHttpRequest cannot load http://localhost:8888/accounts. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.

When checking in the Network tab of the browser dev console, I see that CORS headers are sent along the response to the OPTIONS request but not for the response of the POST request that follows.

I even tried to add the headers directly on the response of the actual route but no luck:

$app->post('/account/new', function (Request $request, Response $response) {
	$data = $request->getParsedBody();
	$account_data = [];
	$account_data['name'] = filter_var($data['name'], FILTER_SANITIZE_STRING);
    $account_data['market_id'] = filter_var($data['market_id'], FILTER_SANITIZE_NUMBER_INT);
    $account_data['category_id'] = filter_var($data['category_id'], FILTER_SANITIZE_NUMBER_INT);
    $account_data['flag'] = 'TEST_RESPONSE';
    
    $response->withJson($account_data);
    return $response
            ->withHeader('Access-Control-Allow-Origin', 'http://localhost')
            ->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
            ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
});

I’m sure I’m missing something silly, thanks for any help!

Did you managed to fix this? Have the same problem…

Unfortunately I didn’t solve it. I circumvented the problem by operating the frontend and backend on the same domain.
I’d be glad to get an answer on that especially since I think I stuck close to what the snippet suggests on the help page.
Good luck!

Hello,

Have you tried replacing the Access-Control-Allow-Origin header with either http://localhost:8888 or *? E.g.:

->withHeader('Access-Control-Allow-Origin', '*')

Note that the origin (in your case the URL of your Angular JS page) must match the Access-Control-Allow-Origin header in scheme (http/https), domain and port number. For CORS “http://localhost” is not the same as “http://localhost:8888”.

Also, check if the headers are actually being sent to the browser. They should be through the middleware (no need to add them to other routes), but the error message you get seems to indicate otherwise:

No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

Indeed, fair point, I tried opening the gates by setting Access-Control-Allow-Origin header to ‘*’ (just to be sure) but it didn’t solve the problem because, as I said in my original post, the problem is actually that no CORS headers are sent in the response of the POST request.
The CORS headers are sent with the response to the OPTIONS request though.
Thanks again for any help toward the resolution.

Hello,

Thanks for checking.

It is strange that you are not getting the CORS headers from the POST request. I have created a small test, posted below, and I can’t reproduce the missing headers.

Is there perhaps some middleware in your application added before the CORS middleware that is removing the headers that are set by the CORS middleware?

Testing CORS with POST requests
I have set up two PHP built-in webservers. On runs on localhost:8000, the other at localhost:8001. Both running in a separate directory:

http://localhost:8000

<!-- index.html -->
<html>
    <head>
        <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    </head>
    <body>
        <p>Open this page at localhost:8000.</p>
        <div id="response"></div>
        <script>
            $(function() {
                $.ajax('http://localhost:8001/account/new', { method: "POST", data: { name: 'test123'} })
                .done(function(data) { $('#response').text(JSON.stringify(data)); })
                .fail(function(xhr, textStatus) { $('#response').text('Error: ' + textStatus); });
            });
        </script>
    </body>
</html>

http://localhost:8001

<?php

// index.php

use Slim\Http\Request;
use Slim\Http\Response;
use Slim\App;

require_once __DIR__ . '/vendor/autoload.php';

$app = new App();

// CORS
$app->options('/{routes:.+}', function ($request, $response, $args) {
    return $response;
});

$app->add(function ($req, $res, $next) {
    $response = $next($req, $res);
    return $response
            ->withHeader('Access-Control-Allow-Origin', 'http://localhost:8000')
            ->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
            ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
});

// POST call
$app->post('/account/new', function (Request $request, Response $response) {
	$data = $request->getParsedBody();

    $responseData = [
        'name' => $data['name'],
        'flag' => 'TEST_RESPONSE'
    ];

    return $response->withJson($responseData);
});

$app->run();

And the result when I open http://localhost:8000:

Hello,

I’m just reading your reply, thanks for that!
Indeed, I might be doing something wrong with a middleware that could be stripping headers somewhere (I’m still a learning the framework). Actually, I’m also having a hard time implementing a middleware to manage the authentication process.

I’ll try to find some time to setup a similar test with just the basics and I’ll report once done.

Thanks again for your help.

Hi,

I’ve experienced the same, yet I confirm that numerous other POST request were working well before this specific one, so I quickly ruled out the http method being the cause itself.

I haven’t found the root cause, but I know I’ve had error in object creation procedure, that resulted in rendering an empty 200 response in the middle of controller method processing the request, rather than throwing an exception. Didn’t have the time to investigate, but I think it was about calling eloquent’s (many-to-many relation) sync() method on an entity that has not been saved yet.

Of course, the problem disappeared when I’ve fixed the error, but I still suspect a bug in the framework.

Best

This is happening because of the CORS (Cross Origin Resource Sharing) . For every HTTP request to a domain, the browser attaches any HTTP cookies associated with that domain. This is especially useful for authentication, and setting sessions. You are doing an XMLHttpRequest to a different domain than your page is on. So the browser is blocking it as it usually allows a request in the same origin for security reasons. You need to do something different when you want to do a cross-domain request.

JSONP ( JSON with Padding ) is a method commonly used to bypass the cross-domain policies in web browsers. You’re on domain example.com , and you want to make a request to domain example.nett . To do so, you need to cross domain boundaries. JSONP is really a simple trick to overcome the XMLHttpRequest same domain policy. So, instead of using XMLHttpRequest we have to use < script > HTML tags, the ones you usually use to load JavaScript files , in order for JavaScript to get data from another domain.

Localhost

If you need to enable CORS on the server in case of localhost, you need to have the following on request header.

Access-Control-Allow-Origin: http://localhost:9999

1 Like

Thanks a lot. Works for me :grinning:

Hello, I also had same problem. When I was testing over http://localhost (backend) to http://localhost (frontend), it had no problems, but as soon as I deployed backend as HTTPS, GET method was working while POST was not.

After break testing every lines, I had found out problem for me was process env. in middelware. Since I had made .gitignore file to ignore process env file, https server couldn’t access the env document variables.

So yea, after replacing process env variables, it worked all good. I managed to solve it due to your post, thanks bro.

How you do that, i also have the same problem …what can i do ?