Search code examples
angulardockeruniversalnginx-reverse-proxyserver-side-rendering

Http failure response for (unknown url) - Angular Universal on Docker


I have the following setup:

Four services in docker-compose.yml: my_nginx, my_client, my_api and my_db.

  1. I use my_nginx as a proxy pass to reach my_client:8000 and my_api:8001 (both are Node.js).
  2. my_client is the Angular application which has server side rendering.
  3. my_api is a node app, which gets data from my_db.

Then, in my windows hosts file I have the same host names I defined in the my_nginx config server blocks:

`127.0.0.1 my-app.local api.my-app.local`

so they can be accessed by host name.

Next, in my angular application I have the api url defined in each environment file, so for the local dev environment it is http://api.my-app.local.

It works perfectly fine when the api request comes from within the browser. But when the request is initiated server side, from the my_client container, I get a very long (many rows) error, but the gist of it (and the commonly used text I could find it by, searching Google) is: message: 'Http failure response for (unknown url): 0 Unknown Error'. However, the other questions I could find didn't answer my issue. They're either not server-rendered, or mentioning CORS or containers not having internet access.

I do allow any client from my api: res.setHeader('Access-Control-Allow-Origin', '*');, CORS is not an issue. Also, if I replaced http://api.my-app.local with http://some-real-website.com, then the request works, it goes through, I'm just not receiving appropriate data because it's not my api, but the point is that my containers are able to access the internet. Jumping into the my_client container and ping api.my-app.local works (showing 127.0.0.1, don't know if this means anything). I don't know why that works, while pinging with http://... doesn't, but I can't replace http://api.my-app.local with api.my-app.local in my Angular app, because then it would make the request to itself, like: http://my-app.local/api.my-app.local.

And lastly, if I ping http://api.my-app.local then I get unknown host. Don't know why, as far as the container is concerned, http://api.my-app.local is on the internet, right?. I mean, I expect things to work the same, no matter who/what/where from tries to access, like:

  1. someone/something accesses the url http://api.my-app.local (can be me/browser, can be a container, whatever)
  2. In the end, that is captured by the host (Windows in my case) and the hosts file redirects it to 127.0.0.1
  3. which then reaches the my_nginx container
  4. which recognises the server name api.my-app.local
  5. and proxies it to my_api container.

Should be very simple.

EDIT:

Docker version: 17.12.0-ce-win47 (15139) stable

docker-compose.yml:

version: '3.4'

services:
  my_nginx:
    container_name: my_nginx
    build:
      context: ./nginx
    image: my_nginx
    ports:
      - '80:80'
      - '443:443' # SSL (not used on 'my-app.local')
    links:
      - my_api
      - my_client

  my_api:
    container_name: my_api
    build:
      context: ./node_api
    image: my_api
    ports:
      - '8001:8001'
    volumes:
      - ./node_api/app:/var/www/api.my-app.com
      - /var/www/api.my-app.com/node_modules
    links:
      - my_db
    depends_on:
      - my_db
    networks:
      default:
        aliases:
          - api.my-app.local
    tty: true
    # command: ["pm2-docker", "pm2.config.json"] < moved to Dockerfile

  my_client:
    container_name: my_client
    build:
      context: ./node_client
    image: my_client
    ports:
      - '8000:8000'
    volumes:
      - ./node_client/app:/var/www/my-app.com
      - /var/www/my-app.com/node_modules # Inside the container, use the node_modules installed there.
    links:
      - my_api
    tty: true
    command: ["pm2-docker", "pm2.config.json"]

  my_db:
    container_name: my_db
    build:
      context: ./mysql
    image: my_db
    restart: always
    ports:
      - '3306:3306'
    volumes:
      - './_space/mysql:/var/lib/mysql'

Nginx config api server block:

server {
    listen 80;
    server_name api.my-app.local;

    charset     utf-8;
    client_max_body_size 4096M;

    location / {
        proxy_pass http://my_api:8001;
        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;
    }

    location ~ /\.ht {
        deny all;
    }
}

And the one for the client is the same, except server_name and proxy_pass directives, of course. I don't know what proxy_http_version, proxy_set_header and proxy_cache_bypass are for, I just copied the config from an online example.


Solution

  • In your docker-compose.yml you need to declare the alias for my_nginx and link for my_client:

    my_nginx:
        networks:
            default:
                aliases:
                    - api.my-app.local
    
    my_client:
        links:
            - my_nginx