5

Stuck on this problem where I received this error everytime making POST request to my actix-web server.

CORS header 'Access-Control-Allow-Origin' missing

my javascript (VueJs running on localhost:3000) :

let data = //some json data
let xhr = new XMLHttpRequest();
xhr.open("POST", "http://localhost:8080/abc");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onload = () => {
    console.log(xhr.responseText);
}
xhr.send(JSON.stringify(data));

My Actix_Web server (running on localhost:8080) :

#[actix_web::main]
async fn main() {

    HttpServer::new(move || {
        let cors = Cors::default()
        .allowed_origin("http://localhost:3000/")
        .allowed_methods(vec!["GET", "POST"])
        .allowed_header(actix_web::http::header::ACCEPT)
        .allowed_header(actix_web::http::header::CONTENT_TYPE)
        .max_age(3600);

        App::new()
        .wrap(cors)
        .service(myfunc)
    })
    .bind(("0.0.0.0", 8080))
    .unwrap()
    .run()
    .await
    .unwrap();

}

my cargo.toml dependencies

[dependencies]
actix-web = "4"
actix-cors = "0.6.1"
...

Got any idea?

  • What do the response header look like if you inspect them in the developer tools? Is the header indeed not set, or maybe incorrectly so? – Elias Jul 24 '22 at 14:45
  • I'm assuming your Vue.js app does not run on http**s**? That would break CORS also. – Elias Jul 24 '22 at 14:45
  • Nitpick: Just use the `fetch` API. `fetch("localhost:8080", { method: "POST", headers: { "Content-Type": "application/json" }}).then(res => res.json()).then(data => /* ... */)`. – Elias Jul 24 '22 at 14:50
  • Still the same with fetch API. request header is `"Accept":"*/*`, `"Access-Control-Request-Headers":"content-type"`. – Muhammad Najid Jul 24 '22 at 15:38
  • Those look like the request headers. You should look at the **response** headers :) – Elias Jul 24 '22 at 15:38
  • And the fetch API is just more modern and easier to use, shouldn't change anything about the result. – Elias Jul 24 '22 at 15:39
  • Got any way that I can bypass the https for dev? :( – Muhammad Najid Jul 24 '22 at 15:40
  • The Vue.js app shouldn't run on HTTPS by default. – Elias Jul 24 '22 at 15:42
  • there's not much in response header, only `"content-length":"42"` and `"date": "//time now"` . – Muhammad Najid Jul 24 '22 at 15:42
  • Have you tried using `Cors::permissive()` instead of `default`? I've never worked with the framework, so I can't really say fore sure what it's doing. But according to your comment, the `Acess-Control-Allow-Origin` header is actually missing. – Elias Jul 24 '22 at 15:44
  • no improvement with `Cors::permissive()` too. I also tried the `default` with both client and server deployed in the cloud. so both of them is behind https. still got no improvement. – Muhammad Najid Jul 24 '22 at 16:04
  • One thing I missed about the response header is `"Referrer Policy":"strict-origin-when-cross-origin"` – Muhammad Najid Jul 24 '22 at 16:06
  • Update: I tried using `Cors::permissive()` again and it works. Super thankful for that!! Nevertheless, my end goal is to get it working with the `default` method. – Muhammad Najid Jul 24 '22 at 16:32
  • As it would be helpful for you, I'll look into the difference and post an answer. – Elias Jul 24 '22 at 17:35

1 Answers1

5

Okay, so I've done some testing. If you're writing a public API, you probably want to allow all origins. For that you may use the following code:

HttpServer::new(|| {
    let cors = Cors::default().allow_any_origin().send_wildcard();

    App::new().wrap(cors).service(greet)
})

If you're not writing a public API... well, I'm not sure what they want you to do. I've not figured out how to tell the library to send that header. I guess I will look at the code.

UPDATE:

So funny story, this is how you allow specific origins:

let cors = Cors::default()
    .allowed_origin("localhost:3000")
    .allowed_origin("localhost:2020");

BUT, and oh boy, is that but juicy. The Access-Control-Allow-Origin response header is only set when there is a Origin request header. That header is normally added by the browser in certain cases 1. So I did that (using the Developer tools in the browser). What did I get? "Origin is not allowed to make this request". I set my origin header to localhost:3000. Turns out, the arctix library simply discards that header if no protocol was provided... (e.g. http://) (I assume it discards it, if it deems its format invalid). That internally results in the header being the string "null". Which is, checks notes, not in the list of allowed origins.

And now the grand finale:

  1. Your origin header needs to be set to (by either you or the browser): "http://localhost:3000".
  2. Your configuration needs to include: .allowed_origin("http://localhost:3000").

After doing that, the server will happily echo back your origin header in the Access-Control-Allow-Origin header. And it will only send that one.

I've no idea if any of that is what the standard specifies (or not). I encourage you to read through it, and if it doesn't comply, please open an issue on GitHub. I would do it myself, but I'm done with programming for today.

Cheers!

Elias
  • 3,592
  • 2
  • 19
  • 42