Safeguard
Security

NGINX Ingress TLS: How to Terminate HTTPS Securely on Kubernetes

Configuring NGINX Ingress TLS means wiring up certificates, secrets, and protocol settings so traffic into your cluster is encrypted and hard to downgrade. Here is the secure setup.

Karan Patel
Platform Engineer
5 min read

NGINX Ingress TLS is how you terminate HTTPS at the edge of a Kubernetes cluster: the ingress-nginx controller presents a certificate, decrypts incoming traffic, and routes the plaintext request to the right service, all driven by an Ingress resource and a TLS secret. Getting it right means more than pasting a certificate into a secret. It means automating renewal, pinning modern protocols, and forcing every request onto the encrypted path so nothing quietly falls back to HTTP.

This is about the community ingress-nginx controller, the one most clusters run. The mechanics are the same whether your certificate comes from Let's Encrypt, an internal CA, or a commercial issuer.

The certificate secret and the Ingress

TLS termination needs two things: a Kubernetes secret of type kubernetes.io/tls holding the certificate and private key, and an Ingress resource whose tls block references it for the hostnames it serves.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app.example.com
      secretName: app-example-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app-svc
                port:
                  number: 80

The secretName must point at a secret in the same namespace as the Ingress. A frequent failure is a working certificate in the wrong namespace, which the controller silently falls back from, serving its default self-signed certificate instead. If browsers see a "Kubernetes Ingress Controller Fake Certificate," that mismatch is almost always the cause.

Automate renewal with cert-manager

Manually rotating certificates is how outages happen at 2 a.m. when one expires. cert-manager is the standard fix: it issues and renews certificates automatically and writes them into the TLS secret your Ingress already references.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: security@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
      - http01:
          ingress:
            class: nginx

Add the annotation cert-manager.io/cluster-issuer: letsencrypt-prod to the Ingress and cert-manager provisions the certificate, completes the ACME challenge through the same ingress, and renews well before expiry. Watch the Certificate resource's status; a stuck ACME challenge usually means the HTTP-01 path is not reachable from the public internet.

Harden the protocols

Terminating TLS is only half the job; terminating it with weak settings leaves you exposed to downgrade and protocol attacks. Configure the controller, through its ConfigMap, to offer only modern protocols and strong ciphers:

apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  ssl-protocols: "TLSv1.2 TLSv1.3"
  ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
  ssl-prefer-server-ciphers: "true"

Drop TLS 1.0 and 1.1 entirely; they are deprecated and offer nothing modern clients need. Prefer server cipher ordering so a client cannot negotiate down to the weakest mutually supported suite. If you must state a range, keep it to TLS 1.2 and above and verify with a scan afterward.

Force HTTPS and add HSTS

An encrypted endpoint that also answers plaintext HTTP is a downgrade waiting to happen. The ssl-redirect: "true" annotation makes the controller 308-redirect HTTP to HTTPS. Layer HTTP Strict Transport Security on top so browsers refuse plaintext for your domain after the first visit:

data:
  hsts: "true"
  hsts-max-age: "31536000"
  hsts-include-subdomains: "true"

Only enable hsts-include-subdomains once every subdomain genuinely serves HTTPS, since the browser will refuse plaintext for all of them. HSTS is sticky by design, so a mistake here is hard to unwind.

Verify what you shipped

Do not trust the YAML; test the endpoint. Check the negotiated protocol and certificate chain from outside the cluster:

openssl s_client -connect app.example.com:443 -servername app.example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -issuer

A DAST scan against the live host confirms the redirect fires, the HSTS header is present, and no weak protocol is still offered, which is the honest end-to-end check that the pieces line up. Keep the ingress-nginx controller itself patched, since it is internet-facing infrastructure and has shipped security advisories; our software composition analysis coverage explains how to track the controller image and its dependencies for known issues.

FAQ

Why does my Ingress serve a fake certificate?

The controller falls back to its default self-signed certificate when it cannot find the TLS secret named in the Ingress. Confirm the secret exists, is type kubernetes.io/tls, and lives in the same namespace as the Ingress.

Do I need cert-manager for NGINX Ingress TLS?

No, but it is strongly recommended. Without it you manage certificate issuance and renewal by hand, which invites expiry outages. cert-manager writes renewed certificates straight into the secret your Ingress already uses.

How do I force all traffic to HTTPS?

Set the nginx.ingress.kubernetes.io/ssl-redirect: "true" annotation so HTTP requests are redirected to HTTPS, and add HSTS through the controller ConfigMap so browsers refuse plaintext on return visits.

Which TLS versions should the ingress allow?

Offer only TLS 1.2 and TLS 1.3. Disable TLS 1.0 and 1.1, which are deprecated, and prefer server cipher ordering so clients cannot negotiate a weaker suite.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.