Skip to content
All writing ‹ Part 16 of 18 · A Cache Is a Chain, Not a Box
Engineering · 1 min read

The Proxy Rejects HEAD, So the Refresh Always Fails

The LB cache refreshes large files with HEAD, but the Go proxy built on Gin registers only GET and returns 405. One missing method breaks a cache layer.

The LB cache uses HEAD to refresh large files. Our proxy accepts only GET, so it returns 405, and the copy stays expired.

The question from the last post: why does the background refresh keep failing (Stale-While-Revalidate: Serve Old First, Refresh in the Background)? I opened the proxy log and code.

// router
engine.GET("/*path", RootHandler)       // only GET is registered

// server
engine.HandleMethodNotAllowed = true    // right path, wrong method → 405
  • The proxy uses the Gin framework in Go. It registers only GET for all paths. No route accepts HEAD.
  • HandleMethodNotAllowed = true makes Gin return 405 instead of 404.
  • Cloud storage, nginx, and the Go standard library net/http all answer HEAD with the GET route by default. Gin does not.

What I Saw in the Log

ObservationResult
HTML requests reaching the proxyMore than ten per second
Of these, HEAD requests that got 40575%
User agent of the 405 requestsThe LB cache itself
Files with the most 405HTML of 3.5 to 4.3 MB
Files with the most 304All smaller than 1 KB

I sent 7 requests through the CDN. The proxy log showed 7 lines of HEAD 405 in the same second. The worst file was 3.65 MB, and it was fetched every 0.24 seconds.

I inferred from the log that the LB cache switches to HEAD to refresh large files. The official docs do not say this.

One method that a framework does not support by default can break a whole cache layer.

Related: One HTTP Request, Stale-While-Revalidate: Serve Old First, Refresh in the Background, Five Parts Make One Loop. Or go back to the series overview.

↑↓ navigate ↵ open esc close