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.
0 Replies
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
so change caddy from holdmybrew.io to :{port}?
17 days ago
Correct
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?
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?
17 days ago
Then you cannot do communication to the backend via the private network.
Using Caddy still is using the public network.
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
}
`
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