diff --git a/statuscode.go b/statuscode.go
index 661c6693e9eb562ca65f0084f5aba0a6a16904a5..42ae40c0fb9b57f626ceb6ca9e6157ba2e93eff2 100644
--- a/statuscode.go
+++ b/statuscode.go
@@ -41,7 +41,8 @@ const (
 )
 
 // CloseError represents a WebSocket close frame.
-// It is returned by Conn's methods when the Connection is closed with a WebSocket close frame.
+// It is returned by Conn's methods when a WebSocket close frame is received from
+// the peer.
 // You will need to use https://golang.org/x/xerrors to check for this error.
 type CloseError struct {
 	Code   StatusCode
diff --git a/websocket.go b/websocket.go
index f875a14267f3809deade516d6bd15cff8380732f..47db5f1882ac70104e146c18cae43efdbc7fbb7b 100644
--- a/websocket.go
+++ b/websocket.go
@@ -792,6 +792,9 @@ func (c *Conn) writePong(p []byte) error {
 // The connection can only be closed once. Additional calls to Close
 // are no-ops.
 //
+// This does not perform a WebSocket close handshake.
+// See https://github.com/nhooyr/websocket/issues/103 for details on why.
+//
 // The maximum length of reason must be 125 bytes otherwise an internal
 // error will be sent to the peer. For this reason, you should avoid
 // sending a dynamic reason.
@@ -823,7 +826,9 @@ func (c *Conn) exportedClose(code StatusCode, reason string) error {
 		p, _ = ce.bytes()
 	}
 
-	err = c.writeClose(p, xerrors.Errorf("sent close frame: %w", ce))
+	// CloseErrors sent are made opaque to prevent applications from thinking
+	// they received a given status.
+	err = c.writeClose(p, xerrors.Errorf("sent close frame: %v", ce))
 	if err != nil {
 		return err
 	}