CSharp
CSharp

Reputation: 1476

Can't serve static assets from docker containers behind Nginx reverse proxy

I'm trying to use Nginx as a reverse proxy to serve two containers. Here is a part of my Nginx conf file:

upstream dashboard {
    server dashboard:80;
}

upstream editor {
    server editor:80;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass         http://dashboard;
    }

    location /editor/ {
        rewrite ^/editor(.*)$ $1 break;
        proxy_pass         http://editor;
    } 

I'm getting 404 errors when I navigate to the /editor url in my browser because the page is submitting requests for static resources that reside in the upstream container, "editor".

enter image description here

I'm pretty new to Nginx but I presume when it receives a request with the url: http://example.com/static/css/2.3d394414.chunk.css

Nginx has no way of knowing that the corresponding css is located inside the editor container. How can I amend the configuration to fix this problem? I've seen some configurations which provide a common path to any static assets but I need a solution that can handle assets within docker containers.

Upvotes: 5

Views: 3843

Answers (4)

Game Terserah
Game Terserah

Reputation: 379

Actually, for easy configuration, you can follow this:

upstream dashboard {
    server dashboard:80;
}

upstream editor {
    server editor:80;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass         http://dashboard;
    }

    location /editor/ {
        proxy_pass         http://editor;
        proxy_set_header Accept-Encoding "";
        sub_filter "/static" "/editor/static;
        sub_filter_once off;
    }

sub_filter function will change example.com/static/ become example.com/editor/static, but for best configuration you must make one path for all static files. Like:

/var/data/build/dashboard/.. (can be css, js)

/var/data/build/editor/.. (can be css, js)

I think use sub_filter is good while the app doesn't have many URLs to rewrite with, I hope this helps.

Upvotes: 0

CSharp
CSharp

Reputation: 1476

In case anyone else encounters the same problem, here's an additional answer as well as the one posted by @b0gusb. This is a solution when you have docker containers as the upstream applications. dashboard and editor, for example, are containers comprising of create-react-app applications and an nginx server.

Firstly, change the directory where the index.html file, generated by create-react-app, searches of the static assets by setting the homepage field in the package.json:

{
  "name": "dashboard",
  "homepage": "https://example.com/dashboard",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.9.0",
    "react-dom": "^16.9.0",
    "react-scripts": "3.1.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Using the current latest version of react-scripts (3.1.1) this will generate an index.html file with links to your static assets which are expected to reside inside the dashboard directory (or whatever name you choose after the slash in the homepage field).

Now in your docker file you need to do some moving of build assets so that the links in the index.html do not break. The following is my Dockerfile:

FROM node:12.2.0-alpine as builder

WORKDIR /usr/src/app

ENV PATH /usr/src/app/node_modules/.bin:$PATH

COPY package.json .

RUN npm install [email protected] -g
RUN npm install

COPY . .

RUN npm run build

FROM nginx:1.17.2

COPY --from=builder /usr/src/app/build/ /usr/share/nginx/html/dashboard
COPY --from=builder /usr/src/app/build/*.html /usr/share/nginx/html

EXPOSE 80

CMD [ "nginx", "-g", "daemon off;" ] 

Notice that the static assets generated by create-react-app build, are located inside the dashboard directory and the index.html in the /usr/share/nginx/html directory. Now your nginx reverse proxy can differentiate requests for different static assets of your various containers:

    # location to handle calls by the editor app for assets
    location /editor/ {
        proxy_pass http://editor/editor/;
    }

    # location to handle calls by the dashboard app for assets
    location /dashboard/ {
        proxy_pass http://dashboard/dashboard/;
    }

    # location to handle navigation to the editor app
    location /editor-path/ {
        access_log /var/logs/nginx/access.log;
        rewrite ^/editor-path(.*)$ $1 break;
        proxy_pass         http://editor/;
    }        

    # location to handle calls to the rest/graphql api
    location /api/ {
        access_log /var/logs/nginx/access.log;
        rewrite ^/api(.*)$ $1 break;
        proxy_pass         http://restserver/;
    }

    # location to handle navigation to the dashboard app
    location / {
        access_log /var/logs/nginx/access.log;
        proxy_pass         http://dashboard/;
    }

Upvotes: 2

b0gusb
b0gusb

Reputation: 4721

If I understood correctly you have static resources on editor and dashboard upstreams and in both cases the URL is the same /static/some.resource Because you cannot differentiate based on the URL you could configure nginx to try if the file exists on dashboard first and proxy the request to editor if not found.

 upstream editor {
    server editor:80;
   }

   upstream dashboard {
     server dashboard:80;
   }

   server {
     location /static {
     # Send 404s to editor
     error_page 404 = @editor;
     proxy_intercept_errors on;

     proxy_pass http://dashboard

     }

     location @editor {
       # If dashboard does not have the file try with editor
       proxy_pass http://editor
     }
   }

See also nginx – try files on multiple named location or server

Hope it helps.

Upvotes: 1

Andrea
Andrea

Reputation: 335

put the /editor configuration above the / configuration.

Nginx perform checks from top to bottom, so it's possible that having the root configuration ( / ) on top will route all contents to the wrong server.

Switch the blocks location and restart/reload nginx configuration.

Upvotes: 0

Related Questions