Docs
Apache APISIXManage traffic and APIsTraffic ManagementTLS and mTLSConfigure HTTPS Between APISIX and Upstream

Configure HTTPS Between APISIX and Upstream

Route Apache APISIX traffic to a controlled HTTPS upstream, prove the TLS transport and exact response, and clean up the test resources.

Applies to:Apache APISIX 3.18.0

Deployments:DockerRoles:API developerPlatform engineerTime:20 minutesImpact:Changes live traffic

Goal

Route a fixed APISIX path to a pinned local NGINX upstream over HTTPS. Verify the sample server certificate independently and require the exact upstream response through APISIX.

The APISIX Route in this guide proves encrypted transport to the upstream; it does not by itself prove that APISIX validates the upstream certificate identity. Configure the deployment-wide upstream trust policy separately when server authentication is required.

Before you begin

  • Complete Get APISIX with the Docker quickstart.
  • Install cURL, jq, and OpenSSL.

Set the endpoints and Admin API key:

export APISIX_ADMIN_URL="http://127.0.0.1:9180"
export GATEWAY_URL="http://127.0.0.1:9080"
export GUIDE_DIR="$(pwd)"
read -rsp "Admin API key (press Enter for an unchanged quickstart): " APISIX_ADMIN_KEY && export APISIX_ADMIN_KEY && echo

Protect the fixed Route, container, and local directory:

test "$(curl -sS --connect-timeout 3 --max-time 10 -o /dev/null -w '%{http_code}' "${APISIX_ADMIN_URL}/apisix/admin/routes/tls-upstream-guide-route" -H "X-API-KEY: ${APISIX_ADMIN_KEY}")" = 404 || { echo 'Stop: route tls-upstream-guide-route already exists'; exit 1; }
test -z "$(docker ps -aq --filter name=^/tls-upstream-guide$)" || { echo 'Stop: container tls-upstream-guide already exists'; exit 1; }
test ! -e tls-upstream-guide || { echo 'Stop: directory tls-upstream-guide already exists'; exit 1; }

Steps

Step 1: Issue a short-lived upstream certificate

Create a permission-restricted directory, private test CA, and seven-day server certificate for the container DNS name:

umask 077
mkdir tls-upstream-guide
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -days 7 \
  -keyout tls-upstream-guide/ca.key -out tls-upstream-guide/ca.crt \
  -subj '/CN=APISIX Upstream TLS Guide CA'
openssl req -new -newkey rsa:2048 -nodes -sha256 \
  -keyout tls-upstream-guide/server.key -out tls-upstream-guide/server.csr \
  -subj '/CN=tls-upstream-guide'
cat > tls-upstream-guide/server.ext <<'EOF'
basicConstraints=critical,CA:FALSE
keyUsage=critical,digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectAltName=DNS:tls-upstream-guide
EOF
openssl x509 -req -sha256 -days 7 \
  -in tls-upstream-guide/server.csr \
  -CA tls-upstream-guide/ca.crt -CAkey tls-upstream-guide/ca.key -CAcreateserial \
  -extfile tls-upstream-guide/server.ext -out tls-upstream-guide/server.crt
openssl verify -CAfile tls-upstream-guide/ca.crt tls-upstream-guide/server.crt

Expected: OpenSSL prints tls-upstream-guide/server.crt: OK.

Step 2: Start the HTTPS upstream

Create a minimal NGINX configuration that exposes only the test path:

cat > tls-upstream-guide/nginx.conf <<'EOF'
events {}
http {
    server {
        listen 8443 ssl;
        server_name tls-upstream-guide;
        ssl_certificate /etc/nginx/tls/server.crt;
        ssl_certificate_key /etc/nginx/tls/server.key;

        location = /tls-upstream-guide {
            default_type text/plain;
            return 200 "tls-upstream-guide\n";
        }
    }
}
EOF
docker run -d --name tls-upstream-guide \
  --network apisix-quickstart-net \
  -v "${GUIDE_DIR}/tls-upstream-guide/nginx.conf:/etc/nginx/nginx.conf:ro" \
  -v "${GUIDE_DIR}/tls-upstream-guide:/etc/nginx/tls:ro" \
  nginx:1.29-alpine
for attempt in {1..15}; do
  docker exec tls-upstream-guide nginx -t >/dev/null 2>&1 && break
  sleep 1
done
docker exec tls-upstream-guide nginx -t

Independently verify the sample upstream's certificate, hostname, and exact body from the Docker network:

docker run --rm --network apisix-quickstart-net \
  -v "${GUIDE_DIR}/tls-upstream-guide/ca.crt:/tmp/ca.crt:ro" \
  curlimages/curl:8.17.0 --fail-with-body -sS \
  --cacert /tmp/ca.crt https://tls-upstream-guide:8443/tls-upstream-guide | \
  grep -Fqx 'tls-upstream-guide'

Step 3: Create an HTTPS Route

Create a fixed-ID Route with scheme: https. Do not put https:// in the node key when the scheme is already configured:

curl -sS --connect-timeout 3 --max-time 15 "${APISIX_ADMIN_URL}/apisix/admin/routes/tls-upstream-guide-route" -X PUT \
  -H "X-API-KEY: ${APISIX_ADMIN_KEY}" -H "Content-Type: application/json" \
  -d '{
    "uri": "/tls-upstream-guide",
    "upstream": {
      "type": "roundrobin",
      "scheme": "https",
      "nodes": {"tls-upstream-guide:8443": 1}
    }
  }' | jq -e '.value.id == "tls-upstream-guide-route" and .value.upstream.scheme == "https"'

Validate

Require the exact response through APISIX:

test "$(curl --fail-with-body -sS --connect-timeout 3 --max-time 15 "${GATEWAY_URL}/tls-upstream-guide")" = "tls-upstream-guide"

Expected: the assertion exits successfully. NGINX listens only with TLS on port 8443, so the response plus the Route readback demonstrates that APISIX used HTTPS to the intended node.

Roll back / Clean up

Delete the fixed Route first, then remove the container and local certificate material:

curl -sS --connect-timeout 3 --max-time 15 -X DELETE "${APISIX_ADMIN_URL}/apisix/admin/routes/tls-upstream-guide-route" -H "X-API-KEY: ${APISIX_ADMIN_KEY}"
docker rm -f tls-upstream-guide
rm -f tls-upstream-guide/ca.key tls-upstream-guide/ca.crt tls-upstream-guide/ca.srl \
  tls-upstream-guide/server.key tls-upstream-guide/server.csr tls-upstream-guide/server.crt \
  tls-upstream-guide/server.ext tls-upstream-guide/nginx.conf
rmdir tls-upstream-guide
unset APISIX_ADMIN_URL APISIX_ADMIN_KEY GATEWAY_URL GUIDE_DIR

Troubleshoot

SymptomCauseFix
The direct cURL container rejects the certificate.The CA mount is wrong or the certificate SAN does not match the container name.Verify the mounted CA and inspect subjectAltName before creating the APISIX Route.
APISIX returns 502.NGINX is not ready, the container is on another network, or the node name or port differs.Require nginx -t, inspect the container network, and read back the fixed Route.
APISIX returns 404.Another Route matched or the path differs.Request /tls-upstream-guide and confirm the fixed Route URI.
Cleanup fails because the directory is not empty.An unexpected artifact was created under the protected directory.Inspect it and remove only files created by this guide; do not use a recursive broad deletion.