However, when calling the JWT login endpoint (/wp-json/jwt-auth/v1/token) from my React frontend using fetch(), I encountered:
403 Forbidden errors
CORS errors (especially during login/signup)
After investigating, I learned that this issue is caused by CORS preflight requests (i.e., OPTIONS requests) being blocked or not handled correctly by WordPress or the JWT plugin.
When a browser sends a POST request with certain headers (e.g., Authorization), it first sends an OPTIONS request (called a preflight request) to ask the server if it’s allowed.
If the server does not respond properly to this preflight request, the browser cancels the actual request, resulting in a CORS error — even if your endpoint is valid.
This is common when:
Using JWT auth (needs Authorization header)
Using React, Axios, or Fetch with credentials, headers, or cross-origin settings
You need to explicitly handle OPTIONS requests for JWT routes:
php
Copy
Edit
add_action('rest_api_init', function () {
register_rest_route('jwt-auth/v1', '/token', [
'methods' => 'OPTIONS',
'callback' => function () {
return new WP_REST_Response(null, 204);
},
'permission_callback' => '__return_true',
]);
register_rest_route('jwt-auth/v1', '/token/validate', [
'methods' => 'OPTIONS',
'callback' => function () {
return new WP_REST_Response(null, 204);
},
'permission_callback' => '__return_true',
]);
});
This code tells WordPress to respond with HTTP 204 (No Content) when the browser sends a preflight OPTIONS request to the JWT login/validation endpoints. Without this, your React app’s requests will be blocked before they even reach the real login logic.
Now, allow your frontend to communicate with WordPress by setting CORS headers:
php
Copy
Edit
add_action('init', function () {
header("Access-Control-Allow-Origin: http://localhost:3000");
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
header("Access-Control-Allow-Headers: Authorization, Content-Type");
});
Это гарантирует, что WordPress разрешает запросы с вашего фронта React (http: // localhost: 3000). Это также позволяет получить учетные данные (например, файлы cookie или токены) и критические заголовки, такие как авторизация.
⚠ Примечание: эти заголовки должны быть добавлены рано (init или до любого вывода), или они не будут отправлены. Второй слой поддержки:
apache
copy
edit < /p>
заголовок всегда устанавливает Access-control-allow-olorigin "http: // localhost: 3000"
healler всегда устанавливать Контент-тип "
заголовок всегда устанавливает Access-Control-Allow-Credentials" true "
This adds CORS headers at the server level — especially helpful when using static or cached routes that WordPress might not dynamically control.
Now try logging in from your React app using fetch():
js
Copy
Edit
fetch('http://task-6.local/wp-json/jwt-auth/v1/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: 'admin',
password: 'password'
})
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
This POST request should now succeed because:
The server accepts OPTIONS preflight requests
CORS headers are correctly included in the response`enter code here`
No 403 or browser-blocking errors occur
Formatted by https://st.elmah.io
Подробнее здесь: https://stackoverflow.com/questions/795 ... ndpoint-ma