Updated: 17 October 2026 · Applies to: Apache Tomcat 11.0 and 10.1 (tested: Tomcat 11.0.22 and 10.1.55 from the Debian 13 packages, OpenJDK 21, OpenSSL 3.5); Tomcat 9.0 uses the same SSLHostConfig syntax according to Apache

To configure SSL in Tomcat, copy your certificate, its chain and the private key to the server and add an HTTPS Connector with an SSLHostConfig element to conf/server.xml. Current Tomcat reads PEM files (certificateFile, certificateKeyFile, certificateChainFile) or a PKCS12 keystore directly, so the JKS conversion from older guides is no longer needed. Restart Tomcat and open https://your-domain:8443/.

Which Tomcat version do you run?

Check it with /usr/share/tomcat11/bin/version.sh (Debian and Ubuntu packages) or $CATALINA_HOME/bin/version.sh; ours printed Server version: Apache Tomcat/11.0.22 (Debian). According to the Apache Tomcat project, Tomcat 6, 7, 8.0, 8.5 and 10.0 are end of life (8.5 since March 2024) and get no security fixes; 11.0, 10.1 and 9.0 are supported. The old Tomcat 6, 7 and 8.0 style, with keystoreFile and keystorePass on the Connector, does not work on Tomcat 10.1 or 11 (see the errors below). If you are still on an old version, upgrade first: our guide to install Tomcat 11 on Ubuntu and run it behind Nginx covers a fresh install.

1. Get the certificate files

  • A certificate you buy: create a private key and a certificate signing request (CSR), send the CSR to the certificate authority, and you get back your certificate plus an intermediate (CA bundle) file:
    openssl req -new -newkey rsa:2048 -nodes -keyout example.com.key -out example.com.csr -subj "/CN=example.com"
    See information needed for an SSL certificate (CSR). Ucartz also sells DV, OV and wildcard SSL certificates with free installation help.
  • Let's Encrypt: according to Certbot, you get fullchain.pem (your certificate plus the chain) and privkey.pem in /etc/letsencrypt/live/example.com/. Renewals are covered in step 6.
  • Testing only: a self-signed certificate made with OpenSSL. Browsers will warn about it.

Put the files where the tomcat user can read them, and nobody else. With the Debian and Ubuntu packages, conf is /etc/tomcat11:

sudo mkdir /etc/tomcat11/ssl
sudo cp example.com.crt example.com.key chain.pem /etc/tomcat11/ssl/
sudo chown -R root:tomcat /etc/tomcat11/ssl
sudo chmod 750 /etc/tomcat11/ssl && sudo chmod 640 /etc/tomcat11/ssl/*

For Tomcat 10.1 use /etc/tomcat10; for a tarball install use the conf folder under $CATALINA_BASE.

2. Option A: PEM files (simplest)

Edit conf/server.xml and add this inside the <Service> element, next to the existing 8080 Connector (the Debian file has a commented-out 8443 example you can replace):

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="150" SSLEnabled="true">
    <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
    <SSLHostConfig>
        <Certificate certificateFile="conf/ssl/example.com.crt"
                     certificateKeyFile="conf/ssl/example.com.key"
                     certificateChainFile="conf/ssl/chain.pem"
                     type="RSA" />
    </SSLHostConfig>
</Connector>

Relative paths start at $CATALINA_BASE. If you have one file with your certificate followed by the chain, such as Let's Encrypt's fullchain.pem, point certificateFile at it and leave out certificateChainFile: in our test Tomcat then still sent the full chain. The UpgradeProtocol line adds HTTP/2; curl connected with HTTP/2 over TLS 1.3.

3. Option B: a PKCS12 keystore

Use this if you already have a .p12 or .pfx file (for example exported from Windows; see convert a certificate to PFX), or create one from the PEM files:

openssl pkcs12 -export -in example.com.crt -inkey example.com.key -certfile chain.pem -name tomcat -out example.com.p12

OpenSSL asks for an export password. Check the file with keytool -list -keystore example.com.p12 -storetype PKCS12; ours showed Keystore type: PKCS12 and one PrivateKeyEntry named tomcat. Then use this Certificate line instead of the PEM one:

<Certificate certificateKeystoreFile="conf/ssl/example.com.p12"
             certificateKeystorePassword="YOUR-EXPORT-PASSWORD"
             certificateKeystoreType="PKCS12"
             certificateKeyAlias="tomcat"
             type="RSA" />

Tomcat's documented default keystore type is JKS, so set certificateKeystoreType. Converting to JKS as older guides do is not needed: keytool now warns that "The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12".

4. Restart and test

  1. Restart: sudo systemctl restart tomcat11, then check journalctl -u tomcat11 for a line like Starting ProtocolHandler ["https-jsse-nio-127.0.0.1-8443"] (our lab Connector listened on 127.0.0.1 only, which is why the address is in the name).
  2. Test from the server or your computer: curl -I https://example.com:8443/ should return a 200 or 302 status without a certificate error.
  3. Check that the chain is complete:
    $ openssl s_client -connect example.com:8443 -servername example.com </dev/null
     0 s:CN=example.com
       i:CN=Lab Test CA
     1 s:CN=Lab Test CA
    ...
    Verify return code: 0 (ok)
    Lab output, shortened; our certificate came from a test CA. A real certificate shows your CA's intermediate as certificate 1. If the chain is missing or the certificate is self-signed, clients report errors such as curl's (60) SSL certificate problem.
  4. Open port 8443 in the firewall if visitors connect to it directly.

5. Redirect HTTP to HTTPS and use port 443

The 8080 Connector in the default server.xml already has redirectPort="8443". To send every request there, add this before </web-app> in conf/web.xml (all applications) or in your application's WEB-INF/web.xml:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Everything</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

In our test, http://127.0.0.1:8080/ then answered 302 with Location: https://127.0.0.1:8443/.

For the normal port 443, change port="8443" and redirectPort to 443. A program running as a normal user cannot open ports below 1024 (our Tomcat started as the tomcat user without extra rights failed with java.net.BindException: Permission denied); Debian 13's tomcat11.service unit file grants that right with AmbientCapabilities=CAP_NET_BIND_SERVICE, so port 443 should work when Tomcat runs as that service (our container had no systemd, so we could not test this part).

The alternative: HTTPS on Nginx or Apache in front

The Tomcat documentation notes that SSL in Tomcat is usually only needed when Tomcat runs as a stand-alone web server; behind another web server, that server handles SSL. In that setup Nginx or Apache holds the certificate on port 443 (renewals with Certbot then work as for any website) and passes requests to Tomcat on 127.0.0.1:8080. Add this inside the <Host> element of server.xml so Tomcat knows the visitor used HTTPS:

<Valve className="org.apache.catalina.valves.RemoteIpValve" />

According to Apache, it reads the X-Forwarded-Proto header and trusts proxies on 127.0.0.0/8 by default; the proxy must send that header (proxy_set_header X-Forwarded-Proto $scheme; in Nginx). See Nginx as a reverse proxy with SSL or Apache as a reverse proxy.

6. Let's Encrypt renewals for Tomcat itself

According to Certbot, its folders are readable by root only, so the tomcat user cannot read privkey.pem there. Copy the files after each renewal with a deploy hook, an executable file in /etc/letsencrypt/renewal-hooks/deploy/, for example tomcat.sh:

#!/bin/sh
install -m 640 -g tomcat /etc/letsencrypt/live/example.com/fullchain.pem /etc/tomcat11/ssl/fullchain.pem
install -m 640 -g tomcat /etc/letsencrypt/live/example.com/privkey.pem /etc/tomcat11/ssl/privkey.pem
systemctl restart tomcat11

Point certificateFile and certificateKeyFile at those two copies.

Errors we saw and what they mean

In each case Tomcat kept running on 8080, but nothing listened on 8443:

  • failed to set property [keystoreFile], then No SSLHostConfig element was found with the hostName [_default_]: an old-style Connector. Move the settings into SSLHostConfig and Certificate as shown above.
  • java.io.IOException: keystore password was incorrect: wrong certificateKeystorePassword.
  • java.io.FileNotFoundException: /var/lib/tomcat11/conf/ssl/example.com.key (Permission denied): the tomcat user cannot read the key; fix the group and mode as in step 1.
  • java.net.BindException: Permission denied: a port below 1024 without the capability (step 5).

Official documentation: Apache Tomcat 11 "SSL/TLS Configuration How-To" and "The HTTP Connector" reference on tomcat.apache.org, and the Certbot user guide.

Related services

KVM VPS hosting

NVMe SSD and full root access, with Free Basic Managed Support.

Unmanaged dedicated servers

full root access and free IPMI KVM, with Free Basic Managed Support.

Managed dedicated servers

our engineers look after the server for you, from $135 a month.

Was this answer helpful?

Still stuck? Open a ticket