Skip to the content.

CORS and Cookies with Vue.js and Flask


1. What is CORS?


2. What are Cookies?


πŸͺ Important Cookie Flags

1. HttpOnly


2. Secure


3. SameSite


4. Domain


5. Path


Example (Recommended for Auth Cookies)

For cross-origin requests (different frontend & backend domains):

resp.set_cookie(
    "auth_token",
    "abc123",
    max_age=3600,           # Expires in 1 hour (simpler than datetime)
    httponly=True,          # Protect from XSS
    secure=True,            # Only send over HTTPS (production)
    samesite="None",        # Required for cross-origin cookies
    path="/",
)

For same-site requests (localhost dev, frontend & backend on same origin):

from datetime import datetime, timedelta
resp.set_cookie(
    "auth_token",
    "abc123",
    expires=datetime.utcnow() + timedelta(minutes=1),  # Explicit expiry date
    httponly=True,          # Protect from XSS
    secure=False,           # OK for local HTTP dev
    samesite="Lax",         # Safe for same-site requests
    path="/",
)

Note: Both max_age (relative seconds) and expires (absolute datetime) achieve cookie expiry. Pick whichever reads clearer to you.


Debugging Tip

To check stored cookies:

  1. Open DevTools β†’ Application β†’ Storage β†’ Cookies in Chrome/Edge/Firefox.

  2. Look for:

    • Name/Value
    • Domain
    • Path
    • HttpOnly
    • Secure
    • SameSite

3. Flask Setup (Backend with CORS + Cookies)

Install dependencies

pip install flask flask-cors

Flask app (cors.py)

from flask import Flask, jsonify, request, make_response
from flask_cors import CORS
from datatime import datetime, timedelta

app = Flask(__name__)

# Enable CORS with credentials support
CORS(app, supports_credentials=True,
     origins=["http://127.0.0.1:5500", "http://localhost:5500"])

@app.route("/set-cookie")
def set_cookie():
    resp = make_response(jsonify({"message": "Cookie is set!"}))
    resp.set_cookie(
        "user_token",
        "abc123",
        # max_age=10 # seconds
        expires=datetime.utcnow() + timedelta(minutes=1), # deletes in 1 minute
        httponly=True,    # JS cannot read this cookie
        # secure=False,     # True in production with HTTPS
        secure=True,
        samesite=None,
        # samesite="Lax"    # Works locally for cross-site cookies
    )
    return resp

@app.route("/get-cookie")
def get_cookie():
    token = request.cookies.get("user_token")
    if token:
        return jsonify({"message": "Cookie retrieved!", "token": token})
    return jsonify({"message": "No cookie found!"}), 404

@app.route("/delete-cookie")
def delete_cookie():
    resp = make_response({"msg": "Cookie deleted"})
    resp.delete_cookie("user_token")
    return resp


if __name__ == "__main__":
    app.run(debug=True)

Why this code?


4. Vue.js Setup (Frontend)

Vue Component (app.html)


<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <script src="https://unpkg.com/vue@2/dist/vue.js"></script>
    <title>CORS + Cookies Demo</title>
  </head>
  <body>
    <div id="app">
      <h1>CORS + Cookies Demo</h1>
      <button @click="setCookie">Set Cookie</button>
      <button @click="getCookie">Get Cookie</button>
      <!-- Create a delete button ( task ) -->
      <p>{{ message }}</p>
    </div>

    <script>
      new Vue({
        el: "#app",
        data: {
          message: "hi",
        },
        methods: {
          async setCookie() {
            try {
              const res = await fetch("http://127.0.0.1:5000/set-cookie", {
                method: "GET",
                credentials: "include", // include cookies
              });
              const data = await res.json();
              this.message = data.message;
            } catch {
              this.message = "Error setting cookie!";
            }
          },
          async getCookie() {
            try {
              const res = await fetch("http://127.0.0.1:5000/get-cookie", {
                credentials: "include",
              });
              const data = await res.json();
              this.message = data.message + " (Token: " + data.token + ")";
            } catch {
              this.message = "Error retrieving cookie!";
            }
          },
          async deleteCookie() {
            try {
              const res = await fetch("http://127.0.0.1:5000/delete-cookie", {
                method: "GET",
                credentials: "include",
              });
              const data = await res.json();
              this.message = data.msg;
            } catch {
              this.message = "Error deleting cookie!";
            }
          },
        },
      });
    </script>
  </body>
</html>

Why this code?

Server vs File (Critical for development):

This is the most common reason cookies fail in local dev. The code is correct; the origin was wrong.


5. Where Cookies Are Stored

The frontend (Vue) does not manually send the cookie β€” the browser handles it automatically when credentials: "include" is set.


6. Frontend vs Backend Roles


7. How It Works – Flow

  1. User clicks Set Cookie β†’ Vue calls Flask β†’ Flask responds with Set-Cookie.
  2. Browser stores user_token locally.
  3. User clicks Get Cookie β†’ Vue calls Flask with credentials β†’ browser attaches user_token β†’ Flask reads it and returns token.

8. Key Notes


What Counts as a Different Origin?

Browsers consider these different origins (cookies won't send between them without CORS + SameSite=None):

Frontend Backend Same Origin? Why?
http://localhost:5500 http://localhost:5000 ❌ No Different port
http://127.0.0.1:5500 http://127.0.0.1:5000 ❌ No Different port
http://localhost:5500 http://127.0.0.1:5500 ❌ No Different domain (localhost β‰  127.0.0.1)
http://localhost:5500 https://localhost:5500 ❌ No Different protocol (HTTP β‰  HTTPS)
http://localhost:5500/app http://localhost:5500/api βœ… Yes Same protocol, domain, port (path doesn't matter)

βœ… How to fix host mismatch: Use the same domain everywhere. Pick either localhost or 127.0.0.1 for both frontend and backend, then CORS + cookies will work.


πŸ”Ή Ways to Auto-Delete Cookies (Extra)

  1. Session Cookies (Default)

    • If you don’t set expires or max_age, the cookie will auto-delete when the browser is fully closed.
    resp.set_cookie("auth_token", "abc123")
    

    Auto-deletes when user closes the browser.


  1. Time-Limited Cookies (Max-Age / Expires)

    • Set an explicit lifetime (e.g., 10 seconds).
    resp.set_cookie(
        "auth_token",
        "abc123",
        max_age=10   # seconds
    )
    

    Auto-deletes after 10 seconds (browser will drop it).

    Or with expires:

    from datetime import datetime, timedelta
    resp.set_cookie(
        "auth_token",
        "abc123",
        expires=datetime.utcnow() + timedelta(minutes=1)
    )
    

    Auto-deletes after 1 minute.


  1. Manual Delete (Server-Controlled)

    • If you want the user to log out immediately, you must explicitly call delete_cookie.
    • Auto-delete only works based on time or session, not on events like "logout".

πŸ”Ή Important Notes


πŸ‘‰ So yes, auto-deletion is possible, but only time-based or session-based. For instant removal, you’ll need explicit deletion code.