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

Configure HTTPS Between Client and APISIX

Configure a trusted test certificate for HTTPS between a client and Apache APISIX, verify the certificate and upstream response, and remove every test resource.

Applies to:Apache APISIX 3.18.0

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

Goal

Configure APISIX to present a locally issued certificate for tls-guide.local. Verify both the server certificate and an exact response from a controlled upstream without disabling TLS verification.

This procedure is for an isolated APISIX quickstart. Use certificates from the organization's approved certificate authority and secret-management workflow in production.

Before you begin

  • Complete Get APISIX with the Docker quickstart.
  • Install cURL, jq, and OpenSSL.
  • Confirm port 9443 from the quickstart is reachable only from the intended test machine.

Set the endpoints and Admin API key:

export APISIX_ADMIN_URL="http://127.0.0.1:9180"
export GATEWAY_TLS_URL="https://tls-guide.local:9443"
read -rsp "Admin API key (press Enter for an unchanged quickstart): " APISIX_ADMIN_KEY && export APISIX_ADMIN_KEY && echo

Require the Admin API and protect every fixed resource and local artifact:

test "$(curl -sS --connect-timeout 3 --max-time 10 -o /dev/null -w '%{http_code}' "${APISIX_ADMIN_URL}/apisix/admin/routes/tls-client-guide-route" -H "X-API-KEY: ${APISIX_ADMIN_KEY}")" = 404 || { echo 'Stop: route tls-client-guide-route already exists'; exit 1; }
test "$(curl -sS --connect-timeout 3 --max-time 10 -o /dev/null -w '%{http_code}' "${APISIX_ADMIN_URL}/apisix/admin/ssls/tls-client-guide-ssl" -H "X-API-KEY: ${APISIX_ADMIN_KEY}")" = 404 || { echo 'Stop: SSL tls-client-guide-ssl already exists'; exit 1; }
test -z "$(docker ps -aq --filter name=^/tls-client-guide-upstream$)" || { echo 'Stop: container tls-client-guide-upstream already exists'; exit 1; }
for file in tls-guide-ca.key tls-guide-ca.crt tls-guide-server.key tls-guide-server.csr tls-guide-server.crt tls-guide-server.ext tls-guide-ca.srl tls-guide-body.txt; do
  test ! -e "${file}" || { echo "Stop: ${file} already exists"; exit 1; }
done

Steps

Step 1: Start a controlled upstream

Run a pinned local echo server on the quickstart network:

docker run -d --name tls-client-guide-upstream \
  --network apisix-quickstart-net \
  hashicorp/http-echo:1.0.0 -listen=:5678 -text=tls-client-guide-upstream
for attempt in {1..15}; do
  docker exec apisix-quickstart bash -c '</dev/tcp/tls-client-guide-upstream/5678' >/dev/null 2>&1 && break
  sleep 1
done
docker exec apisix-quickstart bash -c '</dev/tcp/tls-client-guide-upstream/5678'

Create a fixed-ID Route with Admin API authentication:

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

Step 2: Issue a short-lived test certificate

Create a private test CA and a seven-day server certificate. The Subject Alternative Name, not only the Common Name, binds the certificate to tls-guide.local:

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

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

Step 3: Create the APISIX SSL object

Build the JSON with jq so certificate newlines are escaped correctly, then create a fixed-ID SSL object:

jq -n \
  --arg sni 'tls-guide.local' \
  --arg cert "$(cat tls-guide-server.crt)" \
  --arg key "$(cat tls-guide-server.key)" \
  '{sni: $sni, cert: $cert, key: $key}' | \
curl -sS --connect-timeout 3 --max-time 15 "${APISIX_ADMIN_URL}/apisix/admin/ssls/tls-client-guide-ssl" -X PUT \
  -H "X-API-KEY: ${APISIX_ADMIN_KEY}" -H "Content-Type: application/json" \
  --data-binary @- | jq -e '.value.id == "tls-client-guide-ssl" and .value.sni == "tls-guide.local"'

The private key is sent only to the local Admin API in this disposable setup. Do not print the request body or commit the generated files.

Validate

Send a request while explicitly trusting only the test CA and resolving the test hostname locally:

curl --fail-with-body -sS --connect-timeout 3 --max-time 15 \
  --cacert tls-guide-ca.crt \
  --resolve 'tls-guide.local:9443:127.0.0.1' \
  -o tls-guide-body.txt \
  "${GATEWAY_TLS_URL}/tls-client-guide"
test "$(cat tls-guide-body.txt)" = "tls-client-guide-upstream"

Expected: both commands exit successfully. cURL's certificate verification proves that APISIX presented a certificate issued by the selected CA for tls-guide.local; the exact body proves that the TLS request reached the intended upstream.

Do not add --insecure or -k. Either option would bypass the certificate and hostname checks this guide is meant to prove.

Roll back / Clean up

Delete only the fixed resources created by this guide, remove the upstream, and securely remove local private-key material:

curl -sS --connect-timeout 3 --max-time 15 -X DELETE "${APISIX_ADMIN_URL}/apisix/admin/routes/tls-client-guide-route" -H "X-API-KEY: ${APISIX_ADMIN_KEY}"
curl -sS --connect-timeout 3 --max-time 15 -X DELETE "${APISIX_ADMIN_URL}/apisix/admin/ssls/tls-client-guide-ssl" -H "X-API-KEY: ${APISIX_ADMIN_KEY}"
docker rm -f tls-client-guide-upstream
rm -f tls-guide-ca.key tls-guide-ca.crt tls-guide-server.key tls-guide-server.csr \
  tls-guide-server.crt tls-guide-server.ext tls-guide-ca.srl tls-guide-body.txt
unset APISIX_ADMIN_URL APISIX_ADMIN_KEY GATEWAY_TLS_URL

Expected: both Admin API resources and the container are absent, and no generated key or certificate remains in the working directory.

Troubleshoot

SymptomCauseFix
cURL reports an unknown certificate authority.--cacert points to another CA, or APISIX serves another certificate.Use tls-guide-ca.crt, read back the fixed SSL object, and confirm its SNI and certificate before retrying.
cURL reports a hostname mismatch.The request hostname is not tls-guide.local, or the server certificate lacks the matching SAN.Keep the URL and --resolve hostname aligned and inspect the certificate with openssl x509 -text -noout.
The response is 404.Another Route matched or the fixed Route was not created.Read back tls-client-guide-route and request the exact /tls-client-guide path.
The TLS request succeeds but the body differs.The Route points to another upstream.Read back the Route and confirm the node is tls-client-guide-upstream:5678.
Cleanup of the SSL object fails.The Admin API key is wrong or the object ID differs.Correct authentication and delete only tls-client-guide-ssl; do not delete all SSL objects.