Skip to content

Unmatched routes respond 200 OK with empty body when middleware is registered globally via Use() #87

Description

@jonas0616

When the timeout middleware is registered globally with engine.Use(...), any request that does not match a registered route is answered with 200 OK and an empty body instead of gin's default 404 page not found.

Gin's own access logger still reports 404, so the internal status and the status actually sent on the wire disagree, which makes this very confusing to debug behind a reverse proxy (our ingress logged 200 while the app logged 404).

Registering the middleware per-route (as in _example/example01) does not trigger the bug, because route-level handlers never run for unmatched routes. The examples therefore never exercise this path.

How to reproduce

package main

import (
	"net/http"
	"time"

	"github.com/gin-contrib/timeout"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.New()
	r.Use(gin.Logger(), timeout.New(timeout.WithTimeout(30*time.Second)))
	r.GET("/hello", func(c *gin.Context) {
		c.String(http.StatusOK, "world")
	})
	_ = http.ListenAndServe(":8080", r)
}
$ curl -si http://127.0.0.1:8080/no/such/route
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 0

Server log for the same request (note the 404):

[GIN] 2026/07/30 - 17:14:57 | 404 | 57.607µs | 127.0.0.1 | GET "/no/such/route"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions