(Django) CSRF Verification for AJAX requests working in Chrome but not Firefox
Question: As the title states, my (Django) CSRF verification is working in Chrome but
not Firefox and I'd like to know why so I can fix this.

I have this included in the _head_ tag of my **base.html** file from which all
other files in my application extend:

# base.html, bottom of the head tag

    
    
        <script>
        $(document).ready(function() {
    
            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');
            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);
                    }
                }
            });
        });
        </script>
    

And I have this code in a file called **browse.js** which needs to make ajax
requests to my own server.

# browse.js

    
    
    Template = {
      setup : function(){
        Template.events.csrf();
        // etc. etc.
      },
      events: {
        csrf : function(){
          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');
          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);
              }
            }
          });
        },
    //etc. etc.
    }
    //The actual ajax request
    Data = {
      api : {
        ajax_get_listings : function(cb){
          var g, i, o, _ref;
          _ref = [
            $('#ci').val(), 
            $('#co').val(), 
            $('#guests').val()], 
            i = _ref[0], 
            o = _ref[1], 
            g = _ref[2];
          if (g) {
            console.log('getting listings');
            return $.ajax({
              url:'/api/get_listing_items/', 
              type: 'POST',
              datatype:'json',
              data: {
                available_start_date: i,
                available_end_date: o,
                max_guests: g
              }, 
              success: function(d) {
                if (d.listings !== null){
                  Data.listings._results = [];
                  console.log(d);
                  var l = $.parseJSON(
                    $("<textarea/>").html(d.listings).text());
                  console.log(l);
                  data = l;
                  console.log(data);
                  return cb(data);
                }else{ 
                  $('#ct').text('No listings found for your search criteria. Please keep searching!');
                }
              },
            });
          }
        },
      },
    //etc. etc
    }
    

Again, this works fine in Chrome. It only gives me a 403 Forbidden when I am
on Firefox. Here is the traceback:

# Traceback

## Headers

    
    
    view source
    Content-Type    
    text/html
    Date    
    Mon, 19 Oct 2015 22:06:07 GMT
    Server  
    WSGIServer/0.1 Python/2.7.3
    Vary    
    Cookie
    X-Frame-Options 
    SAMEORIGIN
    view source
    Accept  
    */*
    Accept-Encoding 
    gzip, deflate
    Accept-Language 
    en-US,en;q=0.5
    Cache-Control   
    no-cache
    Connection  
    keep-alive
    Content-Length  
    54
    Content-Type    
    application/x-www-form-urlencoded; charset=UTF-8
    Cookie  
    _ga=GA1.1.1619904474.1445292335; _gat=1; TawkConnectionTime=0;      __tawkuuid=e||127.0.0.1||mnW1PFpM4y26O8w
    +2HatshrE3nWV4w3xD7SAtEMYGtV647bMojOwsqzNlPdxYCdB||2;     Tawk_560d98fcc096ea637ec4b8c0=vs15.tawk.to:443
    ||0
    DNT 
    1
    Host    
    127.0.0.1:8008
    Pragma  
    no-cache
    Referer 
    http://127.0.0.1:8008/properties/
    User-Agent  
    Mozilla/5.0 (Windows NT 6.1; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0
    X-CSRFToken 
    null
    X-Requested-With    
    XMLHttpRequest  
    

## Response

    
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta http-equiv="content-type" content="text/html; charset=utf-8">
      <meta name="robots" content="NONE,NOARCHIVE">
      <title>403 Forbidden</title>
      <style type="text/css">
        html * { padding:0; margin:0; }
        body * { padding:10px 20px; }
        body * * { padding:0; }
        body { font:small sans-serif; background:#eee; }
        body>div { border-bottom:1px solid #ddd; }
        h1 { font-weight:normal; margin-bottom:.4em; }
        h1 span { font-size:60%; color:#666; font-weight:normal; }
        #info { background:#f6f6f6; }
        #info ul { margin: 0.5em 4em; }
        #info p, #summary p { padding-top:10px; }
        #summary { background: #ffc; }
        #explanation { background:#eee; border-bottom: 0px none; }
      </style>
    </head>
    <body>
    <div id="summary">
      <h1>Forbidden <span>(403)</span></h1>
      <p>CSRF verification failed. Request aborted.</p>
    
    
      <p>You are seeing this message because this site requires a CSRF cookie     when submitting forms.       This
     cookie is required for security reasons, to ensure that your browser is not     being hijacked by third
     parties.</p>
          <p>If you have configured your browser to disable cookies, please re-enable them, at least for this
     site, or for &#39;same-origin&#39; requests.</p>
    
    </div>
    
    <div id="info">
      <h2>Help</h2>
        <p>Reason given for failure:</p>
        <pre>
          CSRF cookie not set.
        </pre>
        <p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when
      <a
      href='http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ref-contrib-   csrf'>Django's
      CSRF mechanism</a> has not been used correctly.  For POST forms, you need to
      ensure:</p>
    
          <ul>
          <li>Your browser is accepting cookies.</li>
    
        <li>The view function uses <a
         href='http://docs.djangoproject.com/en/dev/ref/templates/api/#subclassing-  context-requestcontext'
    ><code>RequestContext</code></a>
        for the template, instead of <code>Context</code>.</li>
    
        <li>In the template, there is a <code>{% csrf_token
    %}</code> template tag inside each POST form that
    targets an internal URL.</li>
    
       <li>If you are not using <code>CsrfViewMiddleware</code>, then you must   use <code>csrf_protect</code> on any views that use the <code>csrf_token</code>
    template tag, as well as those that accept the POST data.</li>
    </ul><p>You're seeing the help section of this page because you have <code>DEBUG =
      True</code> in your Django settings file. Change that to   <code>False</code>,
     and only the initial error message will be displayed.  </p>
    
       <p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p>
    </div>
    
    </body>
    </html>
    

What could be wrong?

* * *

SOLVED: I put @ensure_csrf_cookie on the view that was getting the cookie (NOT
the function the ajax request was calling -- this had confused me). No more
403s on Firefox now. Yay


Answer: In your request headers, I see:

> X-CSRFToken null

So my guess is that the cookie is being set in Firefox. Perhaps it was already
set in Chrome from a previous session.

[The Django docs explain one reason why this may
be](https://docs.djangoproject.com/en/1.8/ref/csrf/#ajax):

> Warning
>
> If your view is not rendering a template containing the csrf_token template
> tag, Django might not set the CSRF token cookie. This is common in cases
> where forms are dynamically added to the page. To address this case, Django
> provides a view decorator which forces setting of the cookie:
> ensure_csrf_cookie().

Try importing the `ensure_csrf_cookie` decorator in your views.py and wrapping
your base view with it. Ex:

    
    
    from django.views.decorators.csrf import ensure_csrf_cookie
    
    @ensure_csrf_cookie
    def base_view(request):
        # do stuff
        return render('base.html', {...})
    

I'm not sure if this is the root issue, but I hope this helps!

