Setup CORS in Caddy 2

Caddy 2 is an open source web server with automatic HTTPS. It's a wise choice for pet projects or self-hosted services, since you are free from managing TLS certs on your own and wiring things up can be super annoying.

One missing feature in Caddy 2, however, is cross-origin resource sharing (CORS) support. For a "batteries included" web server, it's rather surprising. Fortunately, one can use the following Caddy snippet to augment any site with CORS headers without repeating oneself over and over again.

You might want to update the list of headers returned by Access-Control-Allow-Headers or Access-Control-Expose-Headers HTTP headers according to your application needs. Please refer to the CORS documentation to learn more what they are about.

(cors) {
  @cors_preflight method OPTIONS
  @cors header Origin {args.0}

  handle @cors_preflight {
    header Access-Control-Allow-Origin "{args.0}"
    header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE"
    header Access-Control-Allow-Headers "Content-Type"
    header Access-Control-Max-Age "3600"
    respond "" 204
  }

  handle @cors {
    header Access-Control-Allow-Origin "{args.0}"
    header Access-Control-Expose-Headers "Link"
  }
}

example.com {
  import cors https://example.com
  reverse_proxy localhost:8080
}

The nice part about this snippet is that CORS headers are only returned for HTTP requests with the Origin HTTP header. That header is normally used by browsers only, which means you won't see CORS headers in responses for requests sent by curl or your-programming-language-of-choice.

I've been successfully using this snippet for quite a while now to protect api.xsnippet.org, so it can be accessed by xsnippet.org.