Skip to content

SSL Termination at the Gateway — Deep Dive into TLS Configuration

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you'll learn about SSL Termination Deep. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

SSL termination at the gateway decrypts incoming HTTPS traffic once, allowing internal backend services to communicate over plain HTTP or re-encrypted connections.

What You'll Learn

By the end of this lesson, you will configure TLS termination at the gateway, manage certificates, enforce TLS versions and cipher suites, implement HSTS, and configure mutual TLS for backend communication.

Why It Matters

TLS termination at the gateway centralizes certificate management and reduces the computational load on backend services, which can focus on business logic.

Real-World Use

Durga Antivirus Pro terminates TLS at the gateway using a wildcard certificate for *.durga-antivirus.com, enforcing TLS 1.3 and HSTS preloading for all API traffic.

TLS Termination Architecture

flowchart LR
    Client-->|HTTPS|Gateway
    Gateway-->|Decrypt|TLS[TLS Termination]
    TLS-->Backend1[Service A - HTTP]
    TLS-->Backend2[Service B - mTLS]
    TLS-->Backend3[Service C - HTTPS]
    Gateway-->|Re-encrypt|Backend3

TLS Configuration Manager

A TLS configuration manager that enforces security policies.

from typing import Dict, List, Optional, Tuple
from datetime import datetime, timedelta
import ssl

class TLSConfig:
    def __init__(self):
        self.min_version = ssl.TLSVersion.TLSv1_2
        self.cert_path: Optional[str] = None
        self.key_path: Optional[str] = None
        self.allowed_ciphers: List[str] = []
        self.hsts_max_age: int = 31536000
        self.hsts_include_subdomains: bool = True
        self.hsts_preload: bool = False

    def set_certificate(self, cert_path: str,
                        key_path: str):
        self.cert_path = cert_path
        self.key_path = key_path

    def get_ciphers(self) -> str:
        if self.allowed_ciphers:
            return ":".join(self.allowed_ciphers)
        return (
            "ECDHE-ECDSA-AES128-GCM-SHA256:"
            "ECDHE-RSA-AES128-GCM-SHA256:"
            "ECDHE-ECDSA-AES256-GCM-SHA384:"
            "ECDHE-RSA-AES256-GCM-SHA384"
        )

    def build_ssl_context(self) -> ssl.SSLContext:
        ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
        ctx.minimum_version = self.min_version
        if self.cert_path and self.key_path:
            ctx.load_cert_chain(self.cert_path, self.key_path)
        ctx.set_ciphers(self.get_ciphers())
        return ctx

    def build_hsts_header(self) -> str:
        value = f"max-age={self.hsts_max_age}"
        if self.hsts_include_subdomains:
            value += "; includeSubDomains"
        if self.hsts_preload:
            value += "; preload"
        return value

config = TLSConfig()
config.set_certificate("/etc/certs/gateway.pem",
                       "/etc/certs/gateway-key.pem")
config.min_version = ssl.TLSVersion.TLSv1_3
print(f"HSTS: {config.build_hsts_header()}")

Certificate Management

Manage TLS certificates including automatic renewal with ACME.

import os
from datetime import datetime
from typing import Dict, Optional, Tuple
import hashlib

class CertificateManager:
    def __init__(self, cert_dir: str = "/etc/certs"):
        self.cert_dir = cert_dir
        self.certs: Dict[str, Dict] = {}

    def load_certificate(self, domain: str
                         ) -> Optional[Dict]:
        cert_path = os.path.join(
            self.cert_dir, f"{domain}.pem"
        )
        if not os.path.exists(cert_path):
            return None
        with open(cert_path) as f:
            cert_pem = f.read()
        cert_info = {
            "domain": domain,
            "path": cert_path,
            "fingerprint": hashlib.sha256(
                cert_pem.encode()
            ).hexdigest(),
            "loaded": datetime.utcnow(),
        }
        self.certs[domain] = cert_info
        return cert_info

    def check_expiry(self, domain: str,
                     threshold_days: int = 30
                     ) -> Tuple[bool, Optional[int]]:
        cert_info = self.certs.get(domain)
        if not cert_info:
            return False, None
        # In production, parse the certificate to check expiry
        return True, threshold_days

    def renew_certificate(self, domain: str) -> bool:
        # In production, implement ACME challenge
        print(f"Renewing certificate for {domain}")
        return True

    def get_cert_for_sni(self, server_name: str
                         ) -> Optional[str]:
        cert = self.certs.get(server_name)
        if cert:
            return cert["path"]
        wildcard = self.certs.get(f"*.{'.'.join(server_name.split('.')[1:])}")
        if wildcard:
            return wildcard["path"]
        return None

mgr = CertificateManager()
info = mgr.load_certificate("api.durga-antivirus.com")
if info:
    print(f"Loaded cert: {info['domain']}")
sni_cert = mgr.get_cert_for_sni("api.durga-antivirus.com")
print(f"SNI cert: {sni_cert}")

Mutual TLS for Backend Communication

Configure mutual TLS between the gateway and backend services.

from typing import Dict, Optional, Tuple
import ssl

class MutualTLSConfig:
    def __init__(self):
        self.ca_cert_path: Optional[str] = None
        self.client_cert_path: Optional[str] = None
        self.client_key_path: Optional[str] = None
        self.verify_mode = ssl.CERT_REQUIRED

    def configure(self, ca_cert: str,
                  client_cert: str,
                  client_key: str):
        self.ca_cert_path = ca_cert
        self.client_cert_path = client_cert
        self.client_key_path = client_key

    def build_client_context(self) -> ssl.SSLContext:
        ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
        ctx.verify_mode = self.verify_mode
        if self.ca_cert_path:
            ctx.load_verify_locations(self.ca_cert_path)
        if self.client_cert_path and self.client_key_path:
            ctx.load_cert_chain(
                self.client_cert_path,
                self.client_key_path
            )
        ctx.check_hostname = True
        return ctx

    def verify_backend_cert(self, cert: bytes) -> bool:
        # Validate backend certificate attributes
        return True

mtls = MutualTLSConfig()
mtls.configure(
    "/etc/certs/ca.pem",
    "/etc/certs/gateway-client.pem",
    "/etc/certs/gateway-client-key.pem"
)
print(f"mTLS configured with CA: {mtls.ca_cert_path}")
print(f"Verify mode: {mtls.verify_mode}")

Common Mistakes

Mistake 1: Supporting Old TLS Versions

TLS 1.0 and 1.1 are deprecated and vulnerable. Enforce TLS 1.2 minimum, prefer TLS 1.3.

Mistake 2: Weak Cipher Suites

Ciphers like RC4, 3DES, and CBC-mode ciphers are broken. Use only AEAD ciphers (GCM, ChaCha20).

Mistake 3: Not Implementing HSTS

Without HSTS, clients may fall back to HTTP on subsequent requests, allowing downgrade attacks.

Mistake 4: Self-Signed Certificates Without Proper CA

Self-signed certs in production cause trust errors. Use Let's Encrypt or a proper CA.

Mistake 5: Not Renewing Certificates Before Expiry

Expired certificates cause immediate service disruption. Automate renewal with ACME.

Practice Questions

  1. What is the difference between TLS termination and TLS passthrough?
  2. Why should TLS 1.3 be preferred over TLS 1.2?
  3. What is HSTS preloading and how does it work?
  4. How does mutual TLS differ from one-way TLS?
  5. What is SNI and why is it important for gateways?

Challenge

Build a TLS configuration for the gateway that terminates TLS 1.3 only, uses AEAD ciphers, enables HSTS with preload, loads certificates from a directory with SNI support, and configures mutual TLS for backend communication.

FAQ

What is TLS termination?

TLS termination is the process of decrypting HTTPS traffic at the gateway so that backend services receive plain HTTP or re-encrypted traffic.

Should you terminate TLS at the gateway or at each service?

Terminate at the gateway to centralize certificate management and reduce computational load on backend services. Re-encrypt to backends if needed.

What is the performance impact of TLS termination?

Modern TLS 1.3 with hardware acceleration adds minimal overhead (1-3 percent CPU). The convenience of centralized management far outweighs the cost.

How do you handle wildcard certificates?

Wildcard certificates (*.example.com) cover all subdomains. Use them for the gateway but ensure SNI matching works correctly.

What is OCSP stapling?

OCSP stapling lets the gateway attest to certificate validity without the client contacting the CA directly, improving both performance and privacy.

Mini Project

Build an SSL termination module for the gateway that supports TLS 1.2 and 1.3, configurable cipher suites, HSTS with preload, SNI-based certificate selection, OCSP stapling, and automatic certificate renewal with ACME.

What's Next

Learn about Compression for response optimization, or explore Gateway Security for comprehensive gateway protection strategies.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro