title: CORS failing somewhere in Docker-Compose Stack w/NGINX - can't find it
body: <p>CORS is the absolute worst. I'm trying to find where in a <code>next.js</code>-><code>NGINX</code>-><code>docker-compose</code>-><code>clojure</code> a POST request is failing it's CORS. I have the following error message with no indication of where in the stack its coming from (yay!):</p>
  
  <pre><code>Cross-Origin Request Blocked: 
  The Same Origin Policy disallows reading the remote resource 
  at http://example.com/back/email. 
  (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
  
  axios error from PostRequest at endpt  /email :  Error: "Network Error"
  </code></pre>
  
  <p>So...I'm going to write out each part of where the request touches and try and show that it should fall through. If anyone can find where in this pipeline the request is failing or CORS is not opened correctly PLEASE let me know. CORS is an antipattern of the highest order.</p>
  
  <p><strong>OK</strong></p>
  
  <p>I have a frontend dockerfile for <code>next.js</code> (the import point is that <code>port 3000</code> is exposed): </p>
  
  <pre><code>FROM node:10
  WORKDIR /usr/src/app
  COPY package*.json ./
  RUN npm install
  COPY . .
  RUN npm run build
  EXPOSE 3000
  CMD [ "npm", "start" ]
  </code></pre>
  
  <p>The backend has a dockerfile for <code>clojure</code> (with <code>port 4000</code> exposed):</p>
  
  <pre><code>FROM java:8-alpine
  WORKDIR /
  COPY ./target/uberjar/ .
  EXPOSE 4000
  CMD java -jar clojure_play-0.1.0-SNAPSHOT-standalone.jar
  </code></pre>
  
  <p>Here is the <code>docker-compose</code> file with both ports <code>3000</code> and <code>4000</code> appropriately mapped and a <code>webserver</code> service that loads NGINX as the reverse proxy:</p>
  
  <pre><code>version: '3'
  
  services:
    frontend:
      build:
        context: .
        dockerfile: Dockerfile
      container_name: frontend
      restart: unless-stopped
      ports: 
        - "3000:3000"
      networks:
        - app-network
  
    backend:
      build:
        context: ./backend/clojure_play
        dockerfile: Dockerfile
      container_name: backend
      restart: unless-stopped
      ports: 
        - "4000:4000"
      networks:
        - app-network
  
  
    webserver:
      image: nginx:mainline-alpine
      container_name: webserver
      restart: unless-stopped
      ports:
        - "80:80"
      volumes:
        - web-root:/var/www/html
        - ./nginx-conf:/etc/nginx/conf.d
      depends_on:
        - frontend
        - backend
      networks:
        - app-network
  
  volumes:
    web-root:
      driver: local
  
  networks:
    app-network:
      driver: bridge  
  </code></pre>
  
  <p>Here is my <code>NGINX</code> config. Notice that I proxy pass to the service names as provided by the network bridge in my docker compose (each mapped to the appropriate port, <code>3000</code> or <code>4000</code>). I also got a little silly with the request headers, but I'm trying to open it up as much as possible:</p>
  
  <pre><code>server {
          listen 80;
          listen [::]:80;
  
          root /var/www/html;
          index index.html index.htm index.nginx-debian.html;
  
          # server_name example.com www.example.com;
          # server_name localhost;
  
  
          server_name example.me www.example.com;
  
          location / { 
            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';
            }
            proxy_pass http://frontend:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
          }
          location /back/ {
            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';
            }
            proxy_pass http://backend:4000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header Host $http_host;
          }
  
  
          location ~ /.well-known/acme-challenge {
                  allow all;
                  root /var/www/html;
          }
  }
  </code></pre>
  
  <p>Finally my backend <code>clojure</code> application has a utility called <code>wrap-cors</code> that is supposed to provide cors support for my site:</p>
  
  <pre><code>(defn -main
    "this is main"
    [&amp; args]
  
    (println "hello there main")
  
    (run-server
     (wrap-cors
       (wrap-json-body my-routes {:keywords? true :bigdecimals? true})
       :access-control-allow-origin [#"http://www.example.me"]
       :access-control-allow-methods [:get :put :post :delete] 
     )
    {:port 4000})
  
    #_(run-server my-routes {:port 8080})
  )
  </code></pre>
  
  <hr>
  
  <hr>
  
  <p>And that's it. </p>
  
  <p>When I make a request on my <code>next.js</code> frontend, I expect that the client's computer will make an http request to <code>www.example.com/back/email</code> which will go into the NGINX proxy, hit the <code>/back/</code> location, send <code>/email</code> to my <code>clojure</code> server and that will then return 200. Instead I'm getting the CORS error above. </p>
  
  <p>Does anyone have any idea on how to debug this? </p>