9

I'm trying to make a simple php backend to handle a contact form in another server, but despite adding the proper headers, it keeps giving me the same error message:

XMLHttpRequest cannot load https://php-contact-form-lual.herokuapp.com/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4000' is therefore not allowed access. The response had HTTP status code 404.


This is the ajax request:

$.ajax({
    type: 'POST',
    url: 'https://php-contact-form-lual.herokuapp.com/',
    data: {
            subject: 'subject',
            to: 'receiver',
            name: $('#name').val(),
            email: $('#email').val(),
            msg: $('#msg').val()
            }
    }) // then the callbacks


and this is the php:

<?php

    if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
  // return only the headers and not the content
  // only allow CORS if we're doing a POST - i.e. no saving for now.
  if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']) && $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'] == 'POST') {
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: X-Requested-With');
  }
  exit;
}

// handling the data    
$subject = $_POST['subject'];
$to      = $_POST['to'];
$name    = $_POST['name'];
$email   = $_POST['email'];
$msg     = $_POST['msg'];
$msg     = "DE: " . $name . " (" . $email .")" . "\n\n" . $msg;

mail($to, $subject, $msg);

?>

Notice that the lines of code before the "handling the data" block are taken from this answer, I also tried with the simpler solution presented in the first part of that same answer -found also elsewhere-, and even replacing the asterisk with the specific URL, but the result has been the same :(

Any help would be appreciated :)


Update: log of the things I've tried on the server side (from oldest to current):

// Allow from any origin
if (isset($_SERVER['HTTP_ORIGIN'])) {
    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, OPTIONS");         

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

    exit(0);
}

------------------------------------------

header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST, OPTIONS");

-----------------------------------------

header("Access-Control-Allow-Origin: http://localhost:4000");
header("Access-Control-Allow-Methods: POST, OPTIONS");

-----------------------------------------

header("Access-Control-Allow-Origin: http://localhost:4000");
header("Access-Control-Allow-Methods: POST, OPTIONS, GET");

-----------------------------------------

if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']) && $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'] == 'POST') {
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Headers: X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers');
    }
    exit;
}

------------------------------------------

if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']) && $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'] == 'POST') {
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Headers: X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers');
    }
    exit;
}

// + sending headers though ajax

------------------------------------------

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers');

-------------------------------------------

# created .htaccess file with this line:
Header set Access-Control-Allow-Origin "*" 

------------------------------------------

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS, GET');
header('Access-Control-Allow-Headers: X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers');

---------------------------------------------

header('Access-Control-Allow-Origin: http://localhost:4000');
header('Access-Control-Allow-Methods: POST, OPTIONS, GET');
header('Access-Control-Allow-Headers: X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers');

-----------------------------------------------

if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
  // return only the headers and not the content
  // only allow CORS if we're doing a POST - i.e. no saving for now.
  if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']) && $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'] == 'POST') {
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: X-Requested-With');
  }
  exit;
}

--------------------------------------------------

header('Origin: http://localhost:4000');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');


Aditional info

Request headers

POST / HTTP/1.1
Host: php-contact-form-lual.herokuapp.com
Connection: keep-alive
Content-Length: 88
Accept: */*
Origin: http://localhost:4000
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:4000/contacto/
Accept-Encoding: gzip, deflate, br
Accept-Language: es,en-GB;q=0.8,en;q=0.6,de;q=0.4

Response headers

HTTP/1.1 404 Not Found
Connection: keep-alive
Date: Sat, 17 Dec 2016 16:10:02 GMT
Server: Apache
Content-Length: 198
Content-Type: text/html; charset=iso-8859-1
Via: 1.1 vegur
sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Lual
  • 2,848
  • 1
  • 20
  • 27
  • This is a duplicate query. try taking help from here http://stackoverflow.com/questions/20035101/no-access-control-allow-origin-header-is-present-on-the-requested-resource – Nadeem Dec 17 '16 at 04:32
  • Thanks @Nadeem, but I already came accross that question and the php answer doesn't work and most of the other answers doesn't address the problem directly, but give third-party solutions or solutions with backends not based on php. I'd really like to solve it for php as for intellectual interest, however I'm strongly considering to rewrite the backend in other language. – Lual Dec 17 '16 at 16:33
  • The strange thing is the response headers don't mention your access headers at all. Does heroku filter the headers you can return? – Chris Dec 19 '16 at 15:43
  • Also, can you try just `header("Access-Control-Allow-Origin: *");` nothing else in the script - not a solution, just to narrow down the problem – Chris Dec 19 '16 at 15:55
  • 1
    I see the server return a `404` error; are you sure you have the PHP code above inside the `index.php` file under `https://php-contact-form-lual.herokuapp.com/index.php`? Do you really need `https`? Does the server also accepts single `http` requests and if so, why don't you try to use it without SSL? Also, did you try to pass the data as JSON data using the jQuery `$.ajax` `dataType: "jsonp"` and `JSON.stringify({})` an object for the `$.ajax` `data`? – Christos Lytras Dec 19 '16 at 20:51
  • 2
    @ChristosLytras You are totally right, I got so fixed in the first part of the message (no cors headers...) that I thought that the 404 was consequence of the headers instead of the other way around. After I read this I went to the directory and found out a typo in the index.php name. Please answer officially so I can accept your answer (@Robbie also got it right, but you commented 3 hours before him), thanks a lot – Lual Dec 20 '16 at 15:38
  • @Lual I'm glad that my response was helpful. I just made my comment an answer. Thank you. – Christos Lytras Dec 20 '16 at 15:42

4 Answers4

9

I see that the server is returning a 404 error. This suggests that you do not have the PHP code above inside the index.php file under https://php-contact-form-lual.herokuapp.com/index.php.

Also, consider whether you really need https. Does the server also accept single http requests, and if so, why don't you try to use it without SSL?

Finally, did you try to pass the data as JSON data using the jQuery $.ajax dataType: "jsonp" and JSON.stringify({}) an object for the $.ajax data?

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Christos Lytras
  • 36,310
  • 4
  • 80
  • 113
4

The problem is the 404 status code. It's not even reaching the code you're typing.

  1. Do you have a

    $app->post('/', function() use($app) {
        // This is the route you need to edit.
    });
    
  2. Do you have any "when" or other condition for the route? If so, remove it for now.

  3. You must has configuration specific for https? I also note that you have different settings on http (403) vs. https (404), by default Heroku delivers the same code for both http and https unless you set in config for Silex.

  4. Once you get that working (i.e. not a 404), you'll need to return the Access-Control-Allow-Origin header at the same time as the response (as you have in one of the "what I tried" examples. Having an "exit" after that will actually prevent the content being returned which is not totally helpful. (You need "exit" after redirect/location headers, but not here).


Other notes:

  • If you're trying to create a "simple" PHP backend, why go for Heroku? You're not really coding PHP, but coding Symphony, Silex, Twig and all the other libraries that mean you're in documentation and library overkill.
  • As you are using Heroku, there's a SwiftMailer interface (will help you make the mail() secure!
Machavity
  • 30,841
  • 27
  • 92
  • 100
Robbie
  • 17,605
  • 4
  • 35
  • 72
  • Thanks for your remarks, I chose Heroku because of its reliability. Your first sentence actually gives me the correct perspective, I had ignored the 404 part of the error message because I assumed (wrongly) that the browser gave me that 404 because of the headers -and it turned out to be the other way around :/. I already upvoted your answer and if @ChristosLytras doesn't publish his comment as an answer, I'll accept yours (his comment also pointed me to the 404 and was published before your answer). Again, thanks a lot – Lual Dec 20 '16 at 15:49
  • Glad you sorted it! And don't be concerned about ticking/not ticking - the thank-you comments are much more appreciated as I know it helped. – Robbie Dec 20 '16 at 21:08
1

I've put up an extremely simple test case (assuming some local server, or mac, etc)

File 1: site1/index.php

<script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
<script>
$
    .ajax({
        type: 'POST',
        url: 'http://127.0.0.1:7772',
        data: {
            subject: 'foo bar 123',
        }
    })
    .done(function(data) {
        alert(data);
    });
</script>
Site 1 - sending data

File 2: site2/index.php

<?php

header('Access-Control-Allow-Origin: *');

echo "You posted " . $_POST['subject'];

Boot up both local "servers", if you're on a mac, you can do something like:

cd ./site1/
php -S 127.0.0.1:7771

cd ../site2/
php -S 127.0.0.1:7772

Now go to 127.0.0.1:7771 and you should see a little alert showing the contents of site2.

Now comment out the header line in site 2:

// header('Access-Control-Allow-Origin: *');

And refresh 127.0.0.1:7771 and you should be back at square one with the error of: No 'Access-Control-Allow-Origin' header is present on the requested resource

"Working" response/request headers:

enter image description here

"Not Working" response/request headers:

enter image description here

I stress that you shouldn't add header('Access-Control-Allow-Origin: *'); in a production site. But you need to narrow down the issue, and this should be enough to where the error/misconfiguration is occurring

Chris
  • 54,599
  • 30
  • 149
  • 186
  • Downvote? I have given all the tools the OP needs to be able to answer his own question - which given the information he/she has provided is the most one can do. – Chris Dec 20 '16 at 05:26
  • Hello @Chris, thanks for your answer, I agree with you that this might have clarified my mistake, since the local test would have made me think of other options different from the first part of the error message given by the browser. – Lual Dec 20 '16 at 15:30
0

is there any htaccess file ?

Yes ?

Then can you try this one for test ?

<IfModule mod_headers.c>
SetEnvIfNoCase ORIGIN (.*) ORIGIN=$1
Header always set Access-Control-Allow-Methods "POST, GET, PUT, OPTIONS, PATCH, DELETE"
Header always set Access-Control-Allow-Origin "%{ORIGIN}e"
Header always set Access-Control-Allow-Credentials "true"
Header always set Access-Control-Allow-Headers "X-Accept-Charset,X-Accept,Content-Type"
Header always set P3P "policyref='/w3c/p3p.xml', CP='NOI DSP COR NID CUR ADM DEV OUR BUS'" 
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L,E=HTTP_ORIGIN:%{HTTP:ORIGIN}]    
</IfModule>

No ?

You can convert this htaccess to the php Header tag. Sample conversion here How to convert my htaccess code to php header

Community
  • 1
  • 1
Emre Karataşoğlu
  • 1,649
  • 1
  • 16
  • 25