# TLS for OpenHTTPd This guide shows you how to enable [TLS|tls/overview]] for [OpenHTTPd](https://bsd.plumbing/about.html). It assumes you have already set up [[plaintext OpenHTTPd](/openhttpd/configure) listening on port 80, and you have successfully requested TLS certs using [[acme-client](/acme-client/configure). This guide shows you how to enable [TLS|tls/overview]] for [OpenHTTPd](https://bsd.plumbing/about.html). It assumes you have already set up [[plaintext OpenHTTPd](/openhttpd/configure) listening on port 80, and you have successfully requested TLS certs using [[acme-client](/acme-client/configure). ## Docs and references Consult [httpd|https://man.openbsd.org/httpd]], [httpd.conf|https://man.openbsd.org/httpd.conf]], [[acme-client](https://man.openbsd.org/acme-client), and [[acme-client.conf](/https://man.openbsd.org/acme-client) man pages. [[Httpd and Relayd Mastery](/https://www.tiltedwindmillpress.com/product/httpd-and-relayd-mastery/) also contains many helpful examples. Consult [httpd|https://man.openbsd.org/httpd]], [httpd.conf|https://man.openbsd.org/httpd.conf]], [[acme-client](https://man.openbsd.org/acme-client), and [[acme-client.conf](/https://man.openbsd.org/acme-client) man pages. [[Httpd and Relayd Mastery](/https://www.tiltedwindmillpress.com/product/httpd-and-relayd-mastery/) also contains many helpful examples. ## Configuring In the previous guide, we used /etc/examples/httpd.conf as a template for /etc/httpd.conf: server "example.com" { listen on * port 80 location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } location * { block return 302 "https://$HTTP_HOST$REQUEST_URI" } } server "example.com" { listen on * tls port 443 tls { certificate "/etc/ssl/example.com.crt" key "/etc/ssl/private/example.com.key" } location "/pub/*" { directory auto index } location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } } **NOTE**: You must replace example.com with your own domain We commented out the second block in the [basic OpenHTTPd guide](openhttpd/configure) because we did not yet request TLS certs yet. Now that we have certs from [[acme-client](/acme-client/configure), we uncomment the second block. We commented out the second block in the [basic OpenHTTPd guide](openhttpd/configure) because we did not yet request TLS certs yet. Now that we have certs from [[acme-client](/acme-client/configure), we uncomment the second block. ## TLS Block Explained Here is a line-by-line description of the TLS block: server "example.com" { listen on * tls port 443 tls { certificate "/etc/ssl/example.com.crt" key "/etc/ssl/private/example.com.key" } location "/pub/*" { directory auto index } location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } } Lines 2-6 tells the web server to listen on all IPs on port 443. As a result, we need a tls block to specify which SSL certs to use. Again, it is necessary to replace `example.com` with your actual hostname. Lines 7-9 say that, for any request beginning with https://example.com/pub/, the web server should automatically show a directory listing. Normally this is not a good idea for security reasons, but for a public folder, it should be fine. In a normal production server, if OpenHTTPd is already running, reloading is best to avoid downtime: $ doas rcctl reload httpd For your first test however, you will want to [stop OpenHTTPd](/rcctl/usage): $ doas rcctl stop httpd Then, check that your configuration is valid: $ doas httpd -n Once you are certain it has been configured properly, you can start the server: $ doas rcctl start httpd ## Testing To test if your web server has a working SSL cert, use [openssl](/openssl/http): $ openssl s_client -connect example.com:443 **NOTE**: You must replace `example.com` with your actual hostname. You should see the correct SSL subject and issuer: $ openssl s_client -connect example.org:443 CONNECTED(00000003) depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3 verify return:1 depth=1 C = US, O = Let's Encrypt, CN = R3 verify return:1 depth=0 CN = example.com verify return:1 depth=0 CN = example.com verify return:1 write W BLOCK --- Certificate chain 0 s:/CN=example.com i:/C=US/O=Let's Encrypt/CN=R3 1 s:/C=US/O=Let's Encrypt/CN=R3 i:/O=Digital Signature Trust Co./CN=DST Root CA X3 --- Server certificate -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- subject=/CN=example.com issuer=/C=US/O=Let's Encrypt/CN=R3 --- No client certificate CA names sent Server Temp Key: ECDH, X25519, 253 bits --- SSL handshake has read 3730 bytes and written 367 bytes --- New, TLSv1/SSLv3, Cipher is AEAD-AES256-GCM-SHA384 Server public key is 4096 bit Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.3 Cipher : AEAD-AES256-GCM-SHA384 Session-ID: Session-ID-ctx: Master-Key: Start Time: 1614233943 Timeout : 7200 (sec) Verify return code: 0 (ok) --- You can also visit the website using your web browser. Open your web browser to `https://example.com`. If you see an error such as 403 Forbidden, it may mean you have not [set up a website](/openhttpd/website). Look for the SSL padlock in the address bar (which indicates your site is secure), then view more information about the certificate: Attach:ssl-cert.png ## Automation Let's Encrypt TLS certs expire after 90 days. As a result, you are highly encouraged to automate the renewal of TLS certs. Otherwise, once a cert expires, your users may no longer be able to visit your site. We can automate the request process using [crontab](/crontab/edit). $ doas crontab -e Add this line at the bottom: ~ ~ * * * acme-client example.com >> /var/log/acme-client.log 2>&1 && sleep 300 && rcctl reload httpd This cronjob will check the certificate once each day at a random time to see if it needs to be renewed. If it does, it will renew the cert, wait 300 seconds, then reload openhttpd to use it. ### Troubleshooting If you were unable to establish the connection above, it may be because your [firewall](/pf/guide) is blocking port 443. You can ensure pf allows incoming http connections by putting this line into /etc/pf.conf: pass in quick proto tcp to port {http https} Then, reload the pf rulesets: $ doas pfctl -f /etc/pf.conf