gojwtcorstraefikgo-chi

Cors headers are set when run locally but not with traefik


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:

Update

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.

Locally:

enter image description here

On the server:

enter image description here


Solution

  • 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.