Sign a CSR with your own CA

To sign a CSR with your own CA, run `openssl x509 -req -in request.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out certificate.crt -days 397 -sha256 -copy_extensions copyall`. The `-copy_extensions` flag is essential: without it OpenSSL silently discards every extension in the CSR, including the subject alternative names, and the resulting certificate is rejected by every browser. Safer still is to ignore the CSR's extensions entirely and supply your own with `-extfile`, because `copyall` lets the requester dictate what the certificate claims.

Updated August 19, 2026 Verified against OpenSSL 3.5.7

Look at the request first #

You are about to vouch for whatever this says. Read it:

openssl req -in request.csr -noout -text -verify

Confirm the subject is who you think it is and the requested SANs are names this requester should be allowed to have.

The quick way #

Sign, copying the CSR's extensions
openssl x509 -req -in request.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out certificate.crt -days 397 -sha256 \
-copy_extensions copyall

Replace these values

PlaceholderWhat to put there
-reqThe input is a CSR rather than a certificate.
-CA / -CAkeyThe issuing CA certificate and its private key.
-CAcreateserialCreate or increment the serial number file so serials stay unique.
-copy_extensions copyallCarry the CSR extensions across. Without this the SANs are lost.
-days 397Validity. 397 days is the public-CA maximum and a reasonable internal ceiling.

The safe way — define the extensions yourself #

ext.cnf
basicConstraints       = critical,CA:FALSE
keyUsage               = critical,digitalSignature,keyEncipherment
extendedKeyUsage       = serverAuth,clientAuth
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid,issuer
subjectAltName         = DNS:service.internal, DNS:*.service.internal
Sign using your extensions, ignoring the CSR's
openssl x509 -req -in request.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out certificate.crt -days 397 -sha256 \
-extfile ext.cnf

This is how a real CA behaves: the request is an application, and the issuer decides what the certificate actually says.

What each extension does #

Extension Why it is there
basicConstraints=CA:FALSE This certificate may not sign other certificates
keyUsage Restricts the key to signatures and key exchange
extendedKeyUsage=serverAuth Valid for TLS servers. Add clientAuth for mTLS
subjectKeyIdentifier Identifies this certificate’s key
authorityKeyIdentifier Links back to the issuing CA, so clients can build the chain
subjectAltName The hostnames. The only thing browsers match against

Verify what you issued #

openssl x509 -in certificate.crt -noout -subject -issuer -dates -ext subjectAltName -ext basicConstraints

Then confirm it actually chains to your CA:

openssl verify -CAfile ca.crt certificate.crt
Expected output
certificate.crt: OK

Building the chain file for the server #

The server needs its own certificate plus every certificate between it and the root:

cat certificate.crt ca.crt > fullchain.crt

If you signed with an intermediate, the intermediate goes in the chain and the root does not — clients that trust the root do not need to be sent it.

Frequently asked questions

Why does my signed certificate have no SANs?
Because openssl x509 -req drops all CSR extensions unless told otherwise. Add -copy_extensions copyall, or better, supply the extensions yourself with -extfile. This is the single most common mistake when running a private CA.
What does -CAcreateserial do?
It creates a serial number file next to the CA certificate, starting at a random value, so each issued certificate gets a unique serial. After the first issuance the file exists and OpenSSL increments it. Duplicate serials from one CA break revocation and confuse clients.
Is -copy_extensions copyall safe?
Not entirely. It copies whatever the requester put in the CSR, including basicConstraints CA:TRUE if they were malicious or careless — which would let their certificate sign others. For a CA that accepts requests from anyone, define the extensions yourself with -extfile.
How long should the certificate be valid?
397 days is the maximum public CAs may issue and a sensible internal ceiling too. Shorter validity limits the damage from a key exposure you have not noticed, and forces you to keep the renewal process working.

References