Reputation: 1266
This question follows this one so some of the text is the same.
The error message on Firefox console when the front end tries to POST JSON data to the back end upon submitting a form.:
"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://backend_domain/anteroom. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing)."
I'm running the Golang back end with a systemd unit and serving it at localhost:12345. Nginx listens at port 80 and passes requests down to it:
listen 80;
server_name backend_domain;
location / {
include proxy_params;
proxy_pass http://localhost:12345/;
}
I'm running the Angular front end as a build (built with --prod
flag) using PM2 with angular-http-server serving it at port 8080. Same as the back end, Nginx does its thing from port 80:
listen 80;
server_name frontend_domain;
location / {
include proxy_params;
proxy_pass http://localhost:8080/;
}
The versions I'm working with: Ubuntu 16.04, PM2 3.3.1, Angular CLI 7.3.4, angular-http-server 1.8.1.
Firefox's network tab in developer's tools reveals the POST request headers:
Host: backend_domain
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: frontend_domain/
Content-Type: text/plain
Content-Length: 111
Origin: frontend_domain
DNT: 1
Connection: keep-alive
And the response headers:
HTTP/1.1 405 Method Not Allowed
Server: nginx/1.10.3 (Ubuntu)
Date: Mon, 11 Mar 2019 21:08:24 GMT
Content-Length: 0
Connection: keep-alive
Strict-Transport-Security: max-age=31536000; includeSubDomains
The actual request that's supposed to go to the back end when I hit the submit button is:
if (val.issues && val.age && val.gender) {
this.profile = JSON.stringify({
age: val.age,
gender: val.gender,
issues: val.issues
});
return this.http
.post(environment.anteroomPOSTURL, this.profile, {
observe: "response"
})
Firefox shows this to successfully trigger, with the JSON showing under the Params tab in Network in dev tools.
This response suggests to me that somehow, Nginx is not passing requests down to the back end at port 12345. Otherwise, it would retrieve and pass the headers from the back end shown below in my Golang code back to the front end, right?
I've read that CORS is a server-side issue. So, I've tried enabling it wherever I've a server, that is, in the back end, Nginx, and angular-http-server.
It's enabled in my Golang code:
func anteroom(res http.ResponseWriter, req *http.Request) {
res.Header().Set("Access-Control-Allow-Origin", "*")
res.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
res.Header().Set("Access-Control-Allow-Headers", "Content-Type")
res.Header().Set("Content-Type", "application/json")
...
}
func main() {
...
# Using Gorilla mux router.
router := mux.NewRouter()
router.HandleFunc("/anteroom", anteroom).Methods("POST, OPTIONS")
}
This successfully enables CORS in development, where serving Golang is just opening its built binary and Angular is served with ng serve
.
The above isn't enough in production. So, I've tried enabling it with angular-http-server. Note the --cors
flag at the end:
pm2 start $(which angular-http-server) --name app -- --path /PATH/TO/DIST -p 8080 --cors
I've also tried enabling it in both the back and front end Nginx files (adapted from here):
location / {
proxy_pass http://localhost:8080; # or 12345 if it's the back end conf
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type';
add_header 'Content-Type' 'application/json';
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type';
add_header 'Content-Type' 'application/json';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type';
}
}
}
Oddly, no matter if the headers are in either Nginx files or not, tcpdump -vvvs 1024 -l -A src host backend_domain | grep 'Access-Control-Allow-Origin:'
produces this:
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
No idea why it repeats 12 times but, anyway, the back end sends the above the moment the front end loads (which means Nginx does successfully pass requests down to port 12345, right?). It doesn't send them when I click the submit button to submit the form. I don't know if this is correct behaviour or if it indicates that something is wrong.
What am I missing?
Update 12 Mar 19, 7.30pm:
As seen above and pointed out by sideshowbarker in the comments, there's a "405 Method Not Allowed" response. I thought at first that this was linked to the CORS issue and also with Nginx. To verify it, I stopped Nginx, and opened my firewall at port 12345 so that I could POST to the Golang back end directly.
To avoid any complication by the same-origin policy, I used cURL to POST from another machine: curl -v -X POST -H 'Content-Type: application/json' -d '{"age":"l","gender":"l","issues":"l"}' http://droplet_IP:12345/anteroom
I got the exact same response: "HTTP/1.1 405 Method Not Allowed".
At this point, my best guess is that the Golang back end isn't allowing POST even though it's explicitly allowed in the code, as seen above. I'm at a loss as to why.
Upvotes: 0
Views: 12635
Reputation: 1266
Primarily, the problem is due to this line for Gorilla mux in main()
in the Golang code:
router.HandleFunc("/anteroom", anteroom).Methods("POST, OPTIONS")
Notice the methods in the back: ("POST, OPTIONS")
. This is wrong. It should be ("POST", "OPTIONS")
. They must be quoted separately.
This is different from when we set the methods in the function for the page with net/http: res.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
. Here, they are quoted together.
I think there may have been diverse issues but it's been such a tiring journey I remember only this last one now.
Upvotes: 2