diff --git a/.changeset/odd-crews-approve.md b/.changeset/odd-crews-approve.md
new file mode 100644
index 0000000000000000000000000000000000000000..5bff5e0d64c9ba86388f8f9d047faf5c790e7c50
--- /dev/null
+++ b/.changeset/odd-crews-approve.md
@@ -0,0 +1,5 @@
+---
+'@eth-optimism/proxyd': minor
+---
+
+Allows string RPC ids on proxyd
diff --git a/go/proxyd/backend.go b/go/proxyd/backend.go
index 7b1617b54669e7d1c56364ad4b15d2ea6d7d76fd..3df87b09b70197038a2e00b0a7519bc75d2f69f2 100644
--- a/go/proxyd/backend.go
+++ b/go/proxyd/backend.go
@@ -25,11 +25,6 @@ const (
 )
 
 var (
-	ErrInvalidRequest = &RPCErr{
-		Code:          -32601,
-		Message:       "invalid request",
-		HTTPErrorCode: 400,
-	}
 	ErrParseErr = &RPCErr{
 		Code:          -32700,
 		Message:       "parse error",
@@ -67,6 +62,14 @@ var (
 	}
 )
 
+func ErrInvalidRequest(msg string) *RPCErr {
+	return &RPCErr{
+		Code:          -32601,
+		Message:       msg,
+		HTTPErrorCode: 400,
+	}
+}
+
 type Backend struct {
 	Name                 string
 	rpcURL               string
@@ -498,7 +501,7 @@ func (w *WSProxier) clientPump(ctx context.Context, errC chan error) {
 		// just handle them here.
 		req, err := w.prepareClientMsg(msg)
 		if err != nil {
-			var id *int
+			var id json.RawMessage
 			method := MethodUnknown
 			if req != nil {
 				id = req.ID
@@ -555,7 +558,7 @@ func (w *WSProxier) backendPump(ctx context.Context, errC chan error) {
 
 		res, err := w.parseBackendMsg(msg)
 		if err != nil {
-			var id *int
+			var id json.RawMessage
 			if res != nil {
 				id = res.ID
 			}
diff --git a/go/proxyd/rpc.go b/go/proxyd/rpc.go
index 6b79671b0708bfeb289df449a9dd50f13406d562..54211c0a4e192ef06bdc3188580fe8e1b8c47b28 100644
--- a/go/proxyd/rpc.go
+++ b/go/proxyd/rpc.go
@@ -4,20 +4,21 @@ import (
 	"encoding/json"
 	"io"
 	"io/ioutil"
+	"strings"
 )
 
 type RPCReq struct {
 	JSONRPC string          `json:"jsonrpc"`
 	Method  string          `json:"method"`
 	Params  json.RawMessage `json:"params"`
-	ID      *int            `json:"id"`
+	ID      json.RawMessage `json:"id"`
 }
 
 type RPCRes struct {
-	JSONRPC string      `json:"jsonrpc"`
-	Result  interface{} `json:"result,omitempty"`
-	Error   *RPCErr     `json:"error,omitempty"`
-	ID      *int        `json:"id"`
+	JSONRPC string          `json:"jsonrpc"`
+	Result  interface{}     `json:"result,omitempty"`
+	Error   *RPCErr         `json:"error,omitempty"`
+	ID      json.RawMessage `json:"id"`
 }
 
 func (r *RPCRes) IsError() bool {
@@ -34,6 +35,17 @@ func (r *RPCErr) Error() string {
 	return r.Message
 }
 
+func IsValidID(id json.RawMessage) bool {
+	// handle the case where the ID is a string
+	if strings.HasPrefix(string(id), "\"") && strings.HasSuffix(string(id), "\"") {
+		return len(id) > 2
+	}
+
+	// technically allows a boolean/null ID, but so does Geth
+	// https://github.com/ethereum/go-ethereum/blob/master/rpc/json.go#L72
+	return len(id) > 0 && id[0] != '{' && id[0] != '['
+}
+
 func ParseRPCReq(r io.Reader) (*RPCReq, error) {
 	body, err := ioutil.ReadAll(r)
 	if err != nil {
@@ -46,11 +58,15 @@ func ParseRPCReq(r io.Reader) (*RPCReq, error) {
 	}
 
 	if req.JSONRPC != JSONRPCVersion {
-		return nil, ErrInvalidRequest
+		return nil, ErrInvalidRequest("invalid JSON-RPC version")
 	}
 
 	if req.Method == "" {
-		return nil, ErrInvalidRequest
+		return nil, ErrInvalidRequest("no method specified")
+	}
+
+	if !IsValidID(req.ID) {
+		return nil, ErrInvalidRequest("invalid ID")
 	}
 
 	return req, nil
@@ -70,7 +86,7 @@ func ParseRPCRes(r io.Reader) (*RPCRes, error) {
 	return res, nil
 }
 
-func NewRPCErrorRes(id *int, err error) *RPCRes {
+func NewRPCErrorRes(id json.RawMessage, err error) *RPCRes {
 	var rpcErr *RPCErr
 	if rr, ok := err.(*RPCErr); ok {
 		rpcErr = rr
diff --git a/go/proxyd/server.go b/go/proxyd/server.go
index 392add32640a52b28e7691518e621fdddf9c08d8..477e5302695af675f5e79fae74819b550feb30fa 100644
--- a/go/proxyd/server.go
+++ b/go/proxyd/server.go
@@ -232,7 +232,7 @@ func (s *Server) populateContext(w http.ResponseWriter, r *http.Request) context
 	)
 }
 
-func writeRPCError(w http.ResponseWriter, id *int, err error) {
+func writeRPCError(w http.ResponseWriter, id json.RawMessage, err error) {
 	var res *RPCRes
 	if r, ok := err.(*RPCErr); ok {
 		res = NewRPCErrorRes(id, r)