Error 502 or perma redirect loop

rannem
HOBBY

17 days ago

I have 3 services, my frontend is a angular spa, using Caddyfile as a reverse proxy to my backend, my backend is connected via internal network to the frontend hence i want the reverse-proxy. After adding custom domain via cloudflare i get 502 error code, or permanent redirect if i open http_port :${port} in Caddyfile. Below is my docker and Caddyfile

Caddyfile:
`# Caddyfile: Final Verified Configuration# ====================================================================# GLOBAL OPTIONS BLOCK# This block MUST be at the very top of the file. It configures Caddy# for the Railway environment BEFORE it processes any sites.# ====================================================================

{

Use Let's Encrypt staging for testing to avoid rate limits.

Remove this line for production once everything works.

acme_ca https://acme-staging-v02.api.letsencrypt.org/directory

log {

output stdout

level debug

}

This solved the 502 error, but it caused a redirect loop.

http_port {$PORT}

This is the most critical setting for fixing the redirect loop.

It tells Caddy to trust the X-Forwarded-Proto: https header from Railway's proxy.

It MUST be inside this global options block.

servers {

trusted_proxies static 0.0.0.0/0 ::/0

}

}# ===================================# SITE DEFINITION# ===================================# By using your domain here, you are telling Caddy to manage HTTPS.# Caddy will automatically use the $PORT variable from Railway.

www.[holdmybrew.io](holdmybrew.io) {

Configure automatic TLS via Cloudflare DNS challenge.

tls {

dns cloudflare {env.CLOUDFLAREAPITOKEN}

resolvers 1.1.1.1

}

Enable modern compression.

encode gzip zstd

Define a single matcher for all backend API and SignalR traffic.

The path matcher /commentHub* covers both /commentHub and /commentHub/*

@backend {

path /api/* /commentHub* /commentHub/*

}

Route all requests matching @backend to your API service.

handle @backend {

reverseproxy {env.BACKENDAPI_URL}

}

Handle all other requests by serving the Angular SPA.

This is the standard pattern for single-page applications.

handle {

root * /srv

try_files {path} /index.html

file_server

}

}`

Dockerfile:
`# === STAGE 1: Build the Angular App ===

FROM node:24.3.0-alpine AS angular_builder

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .# Make sure your build output path is correct here

RUN npm run build# === STAGE 2: Build Caddy with the Cloudflare Plugin ===

FROM caddy:2-builder AS caddy_builder

RUN xcaddy build \

--with github.com/caddy-dns/cloudflare# === STAGE 3: Create the Final Production Image ===

FROM caddy:2-alpine# CRITICAL STEP: Copy the custom Caddy binary from the caddy_builder stage

COPY --from=caddy_builder /usr/bin/caddy /usr/bin/caddy# Copy your Caddyfile for web server configuration

COPY Caddyfile /etc/caddy/Caddyfile# Copy the built Angular app from the angular_builder stage# IMPORTANT: Verify this is the correct path to your built app

COPY --from=angular_builder /app/dist/hold-my-brew-bingo/browser /srv`

Cloudflare configs:

ssl/tls full(strict)

a record -> holdmybrew.io -> 192.0.2.1 orange proxy

cname record -> www -> railway custom address orange proxy

i either get 502 error, if i set http_port to :${port} i get permanent redirects please help.

Solved

0 Replies

rannem
HOBBY

17 days ago

94f60892-92d8-44e3-8942-22e2d5381499


17 days ago

You should not be configuring Caddy to use a domain name, The Railway platform handles that, all Caddy needs to do is listen on 0.0.0.0:$PORT


rannem
HOBBY

17 days ago

so change caddy from holdmybrew.io to :{port}?


rannem
HOBBY

17 days ago

im completly new to networking, so kinda going in blind.


17 days ago

Correct


rannem
HOBBY

17 days ago

Tested that, now i get this in my railwaylogs: client sent an HTTP request to an HTTPS server

and website shows: Client sent an HTTP request to an HTTPS server.
again, so sorry for bothering you, love the platform and slowly moving all of my infrastructue over here hehe, its a learning process


17 days ago

Maybe we should take a step back here, why is a proxy needed?


rannem
HOBBY

17 days ago

I wanted to have communication to the backend via private network, so tried to find best practice on it and found people talking highly about using caddy todo.


17 days ago

Is the frontend a SPA?


rannem
HOBBY

17 days ago

Yes. Angular SPA


rannem
HOBBY

17 days ago

I GOT IT TO WORK



17 days ago

Then you cannot do communication to the backend via the private network.

Using Caddy still is using the public network.


rannem
HOBBY

17 days ago

Atleast i hit the server, now i just need to figure out why backend isnt talking back, but thats another problem ill have to look into, changed Caddyfile to this.
`# Caddyfile: Final Verified Configuration# ====================================================================# GLOBAL OPTIONS BLOCK# This block MUST be at the very top of the file. It configures Caddy# for the Railway environment BEFORE it processes any sites.# ====================================================================
{
# Use Let's Encrypt staging for testing to avoid rate limits.

# Remove this line for production once everything works.

acme_ca [https://acme-staging-v02.api.letsencrypt.org/directory](https://acme-staging-v02.api.letsencrypt.org/directory)
auto_https off # railway handles https for us, this could in some cases cause issues if left enabled
log {
    output stdout

    level debug
}

servers {
    trusted_proxies static 0.0.0.0/0 ::/0
}

}

Listen on the port provided by the Railway environment variable

http://holdmybrew.io:{$PORT} {
tls {
dns cloudflare {env.CLOUDFLAREAPITOKEN}

    resolvers 1.1.1.1
}
# Define a reusable "named matcher" for all backend traffic.
# CRITICAL FIX: Added "/commentHub" to match the base hub URL itself,
# in addition to sub-paths like "/commentHub/negotiate".
@backend {
    path /api/* /commentHub /commentHub/*
}

# All requests matching the @backend matcher are proxied.
handle @backend {
    # The reverse_proxy directive automatically handles WebSockets
    # and sets appropriate X-Forwarded-* headers.

    reverse_proxy {env.BACKEND_API_URL}
}

# All other requests are assumed to be for the Angular frontend.
# This block acts as the catch-all for serving static assets.
handle {
    root * /srv

    try_files {path} /index.html

    file_server
}

# Enable compression for all responses.
encode gzip zstd

}
`


rannem
HOBBY

17 days ago

ahh okay, when i didnt have a custom domain it seemed to work correctly tho? As i said i dont have too much experience with networking on cloud hehe.


17 days ago

Caddy may be talking to your backend via the private network, but your frontend is talking to Caddy via the public network, so you have only added extra complexity with no real benefits.


17 days ago

!s


Status changed to Solved brody 17 days ago