Routing a webpack app results in 404 not found on direct url access

1. Caddy version (caddy version): v2.4.5

2. How I run Caddy:

c. Service/unit/compose file:

FROM caddy:alpine
COPY ./dist /usr/share/caddy
COPY Caddyfile /etc/caddy/Caddyfile

d. My complete Caddyfile or JSON config:

cs.jakubdobos.eu {
        root * /usr/share/caddy
        route /api* {
                uri strip_prefix /api
                reverse_proxy 172.19.0.3:3301
        }
        route {
                file_server
                try_files {path} /index.html
        }
}

a. System environment:

Host OS: alpine-standard-3.14.2-x86_64
Docker: version 20.10.9, build c2ea9bc90bacf19bdbe37fd13eec8772432aca99

b. Command:

it autostarts in the container that I get from the dockerfile specified up

3. The problem I’m having:

curl -v https://cs.jakubdobos.eu/matches

  • Trying 95.102.135.240:443…
  • Connected to cs.jakubdobos.eu (95.102.135.240) port 443 (#0)
  • ALPN, offering h2
  • ALPN, offering http/1.1
  • successfully set certificate verify locations:
  • CAfile: /etc/ssl/certs/ca-certificates.crt
  • CApath: none
  • TLSv1.3 (OUT), TLS handshake, Client hello (1):
  • TLSv1.3 (IN), TLS handshake, Server hello (2):
  • TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
  • TLSv1.3 (IN), TLS handshake, Certificate (11):
  • TLSv1.3 (IN), TLS handshake, CERT verify (15):
  • TLSv1.3 (IN), TLS handshake, Finished (20):
  • TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
  • TLSv1.3 (OUT), TLS handshake, Finished (20):
  • SSL connection using TLSv1.3 / TLS_CHACHA20_POLY1305_SHA256
  • ALPN, server accepted to use h2
  • Server certificate:
  • subject: CN=cs.jakubdobos.eu
  • start date: Oct 10 09:57:59 2021 GMT
  • expire date: Jan 8 09:57:58 2022 GMT
  • subjectAltName: host “cs.jakubdobos.eu” matched cert’s “cs.jakubdobos.eu”
  • issuer: C=US; O=Let’s Encrypt; CN=R3
  • SSL certificate verify ok.
  • Using HTTP2, server supports multiplexing
  • Connection state changed (HTTP/2 confirmed)
  • Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
  • Using Stream ID: 1 (easy handle 0x7f9b01f7ba90)

GET /matches HTTP/2
Host: cs.jakubdobos.eu
user-agent: curl/7.79.1
accept: /

  • TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
  • Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
    < HTTP/2 404
    < server: Caddy
    < content-length: 0
    < date: Sun, 10 Oct 2021 13:38:28 GMT
    <
  • Connection #0 to host cs.jakubdobos.eu left intact

curl -v https://cs.jakubdobos.eu

  • Trying 95.102.135.240:443…
  • Connected to cs.jakubdobos.eu (95.102.135.240) port 443 (#0)
  • ALPN, offering h2
  • ALPN, offering http/1.1
  • successfully set certificate verify locations:
  • CAfile: /etc/ssl/certs/ca-certificates.crt
  • CApath: none
  • TLSv1.3 (OUT), TLS handshake, Client hello (1):
  • TLSv1.3 (IN), TLS handshake, Server hello (2):
  • TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
  • TLSv1.3 (IN), TLS handshake, Certificate (11):
  • TLSv1.3 (IN), TLS handshake, CERT verify (15):
  • TLSv1.3 (IN), TLS handshake, Finished (20):
  • TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
  • TLSv1.3 (OUT), TLS handshake, Finished (20):
  • SSL connection using TLSv1.3 / TLS_CHACHA20_POLY1305_SHA256
  • ALPN, server accepted to use h2
  • Server certificate:
  • subject: CN=cs.jakubdobos.eu
  • start date: Oct 10 09:57:59 2021 GMT
  • expire date: Jan 8 09:57:58 2022 GMT
  • subjectAltName: host “cs.jakubdobos.eu” matched cert’s “cs.jakubdobos.eu”
  • issuer: C=US; O=Let’s Encrypt; CN=R3
  • SSL certificate verify ok.
  • Using HTTP2, server supports multiplexing
  • Connection state changed (HTTP/2 confirmed)
  • Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
  • Using Stream ID: 1 (easy handle 0x7f0f40146a90)

GET / HTTP/2
Host: cs.jakubdobos.eu
user-agent: curl/7.79.1
accept: /

  • TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
  • Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
    < HTTP/2 200
    < accept-ranges: bytes
    < content-type: text/html; charset=utf-8
    < etag: “r0rc3t24y”
    < last-modified: Sun, 10 Oct 2021 10:35:53 GMT
    < server: Caddy
    < content-length: 2770
    < date: Sun, 10 Oct 2021 13:39:32 GMT
    <
G5VWe're sorry but g5v doesn't work properly without JavaScript enabled. Please enable it to continue.
* Connection #0 to host cs.jakubdobos.eu left intact

4. Error messages and/or full log output:

5. What I already tried:

So, I swapped things around in the Caddyfile to familiarize myself more with what each directive is doing. I read the docs a few times through and through already and I looked into how the webpack app handles routes, which I haven’t been able to make out, yet. That is why I am turning to this forum, as I hope there lies someone here, who will be able to help me sort this out. The app works, when you do not need to reach any direct links. So worst-case scenario: I will keep using it as-is. But I think my issue is just a dumb one, and I can not properly word what exactly I am experiencing in this setup.

6. Links to relevant resources:

The website: https://cs.jakubdobos.eu

A request for /matches should be handled by this configuration block:

I will note here that, because “Directives contained in a route block will not be reordered internally” (source: route (Caddyfile directive) — Caddy Documentation), the try_files directive will never excute (since file_server is a terminating handler - it “finishes” the request, returning a result and ending the processing chain).

So, if you’re relying on try_files here to rewrite to /index.html in order to make the request to /matches viable, you’ll need to change the order in this block.

If that’s not the case, and there is some /matches file on disk, and it’s not being served, we might have a different problem.

3 Likes

Alright, I will try and let you know how it goes

1 Like

Thank you, this solved it:
Final config:

cs.jakubdobos.eu {
        root * /usr/share/caddy
        route /api* {
                uri strip_prefix /api
                reverse_proxy 172.19.0.4:3301
        }
        route {
                try_files {path} /index.html
                file_server
        }
}
2 Likes

It’s a bit simpler to use handle and handle_path instead of route for this:

cs.jakubdobos.eu {
    handle_path /api* {
        reverse_proxy 172.19.0.4:3301
    }
    handle {
        root * /usr/share/caddy 
        try_files {path} /index.html
        file_server
    }
}

The handle_path directive has built-in uri strip_prefix logic, so it saves you a line. Then you can move root to the other handle block because it’s not necessary for root to be defined for your proxy (very tiny optimization, nanoseconds at best, but whatever). And using handle makes the two blocks mutually exclusive, so only the first matching handle will execute (in this case mutual exclusivity is not so necessary, but it can come into play if you add more handle blocks later on)

3 Likes

this works just fine, so I will prefer this setup from now on and mark it as a solution instead. thank you

1 Like