[SOLVED] Tunneling to Apache container with subdomain sites

Hi everyone,

I’m recently migrating to using Cloudflare tunnels for personal use, and slowly getting things working. There is one part I haven’t succeeded in, however; that is migrating my previous Apache container to be routed through the tunnel.

  • This container was previously the reverse proxy for all my services. This role has now been replaced by the tunnel
  • It has a small number of basic web sites under subdomains (foo.mydomain.com, bar.mydomain.com)
  • I would like to now use the tunnel to sit between the internet and Apache, to access those sites with the same domains from the outside

I’ve currently done this by renaming the sites to have subdomains for the container:

  • foo.mydomain.comfoo.apache
  • bar.mydomain.combar.apache

Then the Docker shared network has these subdomains (foo.apache, bar.apache as aliases to the container apache). Then in my config.yml, I have something like:

  - hostname: foo.mydomain.com
    service: http://foo.apache
  - hostname: bar.mydomain.com
    service: http://bar.apache

cloudflared is also running in a container. If I use curl from that container, I can successfully get the correct sites from Apache:

  • curl http://foo.apache → expected HTML :white_check_mark:
  • curl http://bar.apache → expected HTML :white_check_mark:

So it seems like Apache can do what I want. The problem now is, when I then try to use the tunnel to access the public domains foo.mydomain.com bar.mydomain.com from the internet, I don’t get the expected sites, and actually just get the Apache default sites.

  • curl https://foo.mydomain.com → Apache’s default site :x:
  • curl https://bar.mydomain.com → Apache’s default site :x:

So, once the tunnel is in play, the routing is broken. Can anyone help/suggest why that might be?

Many thanks

So if I understand this correctly, there are 3 docker containers, one for cloudflared and one for each of the subdomains?

I want to recreate this.

Hi. Thanks for your response.

No, there are just two:

  • cloudflared container, connecting to the “outside”
  • Apache container hosting n number of sites on subdomains
    • Previously connected to the open internet through port 443. Now it’s behind the tunnel

The question is about how cloudflared can server the n number of sites. Any way would be fine, really, but it seems like something like the subdomain approach should be possible. It’s just that the tunnel seems to strip the information Apache needs to address the right site

I’m not sufficiently knowledgeable about how the HTTP works here, this may be something fairly obvious…

Preface: I’m not an expert in web servers but am running a similar setup

Can you just confirm that you tried routing both hostnames like the below, Apache serves 2 sites on foo.mydomain.com & bar.mydomain.com and it didn’t work:

 - hostname: foo.mydomain.com
    service: http://apache
  - hostname: bar.mydomain.com
    service: http://apache

The http traffic hitting Apache should contain the requested path and Apache will recognise that and route accordingly. In theory, the Cloudflare tunnel should be transparent when it comes to the traffic that it routes.

I admit that I’m using Traefik, not Apache but I have a fairly large number of subdomains all routed to the same traefik container in Docker and it works just fine without any network changes.

@asher thanks for your reply! Glad to hear you’re doing something similar. In my setup I was using subdomain aliases that point to the same apache host. As a test, I removed those, and just used the hostname, so my config is effectively the same as yours. Same effect - I only get the Apache default site (but I need to double check this).

I tailed other_vhosts_access.log for Apache, and noted that whatever subdomain I curl (through cloudflared) ends up requesting that same host. It could be that something is wrong in the Apache config (also not an expert!), so I’ll see if I can get more logging to work out what that might be

Update: I tried to do more logging with Apache:

curl https://foo.mydomain.com:

+69226:6245b1ff:0|GET / HTTP/1.1|Host:foo.mydomain.com|User-Agent:curl/7.79.1|Accept:*/*|Accept-Encoding:gzip|Cdn-Loop:cloudflare|Cf-Connecting-Ip:83.229.20.89|Cf-Ipcountry:GB|Cf-Ray:6f499019abdb7457-LHR|Cf-Visitor:{"scheme"%3a"https"}|Cf-Warp-Tag-Id:7ff129a5-8df3-4f58-b611-4fe2ef4f8996|Connection:keep-alive|X-Forwarded-For:83.229.20.89|X-Forwarded-Proto:https

curl https://bar.mydomain.com:

+69227:6245b204:0|GET / HTTP/1.1|Host: bar.mydomain.com |User-Agent:curl/7.79.1|Accept:*/*|Accept-Encoding:gzip|Cdn-Loop:cloudflare|Cf-Connecting-Ip:83.229.20.89|Cf-Ipcountry:GB|Cf-Ray:6f49903c7d6088bc-LHR|Cf-Visitor:{"scheme"%3a"https"}|Cf-Warp-Tag-Id:7ff129a5-8df3-4f58-b611-4fe2ef4f8996|Connection:keep-alive|X-Forwarded-For:83.229.20.89|X-Forwarded-Proto:https

This would seem to strongly suggest that the problem is on the Apache side…

Ok: solved.

I found the originRequest:httpHostHeader: and this injects the HTTP Host header, allows Apache to know what I want:

  - hostname: foo.mydomain.com
    service: http://apache
    originRequest:
      httpHostHeader: foo.mydomain.com
  - hostname: bar.mydomain.com
    service: http://apache
    originRequest:
      httpHostHeader: bar.mydomain.com

Interesting that you had to add the Host Header. I was able to do it with docker compose

Config File

tunnel: <>
credentials-file: /etc/cloudflared/<>.json
ingress:
  - hostname: apache1.cyberjake.xyz
    service: http://httpd:80
  - hostname: apache2.cyberjake.xyz
    service: http://httpd:80
  - service: http_status:404

Docker-Compose

version: '3.9'

services:
  cloudflared:
      image: cloudflare/cloudflared:2022.3.4
      user: root
      command: tunnel --origincert cert.cyberjake.pem run
      volumes:
          - './cloudflared:/etc/cloudflared'

  httpd:
    image: httpd:2.4-alpine3.15
    volumes:
      - ./httpd.conf:/usr/local/apache2/conf/httpd.conf:ro
      - ./sub1.html:/usr/local/apache2/htdocs/sub1/index.html:ro
      - ./sub2.html:/usr/local/apache2/htdocs/sub2/index.html:ro

and just added

<VirtualHost *:80>
    ServerName apache1.cyberjake.xyz
    DocumentRoot /usr/local/apache2/htdocs/sub1/
</VirtualHost>

<VirtualHost *:80>
    ServerName apache2.cyberjake.xyz
    DocumentRoot /usr/local/apache2/htdocs/sub2/
</VirtualHost>

to my apache config

1 Like