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
- Verify client and server use the same obfs method and parameters (ohttp on both ends).
- Check the client's handshake request format matches what serverHandshake expects (path, headers, method).
- Align client/server versions of the library; the handshake format may differ between versions.
- 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
- Keep client and server obfs method and parameters identical.
- Deploy matching library versions on both ends.
- Expect scanner traffic on public ports and treat bad-request handshakes as noise unless they correlate with your clients.
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.