I have created a backend server in Django with Django Rest Framework, and a React frontend. My front retrieves data from the back through APIs. Each app is on a different subdomain of the same domain. I use Cloudflare to manage DNS and for SSL / security.
I have had no problem with GET calls. For POST calls, I send the POST data to the server through a form, and I know it works as there is a change to the database (record created in this instance). However, I have implemented a 'retry until' function using axios and polly-js. This method waits until it receives a 201 CREATED response, otherwise retries.
My problem is that when I submit the form on React, the POST is indeed received and processed by my backend server, but the response is blocked. So after 10-15 seconds, I receive an error message through the console and my 'retry until' method sends another POST request. The response of this second one is not blocked by Chrome, and I receive the 201 status. But the overall effect is that I have now 2 identical records in the database because the first call did not 'receive' the response and retried.
The error in the console I get is:
Access to XMLHttpRequest at 'https://subdomain.domain.io/' from origin 'https://api.domain.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
What I have already done and has not worked:
- I don' think it's a backend problem as the POST goes through and the record created. But I have whitelisted all CORS origins in Django
- I have added the header 'Access-Control-Allow-Origin': '*' to my POST request through axios
- I have manually added the same 'Access-Control-Allow-Origin': '*' header from my Django DRF response.
Both request that I send (first one through form submission, second one through automatic retry) are identical (seen through Chrome network tab):
Accept: application/json, text/plain, */*
Content-Type: application/json;charset=UTF-8
Origin: https://subdomain.domain.io
Referer: https://subdomain.domain.io/path
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36
My POST and retry method:
const postData = (url, data, headers) => {
headers['Access-Control-Allow-Origin'] = "*"
return polly()
.waitAndRetry([100, 200, 400, 1000])
.executeForPromise(async () => {
const rsp = await axios.post(url, data, headers);
if (rsp.status < 210) {
return rsp.data;
}
return Promise.reject(rsp);
});
};
The response I get when the second try succeeds:
access-control-allow-origin: *
allow: GET, POST, HEAD, OPTIONS
cf-ray: 4dd7cbccce256948-CDG
content-length: 364
content-type: application/json
date: Mon, 27 May 2019 11:54:47 GMT
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
server: cloudflare
status: 201
strict-transport-security: max-age=2592000; includeSubDomains; preload
vary: Accept, Origin
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
For reference, the CORS settings in Django
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django_otp.middleware.OTPMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_HEADERS = (
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
'access-control-allow-origin'
)
EDIT
Firefox shows me the response for the 504 GATEWAY TIMEOUT of the first POST request:
cf-ray: 4dd82ac15f42cd97-CDG
content-type: text/html; charset=UTF-8
date: Mon, 27 May 2019 13:00:36 GMT
expect-ct: max-age=604800, report-uri="ht….com/cdn-cgi/beacon/expect-ct"
expires: Thu, 01 Jan 1970 00:00:01 GMT
pragma: no-cache
server: cloudflare
set-cookie: __cfduid=d0a3a9ee872171ada14cb…n=.wisly.io; HttpOnly; Secure
set-cookie: cf_use_ob=0; path=/; expires=Mon, 27-May-19 13:01:06 GMT
strict-transport-security: max-age=2592000; includeSubDomains; preload
x-content-type-options: nosniff
X-Firefox-Spdy: h2
The Access-Control-Allow-Origin is missing, but it is part of my backend code. Could anything be happening with Cloudflare?
The expected result would be that, when I POST through the form, receive the 201 back (which would be accepted and read by Chrome) so that I can
- Show the user the form has been correctly saved to the database
- Not retry the POST, resulting in dual entry.
Thank you!