diff --git a/modules/caddypki/adminapi.go b/modules/caddypki/adminapi.go
index 720a5dee9..c90ecac11 100644
--- a/modules/caddypki/adminapi.go
+++ b/modules/caddypki/adminapi.go
@@ -24,6 +24,7 @@ import (
 	"go.uber.org/zap"
 
 	"github.com/caddyserver/caddy/v2"
+	"github.com/google/uuid"
 )
 
 func init() {
@@ -177,6 +178,29 @@ func (a *adminAPI) handleCACerts(w http.ResponseWriter, r *http.Request) error {
 }
 
 type csrRequest struct {
+	// Custom name assigned to the CSR key. If empty, UUID is generated and assigned.
+	ID string `json:"id,omitempty"`
+
+	// Customization knobs of the generated/loaded key, if desired.
+	// If empty, sane defaults will be managed internally without exposing their details
+	// to the user. At the moment, the default parameters are:
+	// {
+	// 	"type": "EC",
+	// 	"curve": "P-256"
+	// }
+	Key *struct {
+		// The key type to be used for signing the CSR. The possible types are:
+		// EC, RSA, and OKP.
+		Type string `json:"type"`
+
+		// The curve to use with key types EC and OKP.
+		// If the Type is OKP, then acceptable curves are: Ed25519, or X25519
+		// If the Type is EC, then acceptable curves are: P-256, P-384, or P-521
+		Curve string `json:"curve,omitempty"`
+
+		// Only used with RSA keys and accepts minimum of 2048.
+		Size int `json:"size,omitempty"`
+	} `json:"key,omitempty"`
 	// SANs is a list of subject alternative names for the certificate.
 	SANs []string `json:"sans"`
 }
@@ -205,8 +229,13 @@ func (a *adminAPI) handleCSRGeneration(w http.ResponseWriter, r *http.Request) e
 			Err:        fmt.Errorf("failed to decode CSR request: %v", err),
 		}
 	}
+	csrReq.ID = strings.TrimSpace(csrReq.ID)
+	if len(csrReq.ID) == 0 {
+		csrReq.ID = uuid.New().String()
+	}
+
 	// Generate the CSR
-	csr, err := ca.generateCSR(csrReq.SANs)
+	csr, err := ca.generateCSR(csrReq)
 	if err != nil {
 		return caddy.APIError{
 			HTTPStatus: http.StatusInternalServerError,
@@ -227,6 +256,8 @@ func (a *adminAPI) handleCSRGeneration(w http.ResponseWriter, r *http.Request) e
 		}
 	}
 	w.Header().Set("Content-Type", "application/pkcs10")
+	w.Header().Set("content-disposition", fmt.Sprintf(`attachment; filename="%s"`, csrReq.ID))
+
 	if _, err := w.Write(bs); err != nil {
 		return caddy.APIError{
 			HTTPStatus: http.StatusInternalServerError,
diff --git a/modules/caddypki/ca.go b/modules/caddypki/ca.go
index 15339cedb..326f17113 100644
--- a/modules/caddypki/ca.go
+++ b/modules/caddypki/ca.go
@@ -396,8 +396,8 @@ func (ca CA) storageKeyIntermediateKey() string {
 	return path.Join(ca.storageKeyCAPrefix(), "intermediate.key")
 }
 
-func (ca CA) storageKeyCSRKey() string {
-	return path.Join(ca.storageKeyCAPrefix(), "csr.key")
+func (ca CA) storageKeyCSRKey(id string) string {
+	return path.Join(ca.storageKeyCAPrefix(), id+".csr.key")
 }
 
 func (ca CA) newReplacer() *caddy.Replacer {
@@ -427,34 +427,41 @@ func (ca CA) installRoot() error {
 	)
 }
 
-func (ca CA) generateCSR(sans []string) (csr *x509.CertificateRequest, err error) {
+func (ca CA) generateCSR(csrReq csrRequest) (csr *x509.CertificateRequest, err error) {
 	var signer crypto.Signer
-	csrKeyPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyCSRKey())
+	csrKeyPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyCSRKey(csrReq.ID))
 	if err != nil {
 		if !errors.Is(err, fs.ErrNotExist) {
-			return nil, fmt.Errorf("loading csr key: %v", err)
+			return nil, fmt.Errorf("loading csr key '%s': %v", csrReq.ID, err)
+		}
+		if csrReq.Key == nil {
+			signer, err = keyutil.GenerateDefaultSigner()
+			if err != nil {
+				return nil, err
+			}
+		} else {
+			signer, err = keyutil.GenerateSigner(csrReq.Key.Type, csrReq.Key.Curve, csrReq.Key.Size)
+			if err != nil {
+				return nil, err
+			}
 		}
 
-		signer, err = keyutil.GenerateDefaultSigner()
-		if err != nil {
-			return nil, err
-		}
 		csrKeyPEM, err = certmagic.PEMEncodePrivateKey(signer)
 		if err != nil {
 			return nil, fmt.Errorf("encoding csr key: %v", err)
 		}
-		if err := ca.storage.Store(ca.ctx, ca.storageKeyCSRKey(), csrKeyPEM); err != nil {
+		if err := ca.storage.Store(ca.ctx, ca.storageKeyCSRKey(csrReq.ID), csrKeyPEM); err != nil {
 			return nil, fmt.Errorf("saving csr key: %v", err)
 		}
 	}
 	if signer == nil {
 		signer, err = certmagic.PEMDecodePrivateKey(csrKeyPEM)
 		if err != nil {
-			return nil, fmt.Errorf("decoding root key: %v", err)
+			return nil, fmt.Errorf("decoding csr key: %v", err)
 		}
 	}
 
-	csr, err = x509util.CreateCertificateRequest("", sans, signer)
+	csr, err = x509util.CreateCertificateRequest("", csrReq.SANs, signer)
 	if err != nil {
 		return nil, err
 	}