TL;DR: When a frontend web page is served from http://localhost:5173, sending a CORS request to http://127.0.0.1:8000 results in an incorrect Origin header being sent: Expected Origin header: Origin: http://localhost:5173 Actual Origin header: Origin: http://127.0.0.1 (wrong host, and no port) This happens only when the backend request target is 127.0.0.1:8000. If the target is localhost:8000, the Origin header is correct. Is this a BUG of Google Chrome, or is there a justification for this behavior? Details: I am testing a web application written by myself. The frontend (localhost:5173) sends http request to the backend (http://localhost:8000) when clicking a button The Origin header seems to work differently. If I send an GET request to the backend with target host "http://localhost:8000", the preflight request will be sent with header "Origin: http://localhost:5173", which is expected. However, if I send the request with target host "http://127.0.0.1:8000", the preflight request will be sent with header "Origin: http://127.0.0.1", which is not the correct origin (The correct origin should be "http://localhost:5173"). This caused CORS errors even when setting CORS plugins (preflight requests were sent using wrong Origin header, getting wrong Access-Control-Allow-Origin response headers, and furthermore triggered the CORS error). I am including a very simple example, which is a python file that starts 2 servers (on 5173 and 8000), with 2 buttons on the frontend page. You can click for them and observe its preflight requests, and you will see the incorrect Origin header. My question: Is this a BUG of Google Chrome, or is there a justification for this behavior? Environment settings: MacBook Pro (Apple M3 Max); MacOS Sequoia 15.4.1; Chrome Version 136.0.7103.114 (Official) (arm64) Code to demostrate this: from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse, JSONResponse from fastapi.middleware.cors import CORSMiddleware import uvicorn app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=[ "*", ], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/test") async def test(): return JSONResponse(content={"message": "Success from backend"}) frontend_app = FastAPI() @frontend_app.get("/", response_class=HTMLResponse) async def index(request: Request): html_content = """ <!DOCTYPE html> <html> <head> <title>CORS Test</title> </head> <body> <h1>CORS Origin Test (127.0.0.1 vs localhost)</h1> <button onclick="request127()">Request 127.0.0.1:8000</button> <button onclick="requestLocalhost()">Request localhost:8000</button> <pre id="result"></pre> <script> async function request127() { try { const res = await fetch('http://127.0.0.1:8000/test', { method: 'GET', headers: { authorization: 'Bearer token' } }); const data = await res.json(); document.getElementById('result').innerText = '127.0.0.1 Result: ' + JSON.stringify(data); } catch (err) { console.error(err); document.getElementById('result').innerText = '127.0.0.1 Failed'; } } async function requestLocalhost() { try { const res = await fetch('http://localhost:8000/test', { method: 'GET', headers: { authorization: 'Bearer token' } }); const data = await res.json(); document.getElementById('result').innerText = 'localhost Result: ' + JSON.stringify(data); } catch (err) { console.error(err); document.getElementById('result').innerText = 'localhost Failed'; } } </script> </body> </html> """ return HTMLResponse(content=html_content) def run_backend(): uvicorn.run(app, host="127.0.0.1", port=8000) def run_frontend(): uvicorn.run(frontend_app, host="127.0.0.1", port=5173) if __name__ == "__main__": import multiprocessing p1 = multiprocessing.Process(target=run_backend) p2 = multiprocessing.Process(target=run_frontend) p1.start() p2.start() p1.join() p2.join() To run this code: pip install fastapi uvicorn python test_server.py Continue reading...