Skip to content

Odoo CORS Error — 'Access-Control-Allow-Origin' and Cross-Origin Request Fix

DeployMonkey Team · March 24, 2026 9 min read

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-Type

Prevention

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.