Exclude a site address from a site block

1. The problem I’m having:

I want to have a Caddyfile that supports both local development (via localhost) and live environments (via www.foo.com).

I additionally want to redirect requests to foo.com to www.foo.com.

The site address of the “live” environment is provided as a variable. ${CADDY_HOST}. I would set the variable value equal to foo.com or crypter.dev.

Now the problem occurs when CADDY_HOST=localhost. This creates an ambiguous reference (see Caddyfile below).

How should I approach this? I don’t need the rewrite rule to apply to localhost, but I can’t find any syntax that would let Caddy ignore the rewrite for specific site addresses.

2. Error messages and/or full log output:

ambiguous site definition: localhost

3. Caddy version:

caddy:2.6-alpine

4. How I installed and ran Caddy:

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /source/

RUN dotnet tool install --global dotnet-references
ENV PATH="${PATH}:/root/.dotnet/tools"

COPY *.sln ./
COPY */*.csproj ./

RUN dotnet-references fix --entry-point ./Crypter.sln --working-directory ./ --remove-unreferenced-project-files
RUN dotnet restore Crypter.Web

COPY ./ ./
RUN dotnet publish Crypter.Web --no-restore --configuration release --output /app/

FROM caddy:2.6-alpine AS webhost
COPY Crypter.Web/Caddyfile /etc/caddy/Caddyfile
COPY --from=build /app/wwwroot/ /srv/
EXPOSE 80
EXPOSE 443

a. System environment:

Docker running on Windows 10

b. Command:

docker-compose --profile dev up

c. Service/unit/compose file:

version: "3.9"
services:
  api:
    profiles:
      - web
      - dev
    image: ghcr.io/crypter-file-transfer/crypter_api:latest
    build:
      context: .
      dockerfile: Crypter.API.Dockerfile
    expose:
      - "80"
    environment:
      ASPNETCORE_ENVIRONMENT: ${ASPNETCORE_ENVIRONMENT-Production}
      ASPNETCORE_URLS: http://0.0.0.0:80
      ASPNETCORE_TransferStorageSettings__Location: /mnt/storage
      CUSTOMCONNSTR_DefaultConnection: host=${POSTGRES_HOST:-db};database=crypter;user id=crypter_user;pwd=${POSTGRES_USER_PASSWORD:-dev};
      CUSTOMCONNSTR_HangfireConnection: host=${POSTGRES_HANGFIRE_HOST:-db};database=crypter_hangfire;user id=crypter_hangfire_user;pwd=${POSTGRES_HANGFIRE_USER_PASSWORD:-dev};
    volumes:
      - ${API_STORAGE_PATH}:/mnt/storage
      - ${API_SETTINGS_FILE}:/app/appsettings.json
    restart: always
  web:
    profiles:
      - web
      - dev
    image: ghcr.io/crypter-file-transfer/crypter_web:latest
    build:
      context: .
      dockerfile: Crypter.Web.Dockerfile
    ports:
      - ${WEB_BIND_PORT-80}:80
      - ${WEB_SECURE_BIND_PORT-443}:443
    environment:
      CRYPTER_API_BASE: http://api:80
      CADDY_HOST: ${CADDY_HOST}
      CADDY_OPTIONS: ${CADDY_OPTIONS}
      CADDY_TLS_VOLUME: ${CADDY_TLS_VOLUME}
    volumes:
      - ./Containers/Caddy/data:/data
      - ${CADDY_TLS_VOLUME}:/mnt/tls
    restart: always

d. My complete Caddy config:

localhost www.{$CADDY_HOST} {
   {$CADDY_OPTIONS}

   log {
      output file /var/log/caddy
      format console
   }

   handle /api/* {
      reverse_proxy {$CRYPTER_API_BASE}
   }

   handle {
      root * /srv
      try_files {path} /index.html

      header ?Content-Type "application/octet-stream"

      file_server {
         precompressed br gzip
      }
   }
}

{$CADDY_HOST} {
   redir https://www.{host}{uri} permanent
}

5. Links to relevant resources:

Then just put all the relevant site addresses in the environment variable.

Change this to just {$CADDY_HOST} { then set all your site addresses in your env var (either comma or space separated).

See the docs: Common Caddyfile Patterns — Caddy Documentation

Hi Francis.

Then just put all the relevant site addresses in the environment variable.

The problem here is during local development. If I do CADDY_HOST=localhost crypter.dev during local development, Caddy fails to acquire a certificate for crypter.dev.

I guess Caddy still “works” in this scenario, but the log is full of error messages. Is there a cleaner solution?

Change this to just {$CADDY_HOST} { then set all your site addresses in your env var (either comma or space separated).

So CADDY_HOST=localhost www.crypter.dev. And then my site block to handle www. redirects would look like crypter.dev { redir ...}, yes?

Yeah that should work. And if I tell Caddy not to do https:// in the crypter.dev { redir... } site block then Caddy should not try acquire a certificate and won’t fill the log with errors?

Then only include localhost in your env vars on your local machine! That’s the point of environment variables, to be different per environment. You should make a .env file next to your docker-compose.yml with the overrides.

Yep.

There’s no way to redirect HTTPS without having a cert for that domain. But that’s fine, issuing certs is very lightweight. It’s what Caddy is best at.

Thinking out loud…

{$CADDY_HOST} { my main config }

crypter.dev { redir to www. }

Where $CADDY_HOST is one of localhost or www.crypter.dev.

It’s not ideal in the sense that I’ve hard-coded my hostname, but it’s a fair compromise.

Just make a second env var to carry the redirect domain. Or make two Caddyfiles like Caddyfile.dev and Caddyfile.prod that you mount selectively based on the environment.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.