7

I am having a CORS issue with my Django Rest Framework and React app on the same server. I am running Vagrant with an Ubuntu 18 box and NGINX installed (I am assuming this issue will translate to DigitalOcean) I apologize ahead of time if I am providing too much information. DRF is using Supervisor and Gunicorn is on port 8000. I created my React app using create-react-app. I then used npm run build to create the static files.

NGINX Setup:

React Conf

server {
    listen 8080;
    server_name sandbox.dev;

    root /var/sites/sandbox/frontend/build;
    index index.html;
    client_max_body_size 4G;
    location / {
        try_files $uri $uri/ /index.html;
    }

Django Conf

upstream sandbox_server {
    server unix:/var/tmp/gunicorn_sanbox.sock fail_timeout=0;
}
server {
    listen 8000;
    server_name api.sandbox.dev;
    ...
    location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    if (!-f $request_filename) {
        proxy_pass http://sandbox_server;
        break;
    }

Django Setup:

INSTALLED_APPS = [
    ...
    'rest_framework',
    'corsheaders',
    'myapp',
]
MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    ...
]

I have tried the following with no luck

CORS_ORIGIN_ALLOW_ALL = True

and

CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = ('192.168.19.76:8080','localhost:8080',)

React App.js

...
fetch("http://localhost:8000/api/v1/token-auth/", {
    method: 'POST',
    headers: {
       'Content-Type': 'application/json',
    },
    body: JSON.stringify({"email":"test@user.com", "password":"testuser"}),
})

So to state the obvious CORS is correct because the Origin is localhost:8080 which is a different port so it sees it as cross origin. I have tried the different settings with cors origin allow, but it is still the same issue every time. Obviously I am doing something wrong, but I can't see it.

My thoughts are

Option 1

proxy pass using the django nginx conf file and do away with the react nginx conf file, but I don't know what affect that might cause in production or if this is a good idea. Is there a better way?

location /api {
    proxy_set_header X-Forwarded_for $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://sandbox_server;

So finalize my thoughts and my question. After trying the different Django options for CORS I am still getting the CORS error. Why, and is it my NGINX conf files causing it or something else? Will I expect to see this in DigitalOcean?

UPDATE 1

I forgot to add the error. Here is the CORS error

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8000/api/v1/token-auth/. (Reason: CORS request did not succeed).

For those wanting to know the output from the network tab

Host    localhost:8000
Origin  http://192.168.19.76:8080
Pragma  no-cache
Referer http://192.168.19.76:8080/

UPDATE 2 I did test using curl, and everything returned as expected so I know DRF is working corrently.

curl --data "email=test@user.com&password=testuser" http://localhost:8000/api/v1/token-auth/

FINAL UPDATE

Thanks to paulsm4 for all the help and just plain awesomeness.

So, I did abandon django-cors-headers and rolled my own. To answer paulsm4's question, I do not have add_header 'Access-Control-Allow-Origin' '*'; in the NGINX file although I did think about letting NGINX handle CORS vs Django, but never went that far. @paulsm4, this is the proxy_pass I was talking about. The key was adding this block of code to NGINX for the react portion in conjunction with my middleware.

    location /api {
        proxy_set_header X-Forwarded_For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://sandbox_server;

The above code by itself worked, but it did not allow me to whitelist any incoming URLs. Creating my own middleware allowed me to whitelist. I have no idea why django-cors-headers or even django-cors-middleware did not work for me. What was strange was that fetch never made it far enough with those two packages to get response headers and an error of any sorts, other than the CORS error I was asking about. With the middleware I wrote, fetch was able to fully make the call, and return some response headers whether it succeeded or failed.

For future reference, I might revisit NGINX and allowing it to handle CORS. Here is a good link CORS on NGINX

NOTE

To clarify; the only middleware installed besides what Django already includes is the cors middleware. Both Django and React reside on the same server, but with different ports.

  1. api.sandbox.com:8000 is the Django Rest Framework
  2. app.sandbox.com:8080 is the React static files
  3. Django 2.0.2
  4. Python 3.6
  5. django-cors-headers 2.4.0
  6. Vagrant/VirtualBox Ubuntu 18.04
  7. NGINX

Django settings.py

INSTALLED_APPS = [
    ...
    # Third Party
    'rest_framework',
    'corsheaders',
    # My Apps
    'account',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
    'django.middleware.csrf.CsrfViewMiddleware',
    'corsheaders.middleware.CorsPostCsrfMiddleware',
    ...
]
CORS_ORIGIN_WHITELIST = (
    'null',
    '192.168.19.76:8080',
    'localhost:8080',
    'app.sandbox.com:8080'
)

React App.js

    fetch("http://localhost:8000/api/v1/token-auth/", {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            "email": "test@user.com", 
            "password": "testuser"
       }),
    })

So I am at wits end here. It is either django-cors-headers that is not working or it could possibly be NGINX.

vhalik
  • 75
  • 1
  • 6
  • Will this be the same problem on DigitalOcean? – vhalik Jan 25 '19 at 19:40
  • Q: Could you please update your post with 1) the exact error message, 2) the HTTP headers actually being returned by Django? See also: [django-cors-headers not work](https://stackoverflow.com/questions/28046422/), [Handling CORS in Django REST Framework](https://www.techiediaries.com/django-cors/) and https://gist.github.com/Stanback/7145487. – paulsm4 Jan 25 '19 at 19:47
  • @paulsm4 I updated the post to include the error. Sorry, I had totally forgot to add it. – vhalik Jan 25 '19 at 20:23
  • @paulsm4 Thanks for the links. Unfortunately, those were in the list of links I consulted before finally posting here for help. – vhalik Jan 25 '19 at 20:33
  • Please copy/paste the response headers with CORS.header. It might look something like this: `Access-Control-Allow-Origin: http://localhost:4200`. If you don't see it ... well, that's the problem ;) Q: What exactly is on your port 8000? On your port 8080? – paulsm4 Jan 25 '19 at 20:41
  • @paulsm4 Well, I definitely do not see any type of response headers, and only request headers so that looks like the problem. I definitely do not see Access-Control-Allow-Origin anywhere. Port 8000 is the API and 8080 is React – vhalik Jan 25 '19 at 21:48
  • OK - the API is the guy who needs to set the header. If this were Spring Boot/Java (with no intermediate proxy), it could be as simple as adding something like `@CrossOrigin(origins="http://localhost:4200")` in my controller's source. – paulsm4 Jan 25 '19 at 21:52
  • @paulsm4 Wouldn't this be handled by Django when setting CORS_ORIGIN_ALLOW_ALL = True or am I wrong and need to set something else up? – vhalik Jan 25 '19 at 21:59
  • @paulsm4 this is the package I am using in Django https://github.com/ottoyiu/django-cors-headers/ – vhalik Jan 25 '19 at 22:02
  • My "reply" and your "comment" crossed each other. You're already using django-cors-headers. I thought so - good! Your next step is to troubleshoot why it's not working. Maybe ngInx (I doubt it), maybe how you're using it (probably). I've never used it myself, so I can't be of too much more help :( I like `CORS_ORIGIN_ALLOW_ALL = False` and `CORS_ORIGIN_WHITELIST = ('localhost:8080')` (without any trailing comma. Also look here: https://stackoverflow.com/questions/28046422 – paulsm4 Jan 25 '19 at 22:12

1 Answers1

3

We've been exchanging comments; here's my current understanding:

PROBLEM: You have have a Django back-end API (on port 8080) and a React Front end (on port 8000). Both are currently running on localhost, but will ultimately reside on DigitalOcean. The React client is getting Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8000/api/v1/token-auth/. (Reason: CORS request did not succeed).

You need to configure CORS.

The request headers in the HTTP traffic you've captured clearly shows this isn't happening yet.

Some relevant links include:

SUGGESTED NEXT STEP:

If you haven't already, please install and configure django-cors-headers

paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • 1
    I appreciate your help. If you look at my original post under Django Setup you will see I have django-cors-headers installed and configured. Which is the reason why I am confused as to why it is not working. The version is the latest of django-cors-headers which is 2.4.0 – vhalik Jan 25 '19 at 22:14
  • Q: Any luck yet? Failing all else, I would 1) focus on your API: Django and django-cors-headers, 2) turn on maximum trace/debugging logging: https://chrisconlan.com/verbose-logging-output-in-django/, 3) create a minimal Django/React "hello world" with as few middlewares as possible (ideally, start with "django-cors-headers" only) and add stuff back one at a time until CORS "breaks" again (assuming you can get it to work at the outset). – paulsm4 Jan 26 '19 at 00:25
  • No luck. Yes, I am running on a skeleton project with everything at minimum. django-cors-headers absolutely refuses to work. – vhalik Jan 26 '19 at 21:03
  • 1
    I added a NOTE to the original post for everyone hasn't read it yet – vhalik Jan 26 '19 at 21:16
  • Drag - I'm very, very sorry to hear that. SUGGESTION: Have you considered writing your own darn middleware to insert the `Access-Control-Allow-Origin: http://app.sandbox.com:8080` HTTP header that django-cors-headers is failing to do for you? Look here: https://djangobook.com/request-response-objects/. For all I know, maybe it's as simple as `response['Access-Control-Allow-Origin'] = 'http://app.sandbox.com:8080``... – paulsm4 Jan 26 '19 at 23:00
  • lol, I just finished rolling my own middleware and still the same issue. We are both thinking along the same lines! – vhalik Jan 26 '19 at 23:13
  • Yep, literally as simple as `class CorsMiddleware(MiddlewareMixin): def process_response(self, request, response): response['Access-Control-Allow-Origin'] = "*" response['Access-Control-Allow-Methods'] = "POST" return response` – vhalik Jan 26 '19 at 23:16
  • Q: Did it work? Q: If so, do we have any clue why "django-cors-headers" *didn't* work? Q: Do we really care ;)? PS: Feel free to "upvote" my response, if you found it helpful... – paulsm4 Jan 27 '19 at 00:28
  • No, it didn't. I had proxy pass set in NGINX. – vhalik Jan 27 '19 at 05:31
  • "I had proxy pass set in NGINX" - I don't understand what you're trying to say :( Q: Are you still getting "Cross-Origin Request Blocked" in your React app? If "yes", what's the next step? If "no", how did you fix it? Q: Exactly how are you checking the HTTP headers? Chrome Developer Tools/Network tab? Fiddler? Wireshark? Q: You've abandoned django-cors-headers, and you're using your own middleware (at least for now), correct? Plse update your post with your new info. – paulsm4 Jan 27 '19 at 06:03
  • 1
    Please add this to your update: Q: Do you have `add_header 'Access-Control-Allow-Origin' '*';` in your ngInx config file: https://enable-cors.org/server_nginx.html? – paulsm4 Jan 27 '19 at 07:27
  • I really appreciate all your help. You have been awesome and patient. I will update my post and then post an answer. – vhalik Jan 28 '19 at 06:49
  • I wish I had 15 rep so the up-vote was visible, but I did up-vote although it does not show. Your answer was informative and moved me towards the right direction. – vhalik Jan 28 '19 at 07:00
  • Thank you, and thank you for your update. Q: Part of the solution was writing your own middleware (instead of using django-cors-headers), correct? And the other part was "...adding this block of code to NGINX for the react portion" (as shown in your update above), correct? Q: So, although we've still got questions, it's working now: correct? Great job! – paulsm4 Jan 28 '19 at 17:43
  • Yes, absolutely, writing the middleware was definitely part of the solution. The block of code in NGINX working with my middleware allowed me to be able to whitelist which is what I wanted instead of just letting everything through. Everything is working perfectly, but the question does remain; why did django-cors-headers not work? – vhalik Jan 28 '19 at 20:36
  • That's a good question - I have no answer :( Thank you again for your perseverance, your excellent updates ... and for getting everything working. I have no doubt your experience will be helpful to some other Django/ngINX user with a JS front-end like React or Angular! – paulsm4 Jan 28 '19 at 20:44