I'm trying to create a simple backend in go using Chi as my router and traefik as my reverse proxy. My cors headers are returning fine when I run the docker container by itself on port 8000. However, when I run it on my droplet using traefik, the endpoint's data returns fine, but none of the cors headers are returned in the response, and neither is any other header (I tried adding a test header, no luck). What's going on here? Is traefik filtering out my headers somehow?
I tried messing around with the traefik config to manually set the cors headers that way, but no luck. Here's my files for context:
// server.go
package server
import (
"encoding/json"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"github.com/go-chi/render"
)
func (s *Server) RegisterRoutes() http.Handler {
r := chi.NewRouter()
r.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"https://my-deployment-url.netlify.app", "http://localhost:3000"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
ExposedHeaders: []string{"Link"},
AllowCredentials: true,
MaxAge: 300,
}))
r.Use(middleware.Logger)
r.Get("/", s.IndexHandler)
return r
}
func (s *Server) IndexHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Test-Header", "TestHeaderValue")
resp := map[string]string{
"message": "Route 2",
}
render.JSON(w, r, resp)
}
# compose.yaml
services:
reverse-proxy:
image: traefik:v3.1
command:
- "--providers.docker"
- "--providers.docker.exposedbydefault=false"
- "--entryPoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
- "--certificatesresolvers.myresolver.acme.email=my-email@gmail.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
ports:
- "80:80"
- "443:443"
volumes:
- letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock
goapp:
container_name: go
labels:
- "traefik.enable=true"
- "traefik.http.routers.kanjoy.rule=Host(`my-deployment-url`)"
- "traefik.http.routers.kanjoy.entrypoints=websecure"
- "traefik.http.routers.kanjoy.tls.certresolver=myresolver"
build:
context: .
dockerfile: Dockerfile.prod
volumes:
- ./:/app
depends_on:
- db
db:
image: postgres:latest
restart: unless-stopped
environment:
POSTGRES_DB: ${BLUEPRINT_DB_DATABASE}
POSTGRES_USER: ${BLUEPRINT_DB_USERNAME}
POSTGRES_PASSWORD: ${BLUEPRINT_DB_PASSWORD}
ports:
- "${BLUEPRINT_DB_PORT}:5432"
volumes:
- db:/var/lib/postgresql/data
volumes:
db:
letsencrypt:
I have managed to temporarily circumvent this problem by setting my cors headers in my compose.yaml
file instead.
- "traefik.http.routers.to_kanjoy.middlewares=cors"
- "traefik.http.middlewares.cors.headers.accesscontrolallowmethods=GET,OPTIONS,PUT,POST,DELETE,PATCH"
- "traefik.http.middlewares.cors.headers.accesscontrolallowheaders=Content-Type,
Authorization, X-Requested-With"
- "traefik.http.middlewares.cors.headers.accesscontrolallowcredentials=true"
- "traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist=https://my-deployment-url.netlify.app,http://localhost:3000"
- "traefik.http.middlewares.cors.headers.accesscontrolmaxage=100"
- "traefik.http.middlewares.cors.headers.addvaryheader=true"
This allows the proper cors headers to get returned, but any headers I set in Go are still being filtered out. For example, I'm trying to implement Oauth with JWT tokens, and although it's working totally fine when the image is deployed locally without traefik, my headers are getting blocked when I visit the same path through my deployment.
I figured out the issue. In the end, it wasn't an issue with traefik or chi, but rather with my docker build. I forgot to re-build the image without cache after adding in the cors headers. Once I did that, it worked perfectly fine.