ginuerzh/gost · warning

bad request

Error message

bad request

What it means

serverHandshake for the ohttp (HTTP obfuscation) transport returns errors.New("bad request") when the client's opening HTTP request fails validation — the code writes a plain HTTP error response back to the client and then returns this error to abort the handshake. It means the incoming bytes were not a valid expected obfuscated-HTTP request.

Source

Thrown at obfs.go:145

	if err != nil {
		log.Logf("[ohttp] %s -> %s : %v", c.Conn.RemoteAddr(), c.Conn.LocalAddr(), err)
		return
	}

	b := bytes.Buffer{}

	if r.Method != http.MethodGet || r.Header.Get("Upgrade") != "websocket" {
		b.WriteString("HTTP/1.1 503 Service Unavailable\r\n")
		b.WriteString("Content-Length: 0\r\n")
		b.WriteString("Date: " + time.Now().Format(time.RFC1123) + "\r\n")
		b.WriteString("\r\n")

		if Debug {
			log.Logf("[ohttp] %s <- %s\n%s", c.RemoteAddr(), c.LocalAddr(), b.String())
		}

		b.WriteTo(c.Conn)
		return errors.New("bad request")
	}

	b.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
	b.WriteString("Server: nginx/1.10.0\r\n")
	b.WriteString("Date: " + time.Now().Format(time.RFC1123) + "\r\n")
	b.WriteString("Connection: Upgrade\r\n")
	b.WriteString("Upgrade: websocket\r\n")
	b.WriteString(fmt.Sprintf("Sec-WebSocket-Accept: %s\r\n", computeAcceptKey(r.Header.Get("Sec-WebSocket-Key"))))
	b.WriteString("\r\n")

	if Debug {
		log.Logf("[ohttp] %s <- %s\n%s", c.RemoteAddr(), c.LocalAddr(), b.String())
	}

	if c.rbuf.Len() > 0 {
		c.wbuf = b // cache the response header if there are extra data in the request body.
		return
	}

View on GitHub (pinned to a33fdbf4c9)

Solutions

  1. Verify client and server use the same obfs method and parameters (ohttp on both ends).
  2. Check the client's handshake request format matches what serverHandshake expects (path, headers, method).
  3. Align client/server versions of the library; the handshake format may differ between versions.
  4. Confirm the client is pointed at the ohttp-enabled port, not another protocol's port.

Example fix

// client config before
nodes: [{addr: "srv:443", connector: "http2", dialer: "tcp"}]
// after (match server obfs)
nodes: [{addr: "srv:443", connector: "http2", dialer: "obfs", dialerArgs: {method: "ohttp", host: "nginx.example.com"}}]
Defensive patterns

Strategy: validation

Validate before calling

// client side: verify obfs config matches server before connecting
if clientObfsMethod != serverObfsMethod {
	return fmt.Errorf("obfs method mismatch: client=%s server=%s", clientObfsMethod, serverObfsMethod)
}

Type guard

func isOhttpHandshakeRejected(err error) bool {
	return err != nil && strings.Contains(err.Error(), "bad request")
}

Try / catch

conn, err := client.Handshake(ctx)
if err != nil && isOhttpHandshakeRejected(err) {
	// likely wrong obfs/transport on one side; recheck config or probe server type
	log.Printf("server rejected ohttp handshake; check obfs method/params")
}

Prevention

When it happens

Trigger: Client sends a malformed or non-ohttp handshake to an ohttp server; HTTP method/headers missing or wrong; a plain-HTTPS scanner or wrong-protocol client connects to the ohttp port; client and server obfs methods/params mismatch (e.g. server is ohttp but client uses plain http2 or different obfs).

Common situations: Port scanners and bots hitting the exposed port; clients configured with the wrong transport/obfs plugin; version mismatch between client and server ohttp implementations; direct browser access to the tunnel port.

Related errors


AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02). Data as JSON: /api/errors/545129d87ac1b235. Report an issue: GitHub.