1. The problem I’m having:
I’m attempting to serve some precompressed brotli files using Caddy web server using the file_server
→ precompressed br gzip
directives. This works in most cases, but fails when the file is of the octet-stream
type and the browser is Firefox. This works perfectly fine in Chrome. It also works fine in both browsers using Nginx. What is going on?
Edit: Everything also works fine when serving gzip files from Caddy to Firefox. It’s just the combination of brotli + octet-stream + firefox that has this weird behavior.
2. Error messages and/or full log output:
I enabled logging, but all I see are INFO logs that suggest everything within Caddy is working fine.
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:main
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
depends_on:
db:
condition: service_healthy
web:
profiles:
- web
- dev
image: ghcr.io/crypter-file-transfer/crypter_web:main
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
db:
profiles:
- db
- dev
image: postgres:15.2
expose:
- "5432"
ports:
- ${POSTGRES_BIND_IP-[::1]}:${POSTGRES_BIND_PORT-5432}:5432
environment:
POSTGRES_PASSWORD: ${POSTGRES_SUPERUSER_PASSWORD-dev}
POSTGRES_C_PASSWORD: ${POSTGRES_USER_PASSWORD:-dev}
POSTGRES_HF_PASSWORD: ${POSTGRES_HANGFIRE_USER_PASSWORD:-dev}
volumes:
- ./Containers/PostgreSQL/data:/var/lib/postgresql/data
- ./Containers/PostgreSQL/postgres-init-files:/docker-entrypoint-initdb.d
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -d crypter -U postgres"]
interval: 10s
timeout: 10s
retries: 5
start_period: 60s
d. My complete Caddy config:
{$CADDY_HOST} {
{$CADDY_OPTIONS}
log {
output file /var/log/caddy
format console
level INFO
}
route {
reverse_proxy /api/* {$CRYPTER_API_BASE}
try_files {path} /index.html
root * /srv/
file_server {
precompressed br gzip
}
}
}