title: Django+Angular CORS not working with POST
body: <p>My Angular4 app (running on <code>http://127.0.0.1:4200</code> development server) is supposed to access a django REST backend on the web. The backend is under my control and is available only via HTTPS (running Apache that tunnels the request to a gunicorn server running on an internal port). Let's say that this is <code>https://example.com/</code>. For historical reasons, logging the user in is done using sessions, because I want the users to be able to also use Django's admin interface after they logged in. The workflow is as follows:</p>
  
  <ul>
  <li>Users opens <code>http://127.0.0.1:4200</code>, I perform a GET request to <code>https://example.com/REST/is_logged_in</code> which returns a 403 when the user isn't logged in via sessions yet, 200 otherwise. In the former case, the user is redirected to <code>https://example.com/login/</code>, rendered by Django's template engine, allowing the user to log in. Once logged in, the user is redirected to <code>http://127.0.0.1:4200</code></li>
  <li>When clicking on some button in my Angular UI, a POST request is performed. This post request fails with 403, even though the preflight OPTIONS request explicitly lists POST as allowed actions.</li>
  </ul>
  
  <p>Here is my CORS configuration in Django:</p>
  
  <pre><code>NG_APP_ABSOLUTE_URL = 'http://127.0.0.1:4200'
  # adapt Django's to Angular's presumed XSRF cookie/header names
  CSRF_COOKIE_NAME = "XSRF-TOKEN"
  CSRF_HEADER_NAME = "HTTP_X_XSRF_TOKEN"
  CORS_ORIGIN_WHITELIST = (
      urlparse(NG_APP_ABSOLUTE_URL).netloc
  )
  CSRF_TRUSTED_ORIGINS = (
      urlparse(NG_APP_ABSOLUTE_URL).netloc
  )
  CORS_ALLOW_HEADERS = default_headers + (
      'x-xsrf-token',
  )
  CORS_ALLOW_CREDENTIALS = True
  </code></pre>
  
  <p>This is what Chrome reports for the (successful, 200) first REST GET request to check whether the user is logged in (after he successfully did) in the response:</p>
  
  <p><code>Access-Control-Allow-Credentials:true
  Access-Control-Allow-Origin:http://127.0.0.1:4200
  Allow:GET, HEAD, OPTIONS
  Connection:close
  Content-Type:application/json
  Date:Wed, 26 Apr 2017 15:09:26 GMT
  Server:gunicorn/19.6.0
  Set-Cookie:XSRF-TOKEN=...; expires=Wed, 25-Apr-2018 15:09:26 GMT; Max-Age=31449600; Path=/
  Transfer-Encoding:chunked
  Vary:Accept,Cookie,Origin
  X-Frame-Options:SAMEORIGIN</code></p>
  
  <p>The corresponding request had this:</p>
  
  <p><code>Cookie:sessionid=...; XSRF-TOKEN=...
  Host:example.com
  Origin:http://127.0.0.1:4200
  Referer:http://127.0.0.1:4200/</code></p>
  
  <p>Now, to the actual problem:</p>
  
  <p>Preflight request:</p>
  
  <pre><code>Request URL:https://example.com/REST/change_user_data/
  Request Method:OPTIONS
  Status Code:200 OK
  Access-Control-Request-Headers:content-type
  Access-Control-Request-Method:POST
  Connection:keep-alive
  Host:example.com
  Origin:http://127.0.0.1:4200
  Referer:http://127.0.0.1:4200/dashboard/account
  </code></pre>
  
  <p>Preflight response:</p>
  
  <pre><code>Access-Control-Allow-Credentials:true
  Access-Control-Allow-Headers:accept, accept-encoding, authorization, content-type, dnt, origin, user-agent, x-csrftoken, x-requested-with, x-xsrf-token
  Access-Control-Allow-Methods:DELETE, GET, OPTIONS, PATCH, POST, PUT
  Access-Control-Allow-Origin:http://127.0.0.1:4200
  Access-Control-Max-Age:86400
  Connection:close
  Content-Length:0
  Content-Type:text/html; charset=utf-8
  Date:Wed, 26 Apr 2017 15:36:56 GMT
  Server:gunicorn/19.6.0
  Vary:Origin
  X-Frame-Options:SAMEORIGIN
  </code></pre>
  
  <p>Now my failing (403) POST request:</p>
  
  <pre><code>Accept:application/json
  Accept-Encoding:gzip, deflate, br
  Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
  Connection:keep-alive
  Content-Length:60
  Content-Type:application/json
  Cookie:sessionid=...; XSRF-TOKEN=...
  Host:example.com
  Origin:http://127.0.0.1:4200
  Referer:http://127.0.0.1:4200/dashboard/account
  </code></pre>
  
  <p>The response headers:</p>
  
  <pre><code>HTTP/1.1 403 Forbidden
  Date: Wed, 26 Apr 2017 15:36:56 GMT
  Server: gunicorn/19.6.0
  Vary: Accept,Cookie,Origin
  X-Frame-Options: SAMEORIGIN
  Content-Type: application/json
  Access-Control-Allow-Credentials: true
  Allow: POST, OPTIONS
  Access-Control-Allow-Origin: http://127.0.0.1:4200
  Set-Cookie: XSRF-TOKEN=...; expires=Wed, 25-Apr-2018 15:36:56 GMT; Max-Age=31449600; Path=/
  Connection: close
  Transfer-Encoding: chunked
  </code></pre>
  
  <p>Why wouldn't this request work? It makes little sense to me!</p>
  
  <p>Best regards!</p>