Mutual TLS is common on internal APIs, private package feeds, and regulated download endpoints where a password prompt is not acceptable. Presenting a client certificate from wget keeps those transfers scriptable while still binding access to a revocable machine or service identity.
During the TLS handshake, wget can present a client certificate with --certificate and the matching private key with --private-key, while --ca-certificate points to the CA bundle that validates the server. Current GNU wget still treats PEM as the default format, with --certificate-type and --private-key-type available when the files are stored as DER instead.
Client-certificate workflows fail most often because the key does not match the certificate, the server CA chain is incomplete, or the files are readable by other accounts. Keep certificate material in a private directory, test the handshake with spider mode before pulling a large payload, and persist defaults in ~/.wgetrc only when the same identity is reused regularly.
$ wget --help | grep -E -- '--certificate=FILE|--private-key=FILE|--ca-certificate=FILE'
--certificate=FILE client certificate file
--private-key=FILE private key file
--ca-certificate=FILE file with the bundle of CAs
Current GNU wget still accepts PEM by default; add --certificate-type=DER or --private-key-type=DER only when the files are not in PEM format.
$ openssl pkcs12 -in client.p12 -clcerts -nokeys -out client-cert.pem $ openssl pkcs12 -in client.p12 -nocerts -nodes -out client-key.pem
The second command writes the private key without passphrase protection so non-interactive jobs can use it; keep that file in a restricted directory.
$ install -d -m 700 "$HOME/.local/share/wget/mtls" $ install -m 600 client-cert.pem "$HOME/.local/share/wget/mtls/client-cert.pem" $ install -m 600 client-key.pem "$HOME/.local/share/wget/mtls/client-key.pem" $ install -m 600 ca-chain.pem "$HOME/.local/share/wget/mtls/ca-chain.pem" $ ls -l "$HOME/.local/share/wget/mtls" total 24 -rw------- 1 user user 1946 Mar 27 06:55 ca-chain.pem -rw------- 1 user user 1824 Mar 27 06:55 client-cert.pem -rw------- 1 user user 3272 Mar 27 06:55 client-key.pem
Readable private keys let any local user impersonate the client identity for the protected service.
$ openssl x509 -in "$HOME/.local/share/wget/mtls/client-cert.pem" -noout -subject -issuer -enddate subject=CN = build-agent-01.internal.example issuer=CN = Internal Issuing CA notAfter=Dec 12 06:15:01 2029 GMT
Confirm the subject, issuing CA, and expiry against the record from the PKI team before the first live connection.
$ wget --spider \ --certificate="$HOME/.local/share/wget/mtls/client-cert.pem" \ --private-key="$HOME/.local/share/wget/mtls/client-key.pem" \ --ca-certificate="$HOME/.local/share/wget/mtls/ca-chain.pem" \ https://mtls.internal.example/reports/nightly.csv Spider mode enabled. Check if remote file exists. --2026-03-27 06:55:21-- https://mtls.internal.example/reports/nightly.csv Resolving mtls.internal.example (mtls.internal.example)... 192.0.2.50 Connecting to mtls.internal.example (mtls.internal.example)|192.0.2.50|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 84231 [text/csv] Remote file exists.
A certificate or trust failure at this stage usually points to the wrong CA bundle, the wrong certificate/key pair, or a host name that does not match the server certificate.
$ wget \
--certificate="$HOME/.local/share/wget/mtls/client-cert.pem" \
--private-key="$HOME/.local/share/wget/mtls/client-key.pem" \
--ca-certificate="$HOME/.local/share/wget/mtls/ca-chain.pem" \
https://mtls.internal.example/reports/nightly.csv
--2026-03-27 06:55:29-- https://mtls.internal.example/reports/nightly.csv
Resolving mtls.internal.example (mtls.internal.example)... 192.0.2.50
Connecting to mtls.internal.example (mtls.internal.example)|192.0.2.50|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 84231 [text/csv]
Saving to: 'nightly.csv'
0K ...................................................... 100% 11.8M=0.007s
2026-03-27 06:55:29 (11.8 MB/s) - 'nightly.csv' saved [84231/84231]
Keep the first authenticated transfer small when possible so certificate problems are resolved before a large artifact is requested.
~/.wgetrc certificate = /home/user/.local/share/wget/mtls/client-cert.pem private_key = /home/user/.local/share/wget/mtls/client-key.pem ca_certificate = /home/user/.local/share/wget/mtls/ca-chain.pem
Only write account-specific absolute paths into ~/.wgetrc; do not copy shared examples with someone else's home directory.
$ head -n 3 nightly.csv date,total,status 2026-03-27,42,complete $ ls -lh nightly.csv -rw-r--r-- 1 user user 82K Mar 27 06:55 nightly.csv
Successful mutual TLS is not enough on its own; the saved file still needs the expected name, size, and content marker.