feat: per-file Cache-Control, navigation-scoped SPA fallback, nosniff, non-root image; v1.7.0
- Cache-Control per file: no-cache for index.html + service workers, immutable 1y for content-hashed assets, 1h for the rest (Vite/CRA/Angular). Closes the stale-release trap at the HTTP layer that the v1.6.0 image fix closed at the container layer. - SPA fallback scoped to navigation: a missing asset (path w/ extension) or a non-HTML Accept now returns 404 instead of index.html (no more HTML-as-JS 'Unexpected token <'). - X-Content-Type-Options: nosniff on every response. - Image runs as a non-root 'spa' user. - README: caching table, fallback contract, and the Angular dist/<project>/browser/ note.
This commit is contained in:
@@ -35,11 +35,22 @@ func (s *Server) OnInit() error {
|
||||
|
||||
s.srv = &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", s.cfg.Port),
|
||||
Handler: mux,
|
||||
Handler: securityHeaders(mux),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// securityHeaders applies response headers that should hold for every route.
|
||||
// X-Content-Type-Options: nosniff stops a browser from MIME-sniffing a response
|
||||
// into an executable type — the safety net for a missing asset that slips through
|
||||
// as HTML, or any upstream that mislabels a Content-Type.
|
||||
func securityHeaders(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// OnStart begins serving HTTP requests in a background goroutine.
|
||||
// The TCP listener binds synchronously so a port conflict surfaces immediately.
|
||||
func (s *Server) OnStart() error {
|
||||
|
||||
Reference in New Issue
Block a user