Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 17:15 UTC
caddyhttp: Reject invalid Host header (fix #7449)
Matthew Holt committed
Jan 30, 2026 at 12:24 UTC
7d24124430ed8a2bfc3aa0625a742082361e3ff8
1 file changed
+60
-46
modules/caddyhttp/server.go
+60
-46
@@ -18,6 +18,7 @@ import (
18
"context"
19
"crypto/tls"
20
"encoding/json"
21
+ "errors"
22
"fmt"
23
"io"
24
"net"
@@ -297,6 +298,8 @@ var (
298
299
// ServeHTTP is the entry point for all HTTP requests.
300
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
301
+ start := time.Now()
302
+
303
// If there are listener wrappers that process tls connections but don't return a *tls.Conn, this field will be nil.
304
if r.TLS == nil {
305
if tlsConnStateFunc, ok := r.Context().Value(tlsConnectionStateFuncCtxKey).(func() *tls.ConnectionState); ok {
@@ -304,6 +307,17 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
307
}
308
}
309
310
+ // enable full-duplex for HTTP/1, ensuring the entire
311
+ // request body gets consumed before writing the response
312
+ if s.EnableFullDuplex && r.ProtoMajor == 1 {
313
+ if err := http.NewResponseController(w).EnableFullDuplex(); err != nil { //nolint:bodyclose
314
+ if c := s.logger.Check(zapcore.WarnLevel, "failed to enable full duplex"); c != nil {
315
+ c.Write(zap.Error(err))
316
+ }
317
+ }
318
+ }
319
+
320
+ // set the Server header
321
h := w.Header()
322
h["Server"] = serverHeader
323
@@ -316,39 +330,14 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
330
}
331
}
332
319
- // reject very long methods; probably a mistake or an attack
320
- if len(r.Method) > 32 {
321
- if s.shouldLogRequest(r) {
322
- if c := s.accessLogger.Check(zapcore.DebugLevel, "rejecting request with long method"); c != nil {
323
- c.Write(
324
- zap.String("method_trunc", r.Method[:32]),
325
- zap.String("remote_addr", r.RemoteAddr),
326
- )
327
- }
328
- }
329
- w.WriteHeader(http.StatusMethodNotAllowed)
330
- return
331
- }
332
-
333
+ // prepare internals of the request for the handler pipeline
334
repl := caddy.NewReplacer()
335
r = PrepareRequest(r, repl, w, s)
336
336
- // enable full-duplex for HTTP/1, ensuring the entire
337
- // request body gets consumed before writing the response
338
- if s.EnableFullDuplex && r.ProtoMajor == 1 {
339
- if err := http.NewResponseController(w).EnableFullDuplex(); err != nil { //nolint:bodyclose
340
- if c := s.logger.Check(zapcore.WarnLevel, "failed to enable full duplex"); c != nil {
341
- c.Write(zap.Error(err))
342
- }
343
- }
344
- }
345
-
346
- // clone the request for logging purposes before
347
- // it enters any handler chain; this is necessary
348
- // to capture the original request in case it gets
349
- // modified during handling
350
- // cloning the request and using .WithLazy is considerably faster
351
- // than using .With, which will JSON encode the request immediately
337
+ // clone the request for logging purposes before it enters any handler chain;
338
+ // this is necessary to capture the original request in case it gets modified
339
+ // during handling (cloning the request and using .WithLazy is considerably
340
+ // faster than using .With, which will JSON-encode the request immediately)
341
shouldLogCredentials := s.Logs != nil && s.Logs.ShouldLogCredentials
342
loggableReq := zap.Object("request", LoggableHTTPRequest{
343
Request: r.Clone(r.Context()),
@@ -376,36 +365,33 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
365
}
366
367
// capture the original version of the request
379
- accLog := s.accessLogger.With(loggableReq)
368
+ accLog := s.accessLogger.WithLazy(loggableReq)
369
370
defer s.logRequest(accLog, r, wrec, &duration, repl, bodyReader, shouldLogCredentials)
371
}
372
384
- start := time.Now()
385
-
386
- // guarantee ACME HTTP challenges; handle them
387
- // separately from any user-defined handlers
373
+ // guarantee ACME HTTP challenges; handle them separately from any user-defined handlers
374
if s.tlsApp.HandleHTTPChallenge(w, r) {
375
duration = time.Since(start)
376
return
377
}
378
393
- // execute the primary handler chain
394
- err := s.primaryHandlerChain.ServeHTTP(w, r)
379
+ err := s.serveHTTP(w, r)
380
duration = time.Since(start)
381
397
- // if no errors, we're done!
382
if err == nil {
383
return
384
}
385
386
// restore original request before invoking error handler chain (issue #3717)
403
- // TODO: this does not restore original headers, if modified (for efficiency)
404
- origReq := r.Context().Value(OriginalRequestCtxKey).(http.Request)
405
- r.Method = origReq.Method
406
- r.RemoteAddr = origReq.RemoteAddr
407
- r.RequestURI = origReq.RequestURI
408
- cloneURL(origReq.URL, r.URL)
387
+ // NOTE: this does not restore original headers if modified (for efficiency)
388
+ origReq, ok := r.Context().Value(OriginalRequestCtxKey).(http.Request)
389
+ if ok {
390
+ r.Method = origReq.Method
391
+ r.RemoteAddr = origReq.RemoteAddr
392
+ r.RequestURI = origReq.RequestURI
393
+ cloneURL(origReq.URL, r.URL)
394
+ }
395
396
// prepare the error log
397
errLog = errLog.With(zap.Duration("duration", duration))
@@ -424,8 +410,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
410
if s.Errors != nil && len(s.Errors.Routes) > 0 {
411
// execute user-defined error handling route
412
if err2 := s.errorHandlerChain.ServeHTTP(w, r); err2 == nil {
427
- // user's error route handled the error response
428
- // successfully, so now just log the error
413
+ // user's error route handled the error response successfully, so now just log the error
414
for _, logger := range errLoggers {
415
if c := logger.Check(zapcore.DebugLevel, errMsg); c != nil {
416
if fields == nil {
@@ -473,6 +458,35 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
458
}
459
}
460
461
+func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) error {
462
+ // reject very long methods; probably a mistake or an attack
463
+ if len(r.Method) > 32 {
464
+ if s.shouldLogRequest(r) {
465
+ if c := s.accessLogger.Check(zapcore.DebugLevel, "rejecting request with long method"); c != nil {
466
+ c.Write(
467
+ zap.String("method_trunc", r.Method[:32]),
468
+ zap.String("remote_addr", r.RemoteAddr),
469
+ )
470
+ }
471
+ }
472
+ return HandlerError{StatusCode: http.StatusMethodNotAllowed}
473
+ }
474
+
475
+ // RFC 9112 section 3.2: "A server MUST respond with a 400 (Bad Request) status
476
+ // code to any HTTP/1.1 request message that lacks a Host header field and to any
477
+ // request message that contains more than one Host header field line or a Host
478
+ // header field with an invalid field value."
479
+ if r.Host == "" {
480
+ return HandlerError{
481
+ Err: errors.New("rfc9112 forbids empty Host"),
482
+ StatusCode: http.StatusBadRequest,
483
+ }
484
+ }
485
+
486
+ // execute the primary handler chain
487
+ return s.primaryHandlerChain.ServeHTTP(w, r)
488
+}
489
+
490
// wrapPrimaryRoute wraps stack (a compiled middleware handler chain)
491
// in s.enforcementHandler which performs crucial security checks, etc.
492
func (s *Server) wrapPrimaryRoute(stack Handler) Handler {