Search code examples
node.jsnginxamazon-ec2cors

aws ec2 nginx reverse proxy to nodejs allow cors not working


I tried a couple of examples:

/etc/nginx/conf.d/default.conf

server {
    listen  80;
    server_name ec2ip;

    set $cors_origin "";
    set $cors_cred   "";
    set $cors_header "";
    set $cors_method "";
 
    if ($http_origin ~ '^https?://(localhost|mywebsite\.co\.uk)$') {
      set $cors_origin $http_origin;
      set $cors_cred   true;
      set $cors_header $http_access_control_request_headers;
      set $cors_method $http_access_control_request_method;
    }

    add_header Access-Control-Allow-Origin      $cors_origin;
    add_header Access-Control-Allow-Credentials $cors_cred;
    add_header Access-Control-Allow-Headers     $cors_header;
    add_header Access-Control-Allow-Methods     $cors_method;

    location / {

        # Add proxy headers for cors
        proxy_set_header Access-Control-Allow-Origin      $cors_origin;   
        proxy_set_header Access-Control-Allow-Credentials $cors_cred;     
        proxy_set_header Access-Control-Allow-Headers     $cors_header;   
        proxy_set_header Access-Control-Allow-Methods     $cors_method;
    
        proxy_pass http://localhost:8081; # my nodejs application
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

I can view the home route - ie / because it just renders html I cannot post to /api/users/register because I get cors error, despite the above.

My nodejs application using fastify:

node-server.ts

const server = Fastify();

const indexHtml = fs
  .readFileSync(path.join(__dirname, "/index.html"))
  .toString();

server.get("/", async (request, reply) => {
  console.log('indexHtml', indexHtml);
  reply.type("text/html").send(indexHtml); // <body>Hello from nodejs</body>
});

server.post("/api/users/register", opts, (req, reply) => {
  reply.header("Access-Control-Allow-Origin", "*");
  reply.header("Access-Control-Allow-Methods", "POST");
  reply.header("Access-Control-Allow-Headers",  "*");
  reply.code(201);
  reply.send({ message: "Success" });
});

server.listen({ port: 8081 }, (err, address) => {
 // stuff...
});

visit url http://ec2ip/

hello from nodejs

api request from my app at http://localhost

axios({
  method: 'POST',
  url: 'http://ec2ip/api/users/register',
  data: {},
  headers: {
        'Access-Control-Allow-Origin': '*',
        'Content-Type': 'application/json',
        'Access-Control-Allow-Methods':'POST,OPTIONS',
  },
  withCredentials: true
});

> Error: 
register    CORS error  xhr apiRegister.ts:5    0 B 28 ms   register    204 preflight   


Solution

  • ok I fixed it.

    1. Remove all allow headers from nodejs. Not needed. Going to set only in nginx.
    2. Remove nginx proxy_set_header. Not needed.
    3. If I use withCredentials: true. Then I cannot set the wildcard * in nginx. I have to set specific ip address for allow cors. Also I have to add credentials.
    add_header 'Access-Control-Allow-Credentials' $cors_cred;
    
    1. The if statements weren't working. I also couldn't find a way to log them in my access log to test them so settled with map solution.

    2. When making api request include withCredentials: true

    final nginx config

    # Instead of if statements, declare my variables at the top (within http object)
    # This way I can log them later to test they worked.
    
    map $http_origin $cors_origin {
       ~^https?://localhost$      $http_origin;
       ~^https?://(www)?mysite\.co\.uk$ $http_origin;
       default "";
    }
    map "$cors_origin" $cors_cred {
       ~^$ false;
       default true;
    }
    
    # echo into log to test variables
    log_format upstream_time "$time_local http_origin=$http_origin remote_addr=$remote_addr allow_origin=$cors_origin cred=$cors_cred";
     
    server {
        listen  80;
        server_name my-EC2-PUBLIC-ip;
    
        location / {
    
    
       # Trigger the log into the access_log:
       # tail -f /var/log/nginx/access.log
    
       access_log /var/log/nginx/access.log upstream_time;
       if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' $cors_origin;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Credentials' $cors_cred;
            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-Credentials' $cors_cred always;
            add_header 'Access-Control-Allow-Origin' $cors_origin always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;   
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
         }
    
    
            proxy_pass http://localhost:8081;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }
    
    1. Be sure to restart the node server
    2. Be sure to restart nginx
    3. Repost to test
    4. works