403 Forbidden error when making an ajax Post request in Django framework

112,295

Solution 1

Because you did not post the csrfmiddlewaretoken, so Django forbid you. this document can help you.

Solution 2

For the lazy guys:

First download cookie: http://plugins.jquery.com/cookie/

Add it to your html:

<script src="{% static 'designer/js/jquery.cookie.js' %}"></script>

Now you can create a working POST request:

var csrftoken = $.cookie('csrftoken');

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

$.ajax(save_url, {
    type : 'POST',
    contentType : 'application/json',
    data : JSON.stringify(canvas),
    success: function () {
        alert("Saved!");
    }

})

Solution 3

The fastest solution if you are not embedding js into your template is:

Put <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script> before your reference to script.js file in your template, then add csrfmiddlewaretoken into your data dictionary:

$.ajax({
            type: 'POST',
            url: somepathname + "do_it/",
            data: {csrfmiddlewaretoken: window.CSRF_TOKEN},
            success: function() {
                console.log("Success!");
            }
        })

If you do embed your js into the template, it's as simple as: data: {csrfmiddlewaretoken: '{{ csrf_token }}'}

Solution 4

I find all previous answers on-spot but let's put things in context.

The 403 forbidden response comes from the CSRF middleware (see Cross Site Request Forgery protection):

By default, a ‘403 Forbidden’ response is sent to the user if an incoming request fails the checks performed by CsrfViewMiddleware.

Many options are available. I would recommend to follow the answer of @fivef in order to make jQuery add the X-CSRFToken header before every AJAX request with $.ajaxSetup.

This answer requires the cookie jQuery plugin. If this is not desirable, another possibility is to add:

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');

BUT: if the setting CSRF_COOKIE_HTTPONLY is set to True, which often happens as the Security middleware recommends so, then the cookie is not there, even if @ensure_csrf_cookie() is used. In this case {% csrf_token %} must be provided in every form, which produces an output such as <input name="csrfmiddlewaretoken" value="cr6O9...FUXf6" type="hidden">. So the csrfToken variable would simply be obtained with:

var csrftoken = $('input[name="csrfmiddlewaretoken"]').val();

Again $.ajaxSetup would be required of course.

Other options which are available but not recommended are to disable the middleware or the csrf protection for the specific form with @csrf_exempt().

Solution 5

data: {"csrfmiddlewaretoken" : "{{csrf_token}}"}

You see "403 (FORBIDDEN)", because you don`t send "csrfmiddlewaretoken" parameter. In template each form has this: {% csrf_token %}. You should add "csrfmiddlewaretoken" to your ajax data dictionary. My example is sending "product_code" and "csrfmiddlewaretoken" to app "basket" view "remove":

$(function(){
    $('.card-body').on('click',function(){
        $.ajax({
          type: "post",
          url: "{% url 'basket:remove'%}",
          data: {"product_code": "07316", "csrfmiddlewaretoken" : "{{csrf_token}}" }
        });
    })
});
Share:
112,295

Related videos on Youtube

Annihilator8080
Author by

Annihilator8080

Updated on July 09, 2022

Comments

  • Annihilator8080
    Annihilator8080 almost 2 years

    I am trying to integrate jquery into a web application I am making with Django framework. I am however having a hard time trying to make a simple ajax call to work. My template file that contains the form html and javascript to handle the ajax call looks like:

    <script type="text/javascript">
    $(document).ready(function() {
    $( "#target" ).submit(function() {
    console.log('Form was submitted');
    $.ajax({
            type: "POST",
            url: "/hello/",  // or just url: "/my-url/path/"
            data: {
                query: $( "#query" ).val()   
            },
            success: function(data) {
                console.log(data);
            }
        });
    return false;
      });   
      })
    </script>
    <form id="target" action="." method="post">{% csrf_token %}
     <input id= "query" type="text" value="Hello there">
     <input type="submit" value="Search Recent Tweets">
    </form>
    

    My views.py that is supposed to handle the ajax call looks like:

     from django.core.context_processors import csrf
     from django.shortcuts import render_to_response
     from django.template.loader import get_template
     from django.template import Context,RequestContext
     from django.views.decorators.csrf import ensure_csrf_cookie
     from django.http import HttpResponse
    
     # access resource
     def hello(request):
      c = {}
      c.update(csrf(request))
      if request.is_ajax():
            t = get_template('template.html')
            #html = t.render(Context({'result': 'hello world'}))
            con = RequestContext(request, {'result': 'hello world'})
            return render_to_response('template.html', c, con)
      else:
            return HttpResponse('Not working!') 
    

    I have tried to follow the official documentation on Cross-Site Request Forgery Protection and also looked at several stackoverflow questions addressing a similar problem. I have included the {% csrf_token %} in my html template file but it still doesn't seem to be working. I get an error in the console suggesting that the ajax call failed:

    POST http://127.0.0.1:8000/hello/ 403 (FORBIDDEN)   
    

    How do I pass the result variable along with my http response and get the ajax call to work smoothly? Any help is deeply appreciated.

    Edit-1

    I wasn't supposedly passing the csrf token along with my post request. SO as per the documentation I added the following code to my template javascript:

    function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
        return cookieValue;
    }
    var csrftoken = getCookie('csrftoken');
    console.log(csrftoken);
    
    //Ajax call
    function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    $.ajaxSetup({
        crossDomain: false, // obviates need for sameOrigin test
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type)) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        }
    });
    

    When I refresh the template html page in the browser, I get null in the console, suggesting that the cookie is not set or not defined. What am I missing?

  • Annihilator8080
    Annihilator8080 over 10 years
    I have updated my question as per your suggestion. I still get the same error. I would appreciate if you can tell me how you got it working.
  • Yohn
    Yohn over 10 years
    "csrfmiddlewaretoken" is a key in your posted data,for example,when you need to ajax login,your posted data may like this: {'username':'username','password':'password','csrfmiddleware‌​token':'CSRF-TOKEN-V‌​ALUE'}
  • steinerkelvin
    steinerkelvin over 10 years
    And you have the middleware 'django.middleware.csrf.CsrfViewMiddleware', in MIDDLEWARE_CLASSES in your settings.py`?
  • Annihilator8080
    Annihilator8080 over 10 years
    Yes, @kelvinss, I had made the necessary changes in settings.py
  • Annihilator8080
    Annihilator8080 over 10 years
    Thanks @PYTY, that was the problem. I wasn't embedding the token.
  • alireza
    alireza about 9 years
    your link not work, link changed to this : docs.djangoproject.com/en/1.7/ref/contrib/csrf/#ajax
  • john
    john over 8 years
    i know this has been a while, but does this still hold true even if you've disabled csrf middleware in settings.py?
  • Airith
    Airith over 7 years
    Finally figured out what was going on through your comment. They should definitely add this to the csrf ajax docs. I had no idea that CSRF_COOKIE_HTTPONLY was breaking my ajax posts.
  • Borgy Manotoy
    Borgy Manotoy almost 7 years
    I followed the above code and I can now send ajax() post, but it does not read my data/parameters. Example data: {"name":"John Birada"}. The name parameter is always null. thanks
  • VIISHRUT MAVANII
    VIISHRUT MAVANII about 5 years
    Hi AndrewPt, please consider explaining what exactly you've done, or minimally provide an explanation of what your code does so that people who read the code understand what you're doing exactly. Thanks!
  • AndrewPt
    AndrewPt about 5 years
    Hi, Vishrut Mavani, I`ve added an explanation!
  • Anupam
    Anupam over 4 years
    perfect and straightforward answer
  • Laxmikant
    Laxmikant over 3 years
    csrf_exempt is not recommended