ginuerzh/gost · error
method not allowed
Error message
method not allowed
What it means
h2Listener.upgrade rejects an incoming HTTP/2 request whose method is not CONNECT when the listener was created without a fixed path (l.path == ""). In pathless mode the listener only accepts HTTP/2 CONNECT-style tunneling requests; anything else is answered with 405 Method Not Allowed and this error.
Source
Thrown at http2.go:787
if err != nil {
log.Logf("[http2] %s - %s %s %s %s: %s",
r.RemoteAddr, r.Host, r.Method, r.RequestURI, r.Proto, err)
return
}
select {
case l.connChan <- conn:
default:
conn.Close()
log.Logf("[http2] %s - %s: connection queue is full", conn.RemoteAddr(), conn.LocalAddr())
}
<-conn.closed // NOTE: we need to wait for streaming end, or the connection will be closed
}
func (l *h2Listener) upgrade(w http.ResponseWriter, r *http.Request) (*http2Conn, error) {
if l.path == "" && r.Method != http.MethodConnect {
w.WriteHeader(http.StatusMethodNotAllowed)
return nil, errors.New("method not allowed")
}
if l.path != "" && r.RequestURI != l.path {
w.WriteHeader(http.StatusBadRequest)
return nil, errors.New("bad request")
}
w.WriteHeader(http.StatusOK)
if fw, ok := w.(http.Flusher); ok {
fw.Flush() // write header to client
}
remoteAddr, _ := net.ResolveTCPAddr("tcp", r.RemoteAddr)
if remoteAddr == nil {
remoteAddr = &net.TCPAddr{
IP: net.IPv4zero,
Port: 0,
}View on GitHub (pinned to a33fdbf4c9)
Solutions
- Use the library's HTTP/2 client dialer so the request is sent as an HTTP/2 CONNECT, or upgrade via the expected method.
- Configure a path on the listener (l.path) if you want to accept ordinary requests on a specific URI.
- Check the server-side listener options: if path is empty, only CONNECT is valid — fix the client to match.
- Verify any reverse proxy in front preserves the CONNECT method and does not rewrite it to GET.
Example fix
// before (client sends plain GET)
resp, _ := http.Get("https://host/tunnel")
// after (client performs HTTP/2 CONNECT via library dialer)
conn, _ := (&gost.http2.Client{Host: "host:443"}).Dial(ctx, "tcp", "target:80") Defensive patterns
Strategy: validation
Validate before calling
// client side: only CONNECT-style requests are valid when the listener has no path
if listenerPath == "" && req.Method != http.MethodConnect {
return fmt.Errorf("pathless h2 listener requires CONNECT, got %s", req.Method)
} Try / catch
conn, err := dialer.Dial(ctx, network, addr)
if err != nil {
var oe *net.OpError
if errors.As(err, &oe) && strings.Contains(err.Error(), "method not allowed") {
return fmt.Errorf("server requires CONNECT for this endpoint: %w", err)
}
return err
} Prevention
- Keep client request method and server listener path configuration symmetric.
- Test tunnel endpoints with the library's own dialer, not curl/browsers.
- Ensure reverse proxies preserve the CONNECT method.
When it happens
Trigger: Dialing an h2Listener configured with no path using a plain GET/POST (not http2 transport CONNECT), e.g. a normal HTTP request from a browser or curl hitting the server's endpoint while l.path == "".
Common situations: Testing the h2 endpoint with curl/browser instead of the library's http2 client; client and server configured with mismatched modes (one expects CONNECT tunneling, the other sends ordinary requests); proxying non-CONNECT traffic to an http2 tunnel endpoint.
Related errors
AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02).
Data as JSON: /api/errors/fadd8d7e3a84d95a.
Report an issue: GitHub.