2

Several questions for the same topic on the net but nothing worked.

I have a serverXYZ running an Angular app, a backend tomcat webapp for authentication, a nginx server. Angular app on port 4200, tomcat app on 8080. Everything is on the same host.

The angular app has a environment.ts file (the commented string is one of my tests, see nginx conf below):

export const environment = {
  production: false,
//  apiUrl: 'http://serverXYZ:444/api'
  apiUrl: 'http://localhost:8080/backend'
};

The nginx conf:

    server {
        listen 444;
    
            server_name serverXYZ;

            location / {
                    proxy_pass http://localhost:4200;
                    
                    //websocket
                    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;
                    proxy_pass_request_headers on;
                    proxy_http_version 1.0;
                    proxy_set_header Upgrade $http_upgrade;
                    proxy_set_header Connection "Upgrade";
                    proxy_read_timeout 120s;
            
                    if ($request_method = 'OPTIONS') {
                            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-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 /api {
                proxy_pass http://localhost:8080/backend;

                if ($request_method = 'OPTIONS') {
                        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-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';
                }

           }
}

The backend tomcat app has this in its web.xml:

...
<!-- Tomcat built in CORS implementation -->
        <!--  https://tomcat.apache.org/tomcat-7.0-doc/config/filter.html -->
        <filter>
                <filter-name>CorsFilter</filter-name>
                <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
                <init-param>
                        <param-name>cors.allowed.headers</param-name>
                        <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization</param-value>
                </init-param>
                <init-param>
                    <param-name>cors.allowed.methods</param-name>
                    <param-value>GET,POST,HEAD,OPTIONS,PUT,DELETE</param-value>
                </init-param>
        </filter>
        <filter-mapping>
                <filter-name>CorsFilter</filter-name>
                <url-pattern>/*</url-pattern>
        </filter-mapping>
        <!-- End of built in CORS implementation -->
...

I use my computer, open the browser, With the described conf if I connect to http://serverXYZ:444 the app shows up but I get CORS error on authentication (Failed CORS request). If instead I use the configuration with the commented string in environment.ts , the browser authentication doesn't say CORS, just 403. Of course the authentication api is tested and working.

I'm clueless, what's wrong? Also, solving this with nginx is my preferred way but not mandatory.

Edit: I add one more info, I'm launching the angular app with "ng serve --disableHostCheck=true" . I don't know if maybe the "--publicHost" option or "--host 0.0.0.0" should be used in this case?

Edit 2: Another thing I see just now, Firefox doesn't give me any error on OPTIONS request, just Cors on POST.

1 Answer 1

0

Have you already seen the famous If is Evil article? The article states that

The only 100% safe things which may be done inside if in a location context are:

return ...;
rewrite ... last;

For the most cases those clarifications are too strict, usually you can safely use any nginx directive from ngx_http_rewrite_module within the if block. However using any other directive including the add_header one is really unsafe and can lead to the unpredictable results. Here is how I would write such a configuration:

map $request_method $route {
    GET        main;
    POST       main;
    OPTIONS    options;
    default    invalid;
}

map $request_method $api {
    GET        api;
    POST       api;
    OPTIONS    options;
    default    invalid;
}

server {
    listen 444;
    server_name serverXYZ;

    location / {
        try_files /dev/null @$route;
    }

    location /api {
        try_files /dev/null @$api;
    }

    location @main {
        # websocket conntection setup
        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;
        proxy_pass_request_headers on;
        proxy_http_version 1.0;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_read_timeout 120s;
        # pass the request
        proxy_pass http://localhost:4200;
        # add response CORS headers
        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 @api {
        # transform the URI        
        rewrite ^/api(.*) /backend$1 break;
        # pass the request
        proxy_pass http://localhost:8080;
        # add response CORS headers
        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 @options {
        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-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
    }

    location @invalid {
        # method not allowed
        add_header Allow "GET, POST, OPTIONS";
        return 405;
    }
}
5
  • Thanks a lot for these info, sadly the behaviour didn't change.
    – glass
    Nov 6, 2020 at 8:22
  • After long debugging, turns out the problem wasn't here but on the tomcat running on server side. In any case your answer has some useful info & example that might help people trying to proxy, so I accept it.
    – glass
    Nov 6, 2020 at 16:51
  • @glass Thanks, have you finished up using nginx config like I proposed in my answer? Nov 6, 2020 at 19:31
  • Yes I did and it works. It avoids the potential issues described in that official nginx article.
    – glass
    Nov 7, 2020 at 17:23
  • @glass Looking closer at HTTP 405 Method Not Allowed specification turns out you should add the Allow header to the response, so I added an additional line to the @invalid location (check the answer edit history). Nov 7, 2020 at 17:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .