Send CORS headers only from the image routes (closes #98)
check / check (push) Successful in 4m34s

The CORS middleware with the `access_control_allow_origin` origin wrapped every route from the router root, so the login and URL generator pages and `/metrics` sent `Access-Control-Allow-Origin` too. It now wraps only `/v1/image/` and `/v1/e/`, which form a `/v1` subrouter so that a browser's preflight `OPTIONS` request still gets its answer; the maintenance mode group moved inside it unchanged. Every other route sends no CORS headers. A new test checks both image routes, a preflight included, and the login and URL generator pages. `README.md` and `config.example.yml` say the setting covers the image routes only.

Unverified by test: `/metrics`, whose middleware registers with the process-wide Prometheus registry.

Model: opus-5-5
This commit was merged in pull request #162.
This commit is contained in:
2026-10-03 17:49:43 +02:00
parent 869b5ba67f
commit 021516e099
6 changed files with 106 additions and 22 deletions
+23 -15
View File
@@ -38,7 +38,6 @@ func (s *Server) SetupRoutes() {
s.router.Use(s.mw.Metrics())
}
s.router.Use(s.mw.CORS())
s.router.Use(middleware.Timeout(s.config.DownstreamTimeout))
if s.sentryEnabled {
@@ -74,22 +73,31 @@ func (s *Server) SetupRoutes() {
s.router.Get("/logout", s.h.HandleLogout())
// Image routes, refused while maintenance mode is on. Only these: the
// image's Docker HEALTHCHECK requests the health check, a 503 there
// would make the container unhealthy, and upaas marks a deploy failed
// when its container is unhealthy.
s.router.Group(func(r chi.Router) {
r.Use(s.refuseDuringMaintenance)
// Image routes, the only ones that send CORS headers, as pages on other
// sites read them. They are a subrouter rather than a group: a group's
// middleware runs only for a request that matches one of its routes,
// and a browser's preflight OPTIONS request matches none, so the CORS
// middleware could not answer it.
s.router.Route("/v1", func(r chi.Router) {
r.Use(s.mw.CORS())
// Main image proxy route
// /v1/image/<host>/<path>/<width>x<height>.<format>
r.Get("/v1/image/*", s.h.HandleImage())
r.Head("/v1/image/*", s.h.HandleImage())
// Refused while maintenance mode is on. Only these: the image's
// Docker HEALTHCHECK requests the health check, a 503 there would
// make the container unhealthy, and upaas marks a deploy failed
// when its container is unhealthy.
r.Group(func(r chi.Router) {
r.Use(s.refuseDuringMaintenance)
// Encrypted image URL route
// The trailing filename (e.g., /img.jpg) is ignored but helps
// browsers with content type
r.Get("/v1/e/{token}/*", s.h.HandleImageEnc())
// Main image proxy route
// /v1/image/<host>/<path>/<width>x<height>.<format>
r.Get("/image/*", s.h.HandleImage())
r.Head("/image/*", s.h.HandleImage())
// Encrypted image URL route
// The trailing filename (e.g., /img.jpg) is ignored but helps
// browsers with content type
r.Get("/e/{token}/*", s.h.HandleImageEnc())
})
})
// Metrics endpoint with auth