Mutual TLS is common on private package mirrors, internal APIs, and partner download endpoints that identify the caller by certificate instead of by password. wget can make the same authenticated HTTPS request from a shell, script, or scheduled job without moving the transfer back into a browser.
GNU wget sends the client certificate with --certificate and the matching private key with --private-key. PEM is the default format, so add --certificate-type=DER or --private-key-type=DER only when the files are actually DER encoded, and use --ca-certificate when the server certificate is signed by a CA that the system trust store does not already trust.
Keep the certificate and key readable only by the account running wget, confirm they match before the first request, and probe the endpoint before starting a large transfer. That catches the usual failures early: the wrong private key, the wrong CA chain, or a client certificate that is already expired.
Steps to use client certificates with wget:
- Stage the client certificate, private key, and private CA bundle in a directory that only the calling account can read.
$ install -d -m 700 "$HOME/.local/share/wget/client-auth" $ install -m 600 client-cert.pem "$HOME/.local/share/wget/client-auth/client-cert.pem" $ install -m 600 client-key.pem "$HOME/.local/share/wget/client-auth/client-key.pem" $ install -m 600 issuing-ca-chain.pem "$HOME/.local/share/wget/client-auth/issuing-ca-chain.pem" $ ls -l "$HOME/.local/share/wget/client-auth" total 24 -rw------- 1 deploy deploy 1946 Mar 28 06:44 issuing-ca-chain.pem -rw------- 1 deploy deploy 1824 Mar 28 06:44 client-cert.pem -rw------- 1 deploy deploy 3272 Mar 28 06:44 client-key.pem
If the server certificate already chains to a public CA trusted by the system, omit the CA bundle and later omit --ca-certificate. Export .p12 or .pfx identities to separate certificate and key files first because wget expects PEM or DER files.
- Confirm that the certificate and private key belong to the same client identity before contacting the remote service.
$ openssl x509 -in "$HOME/.local/share/wget/client-auth/client-cert.pem" -pubkey -noout | openssl sha256 SHA2-256(stdin)= 3f2f8c3ca4d7d9f1e21d96038d1f5c86e6f1b8d7adf7568575b9d90a4b2f0e39 $ openssl pkey -in "$HOME/.local/share/wget/client-auth/client-key.pem" -pubout | openssl sha256 SHA2-256(stdin)= 3f2f8c3ca4d7d9f1e21d96038d1f5c86e6f1b8d7adf7568575b9d90a4b2f0e39
Matching digests show that the certificate and private key are a valid pair instead of two unrelated files with similar names.
- Inspect the certificate metadata so the subject, issuer, and expiry match the client identity that the service expects.
$ openssl x509 -in "$HOME/.local/share/wget/client-auth/client-cert.pem" -noout -subject -issuer -enddate subject=CN = billing-sync-client-01.ops.example.net, O = Example Finance Delivery, OU = Automated Exports issuer=CN = Example Finance Issuing CA 02, O = Example Internal PKI notAfter=Dec 12 06:15:01 2029 GMT
Fixing an expired or mis-issued certificate before the first request is faster than debugging a rejected handshake later.
- Probe the endpoint in spider mode so certificate, key, and CA problems surface before the real download starts.
$ wget --spider \ --certificate="$HOME/.local/share/wget/client-auth/client-cert.pem" \ --private-key="$HOME/.local/share/wget/client-auth/client-key.pem" \ --ca-certificate="$HOME/.local/share/wget/client-auth/issuing-ca-chain.pem" \ https://feed.ops.example.net/exports/billing-ledger-2026-03-28.csv Spider mode enabled. Check if remote file exists. ##### snipped ##### HTTP request sent, awaiting response... 200 OK Length: 84231 [text/csv] Remote file exists.
If the service trusts a public CA, omit --ca-certificate. If the endpoint rejects HEAD requests, run the download step against a small test object instead of using spider mode.
- Download the file with the same certificate, private key, and CA settings after the probe succeeds.
$ wget \ --certificate="$HOME/.local/share/wget/client-auth/client-cert.pem" \ --private-key="$HOME/.local/share/wget/client-auth/client-key.pem" \ --ca-certificate="$HOME/.local/share/wget/client-auth/issuing-ca-chain.pem" \ https://feed.ops.example.net/exports/billing-ledger-2026-03-28.csv ##### snipped ##### HTTP request sent, awaiting response... 200 OK Length: 84231 [text/csv] Saving to: 'billing-ledger-2026-03-28.csv' ##### snipped ##### 2026-03-28 06:45:29 (11.8 MB/s) - 'billing-ledger-2026-03-28.csv' saved [84231/84231]
Reuse the exact same option set from the probe so the successful handshake and the real transfer go through the same trust path.
- Confirm that the saved file is the expected payload before handing it to the next job.
$ cat billing-ledger-2026-03-28.csv billing_date,account_id,delivery_state 2026-03-28,ACCT-48271,complete 2026-03-28,ACCT-48288,complete
The HTTPS handshake proves the client certificate was accepted, but the local file still needs a quick content check before another script imports it.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
