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
9443from 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 && echoRequire 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; }
doneSteps
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.crtExpected: 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_URLExpected: both Admin API resources and the container are absent, and no generated key or certificate remains in the working directory.
Troubleshoot
| Symptom | Cause | Fix |
|---|---|---|
| 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. |