Two node app making http request through a reverse proxy

Hello there!

I have a problem that is hard to describe and I need your help :slight_smile:

I’ll try to describe it in simple terms. As a new user of this forum I can’t write urls so i’ll replace dots with [dot] but you’ll get it :innocent:

My context

I have a compose file with 2 node apps and a reverse-proxy:

  • A nodejs api
  • An Angular Server Side Renderer project called ssr
  • The reverse-proxy container in place with traefik and this set of rules:
    • All requests to ssr[dot]com goes to the ssr container.
    • All requests to ssr[dot]com/api goes to the api container.

That makes 3 containers.

On my development machine, I edited my /etc/hosts file to point ssr[dot]com to 127.0.0.1 for being able to see my website on this domain. So when I request ssr[dot]com domain through my web browser, the reverse-proxy takes the request and redirect it to the ssr container.

My issue

My issue is that when my ssr container have to make a call to the api the request is made with ssr[dot]com/api/my-endpoint for example.

The thing is that from my ssr container it seems that I can’t access my api because the reverse-proxy does not respond to this url. (That is the point I can’t explain :sweat_smile:). When I execute ping ssr[dot]com I have 127.0.0.1 that respond…

Basically I want that all request made to ssr[dot]com from any of my containers are handled by the reverse proxy. I will not change my website urls to api because it will not work on browser side. How can I achieve this? Also this is an issue only on my machine because of the /etc/hosts… It should not happened on my server.

Thanks a lot!

Please share you compose file that contains the container labels that include the traefik rules. If you don’t use labels, please share the configuration you used to create the rules.

As i understood issue here is most likely caused by container networking. When a container tries to access ssr.com, it’s attempting to access itself because, in the isolated network environment of the container, localhost or 127.0.0.1 refers to the container itself, not to your host machine where your Traefik is running.

To solve this issue, you can use Docker’s built-in DNS which resolves service names to their respective container IPs within a Docker network.

Thanks for your reply!
Here is an adapted version of my compose file that I can share with you:

version: "3.8"

services:

  reverse-proxy:
    image: traefik:v2.9
    container_name: reverse-proxy
    command:
      - "--log.level=${LOG_LEVEL}"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.file.directory=/configuration"
      - "--providers.file.watch=true"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entrypoint.to=web-secure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--entrypoints.web-secure.address=:443"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./traefik:/configuration"
      - "./ssl:/ssl"

  api:
    image: node:18-alpine
    container_name: api
    environment:
      - NODE_ENV=development
      - PORT=3333
      - NODE_TLS_REJECT_UNAUTHORIZED=0
      - SOME_OTHER_ENV_VAR=...
    working_dir: /app
    command: [ "node", "dist/apps/api/main.js" ]
    volumes:
      - .:/app
    expose:
      - "3333"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.entrypoints=web-secure"
      - "traefik.http.routers.api.rule=Host(`ssr.com`) && PathPrefix(`/api`)"
      - "traefik.http.routers.api.tls=true"
      - "traefik.http.routers.api.priority=2"
      - "traefik.http.middlewares.api-stripprefix.stripprefix.prefixes=/api"
      - "traefik.http.routers.api.middlewares=api-stripprefix@docker"
    depends_on:
      - reverse-proxy

  ssr:
    image: node:18-alpine
    container_name: ssr
    working_dir: /app
    command: [ "node", "dist/apps/ssr/server/fr/main.js" ]
    volumes:
      - .:/app
    environment:
      - PORT=8090
      - NODE_TLS_REJECT_UNAUTHORIZED=0
    expose:
      - "8090"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.ssr.entrypoints=web-secure"
      - "traefik.http.routers.ssr.rule=Host(`ssr.com`)"
      - "traefik.http.routers.ssr.tls=true"
      - "traefik.http.routers.ssr.priority=1"
      - "traefik.http.middlewares.ssr-redirectregex.redirectregex.regex=^(https?://[^/]*)[/]?$$"
      - "traefik.http.middlewares.ssr-redirectregex.redirectregex.replacement=$${1}/fr/"
      - "traefik.http.middlewares.srr-redirectregex.redirectregex.permanent=true"
      - "traefik.http.routers.ssr.middlewares=ssr-redirectregex@docker"
    depends_on:
      - api

Thanks for your reply!

My ssr website have to make the api calls with the absolute urls of the endpoints, I can not use the container name to query my api from my frontend. How can I use the built-in DNS that you are talking about?

Thanks!

you can define a custom network in your docker-compose.yml file. All services that need to communicate with each other should be added to this network like following

version: '3'
services:
  api:
    ...
    networks:
      - mynetwork
  ssr:
    ...
    networks:
      - mynetwork
  traefik:
    ...
    networks:
      - mynetwork

networks:
  mynetwork:

then use the service name traefik as the hostname instead of ssr in your application. Docker’s internal DNS resolver will resolve traefik to the IP address of the Traefik container within the mynetwork network.

This way, when your ssr service makes a call to http://traefik/api/my-endpoint , Docker’s DNS resolver will ensure that the call is directed to your Traefik container.

This could work in development environment but for production you might need to apply some other solution with external dns resolver.

It really depends on how the interaction takes place. I assume SSR service some sort of web application, where

  1. the browser is responsible to access the api itself, or
  2. the browser only sends arguments to the SSR service, which then performs back channel communication with the api service directly, renders a response and serves it to the browser.

Only in case 1 traffic is involved. In case 2 you would indeed need what @dataheadless says.

You traeffik rules on the other hand look like case1.