Back to topics

The Pitfall of Cross-Origin Preflight — Preflight

Recently, some interfaces in my project required cross-origin requests. Here's what I did:

  1. First, create a new axios instance, which is only responsible for cross-origin requests.
  2. The new instance reuses the interceptors of the previous instance.
  3. When the new instance request fails, the error message is CORS.
  4. Contacted the backend to confirm whether cross-origin was enabled. After getting a positive response, directly using fetch(url) showed no CORS error.

Problem Analysis and Conclusion

  1. Since fetch works but axios (based on XHR) reports an error, it should be an axios configuration issue.
  2. Discovered that when reusing the request interceptor of axios, additional fields were carried in the request header.
  3. Because of the extra fields, the browser sends a preflight request before sending the actual request. This preflight request uses the OPTIONS method, and only after it succeeds does the actual request get sent.
  4. To allow the preflight to pass, simply adding CORS headers is not enough; the server needs to handle the preflight request separately.
  5. The following code comes from ChatGPT. I ran a demo myself to try handling the preflight request.
const express = require('express');
const app = express();

// Middleware to handle preflight requests
app.options('*', (req, res) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Methods', 'GET, POST');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.status(200).send();
});

// Route to handle actual requests
app.get('/api/data', (req, res) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  // Handle business logic and send response
  res.json({ message: 'Hello, world!' });
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});