CORS Errors When Calling Odoo APIs
When a frontend application (React, Vue, SvelteKit) running on a different domain calls Odoo's API, browsers block the request with:
Access to XMLHttpRequest at 'https://odoo.example.com/api/data'
from origin 'https://app.example.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Response to preflight request doesn't pass access control check:
The 'Access-Control-Allow-Origin' header has a value 'null' that is not equal to the supplied origin.Understanding CORS in Odoo
CORS (Cross-Origin Resource Sharing) is a browser security mechanism. When your frontend at app.example.com calls Odoo at odoo.example.com, the browser requires Odoo to explicitly allow this cross-origin request by returning proper headers.
Odoo does not enable CORS by default. You must configure it at the controller level or through a reverse proxy.
Fix 1: Enable CORS on Odoo Controllers
For custom API endpoints, add the cors parameter to your route decorator:
# In your controller
from odoo import http
from odoo.http import request
class MyAPI(http.Controller):
@http.route('/api/data', type='json', auth='public', cors='*')
def get_data(self, **kwargs):
return {'status': 'ok'}
# Or restrict to specific origins
@http.route('/api/data', type='json', auth='public',
cors='https://app.example.com')
def get_data_restricted(self, **kwargs):
return {'status': 'ok'}Important: Using cors='*' allows ANY domain to call this endpoint. For production APIs handling sensitive data, always specify the exact allowed origin.
Fix 2: Configure CORS via Nginx Reverse Proxy
For broader CORS support without modifying Odoo code, configure your Nginx reverse proxy:
server {
listen 443 ssl;
server_name odoo.example.com;
location /api/ {
# CORS headers
add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' '3600' always;
# Handle preflight requests
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
add_header 'Access-Control-Max-Age' '3600' always;
add_header 'Content-Length' '0';
return 204;
}
proxy_pass http://127.0.0.1:8069;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Fix 3: Preflight Request Failures
Browsers send an OPTIONS preflight request before the actual request. Odoo must respond to OPTIONS with proper headers:
# If using cors parameter on route, Odoo handles OPTIONS automatically
# If using Nginx, ensure OPTIONS returns 204 (see above)
# Common mistake: forgetting 'OPTIONS' in allowed methods
# Wrong:
add_header 'Access-Control-Allow-Methods' 'GET, POST';
# Right:
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';Fix 4: Cookies and Credentials
If your frontend sends cookies (for session auth), additional configuration is needed:
# Frontend: include credentials
fetch('https://odoo.example.com/api/data', {
method: 'POST',
credentials: 'include', // Required for cookies
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', params: {} })
});
# Backend: cors='*' does NOT work with credentials
# You must specify the exact origin
@http.route('/api/data', type='json', auth='user',
cors='https://app.example.com')
# Nginx: must include
add_header 'Access-Control-Allow-Credentials' 'true' always;
# And Access-Control-Allow-Origin must NOT be '*'Fix 5: JSON-RPC Specific Issues
Odoo's JSON-RPC endpoints need the Content-Type header allowed:
# The preflight must allow Content-Type header
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With' always;Debugging CORS Issues
# Check response headers with curl
curl -v -X OPTIONS https://odoo.example.com/api/data \
-H "Origin: https://app.example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type"
# Look for these headers in the response:
# Access-Control-Allow-Origin: https://app.example.com
# Access-Control-Allow-Methods: POST
# Access-Control-Allow-Headers: Content-TypePrevention
DeployMonkey configures CORS policies automatically based on your application architecture. The AI agent sets up proper cross-origin headers for API endpoints and manages reverse proxy CORS configuration.