Search code examples
ajaxnginxxmlhttprequesthttprequestpreflight

AJAX CORS http request nginx rejection


I've searched all the other related topics but can't find a solution to my specific problem.

I wrote a website containing AJAX http requests (get and put). The server getting those requests is nginx and running under debian.

Everything works perfectly as long as I advise my browser to ignore the Allow-Access-Header rejection. But if I don't let the browser ignore it. This happens:

Failed to load http://***/api/devices/7: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://***' is therefore not allowed access.

This is my nginx configuration file, it's running on a z-wave base station so all the other configuration is related to that:

worker_processes 1;

error_log /dev/null;

events {
    worker_connections  1024;
}

http {
    include mime.types;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    types_hash_max_size 2048;
    keepalive_timeout  65;

client_max_body_size 300M;

    access_log /dev/null;

    upstream hcserver {
        server 127.0.0.1:11111;
        keepalive 15;
    }


    server {
        listen 80;
        server_name localhost;

        proxy_read_timeout 400;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;


        location /api/service/ {
            rewrite ^/api/service/(.*) /services/system/$1.php;
        }

        location /api/service/backups/ {
            rewrite ^/api/service/backups/(.*)$ /services/system/backups.php?id=$1;
        }

        location /api/ {
            proxy_pass http://hcserver;
            proxy_http_version 1.0;
            proxy_set_header Connection "Keep-Alive";
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_buffering off;

            error_page 502 =503 /vendor/en/home/503.html;
        }

        location / {
            root   /var/www/;
            index  index.html index.htm index.php;

if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                #
                # Custom headers and headers various browsers *should* be OK with but aren't
                #
                #
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
                #
                # Tell client that this pre-flight info is valid for 20 days
                #
                add_header 'Access-Control-Max-Age' 1728000;
                add_header 'Content-Type' 'text/plain; charset=utf-8';
                add_header 'Content-Length' 0;
                return 204;
             }
             if ($request_method = 'POST') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
                add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
             }
             if ($request_method = 'GET') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
                add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
             }

            location ~* \.(css|js|html)$ {
                add_header Cache-Control "must-revalidate, max-age=0, max-age:0, no-cache, no-store";
                add_header Pragma no-cache;
                add_header Expires 'Fri, 01 Jan 2010 00:00:00 GMT';
            }

            ssi on;
            ssi_value_length 2056;

            error_page  404              /vendor/404.html;
        }

        location ~* \.sh$ {
            proxy_pass   http://127.0.0.1:8000;
        }

        location ~* \.php$ {
            proxy_pass   http://127.0.0.1:8000;
        }

        location ~* \.php\?.* {
            proxy_pass   http://127.0.0.1:8000;
        }

        rewrite ^/vendor/icons/User(.*) /vendor/icons/userIcons/User$1;
        rewrite ^/vendor/icons/rooms/User(.*) /vendor/icons/rooms/userIcons/User$1;
        rewrite ^/vendor/icons/scena/User(.*) /vendor/icons/scena/userIcons/User$1;
        rewrite ^/vendor/n_vicons/User(.*) /vendor/n_vicons/userIcons/User$1;
        rewrite ^/vendor/data_request(.*)php(.*) /api/mobile$2;
        rewrite ^/vendor/(js/)(.*) /vendor/js/$2 last;
        rewrite ^/vendor/([a-z][a-z]/)(.*) /vendor/$2;
    }
}

kind regards and please do not hack my IP: 127.0.0.1 ;)


Solution

  • The indentation in your config is a little off, but it looks to me like you're only adding the CORS headers inside the location / block. As a starting point, try elevating those headers to the server block, like this:

    server {
        listen 80;
        server_name localhost;
    
        add_header Access-Control-Allow-Origin $http_origin;
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
        add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range";
        add_header Access-Control-Expose-Headers "Content-Length,Content-Range";
        add_header Access-Control-Max-Age 1728000;
    

    Now, these rules will apply to every location block for this server (and for every $request_method as well).

    If you need context-specific rules - different CORS header values for different endpoints, for example - this is still a good place to start because it will validate the catch-all solution. Once you have something working, it's easier to make small changes to your rules while testing to figure out where things break down.

    Hope this helps. If not, an update to your question including a full curl - headers and bodies for both the request and response - would be really helpful.