This error looks like a SOAP parsing problem, but in practice it means your client never received a SOAP response at all. Something between your client and the SOAP service returned HTML instead of XML.
Content Type text/html; charset=utf-8 was not supported by service
Your SOAP client expected text/xml or application/soap+xml. Instead it got text/html — a login page, an error page, a gateway warning, or a WAF block page. The client library cannot parse HTML as a SOAP envelope, so it throws this content-type error.
Diagnostic flowchart
Work through these causes in order. Each one is more common than the next in enterprise environments.
Cause 1: Wrong endpoint URL
The most basic cause. Developers often send SOAP requests to the WSDL URL or a documentation page.
Wrong: https://example.com/service.svc?wsdl
Wrong: https://example.com/services/
Right: https://example.com/service.svc
Find the correct URL from the WSDL:
<wsdl:service name="UserService">
<wsdl:port name="UserServiceSoap" binding="tns:UserServiceSoap">
<soap:address location="https://example.com/service.svc" />
</wsdl:port>
</wsdl:service>
The location attribute of <soap:address> is the actual service endpoint.
Cause 2: HTTP redirect to a login page
Extremely common behind enterprise SSO gateways, ADFS, Okta, or Azure AD. Your SOAP client follows a 302 redirect to a login page and receives HTML.
# Check for redirects
curl -v -o /dev/null https://example.com/service.svc 2>&1 | grep -E "< HTTP|< Location"
If you see:
< HTTP/1.1 302 Found
< Location: https://sso.example.com/login?returnUrl=...
The SOAP service is behind an authentication gateway. You need to either:
- Provide credentials that satisfy the gateway (Basic Auth, client certificate, bearer token)
- Allowlist the service endpoint in the gateway configuration
- Use a direct URL that bypasses the SSO layer for service-to-service calls
Example: SSO login page HTML your client might receive
<!DOCTYPE html>
<html>
<head><title>Sign In - Corporate SSO</title></head>
<body>
<form action="/auth/login" method="POST">
<input type="text" name="username" />
<input type="password" name="password" />
<button type="submit">Sign In</button>
</form>
</body>
</html>
Your SOAP client tries to parse this as XML and throws the content-type error.
Cause 3: Reverse proxy or load balancer returning HTML
When the upstream SOAP service is down, the proxy layer often returns its own HTML error page. Each proxy type produces different HTML.
nginx (502 Bad Gateway):
<html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx/1.24.0</center>
</body>
</html>
F5 BIG-IP (connection error):
<html>
<head><title>BIG-IP Maintenance</title></head>
<body>The server is temporarily unable to service your request.</body>
</html>
AWS ALB (503 Service Unavailable):
<html>
<body><h1>503 Service Temporarily Unavailable</h1></body>
</html>
In all cases, the real problem is upstream availability, not the SOAP request itself.
Cause 4: WAF or API gateway blocking the request
Web Application Firewalls and API gateways may block SOAP requests that look suspicious. The block page is HTML.
Signs of WAF blocking:
- HTTP 403 with an HTML body
- A "Request blocked" or "Access denied" page
- A CAPTCHA challenge page
- A Cloudflare or Akamai challenge page
# Check if a WAF is intercepting
curl -s -D - \
-H "Content-Type: text/xml; charset=utf-8" \
-H 'SOAPAction: "http://tempuri.org/GetUser"' \
-d @request.xml \
https://example.com/service.svc | head -20
Look for Server: cloudflare, X-WAF-* headers, or HTML bodies with security vendor names.
Debugging with different languages
curl (baseline verification)
Always start with curl to isolate client library behavior from network issues.
curl -v \
-H "Content-Type: text/xml; charset=utf-8" \
-H 'SOAPAction: "http://tempuri.org/GetUser"' \
-d '<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetUser xmlns="http://tempuri.org/">
<userId>1</userId>
</GetUser>
</soap:Body>
</soap:Envelope>' \
https://example.com/service.svc
The -v flag shows the full HTTP conversation: redirects, response headers, and the first few lines of the body. If the response Content-Type is text/html, read the HTML body to identify the source.
Python (requests)
import requests
url = "https://example.com/service.svc"
headers = {
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": '"http://tempuri.org/GetUser"',
}
body = """<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetUser xmlns="http://tempuri.org/">
<userId>1</userId>
</GetUser>
</soap:Body>
</soap:Envelope>"""
# Disable redirect following to catch auth redirects
response = requests.post(url, data=body, headers=headers, allow_redirects=False)
print(f"Status: {response.status_code}")
print(f"Content-Type: {response.headers.get('Content-Type')}")
print(f"Location: {response.headers.get('Location', 'N/A')}")
if "text/html" in response.headers.get("Content-Type", ""):
print("ERROR: Received HTML instead of XML")
print(f"Body preview: {response.text[:500]}")
Key: set allow_redirects=False to see the redirect before the client follows it. This makes auth-redirect issues immediately visible.
Node.js (axios)
const axios = require("axios");
const url = "https://example.com/service.svc";
const soapBody = `<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetUser xmlns="http://tempuri.org/">
<userId>1</userId>
</GetUser>
</soap:Body>
</soap:Envelope>`;
axios
.post(url, soapBody, {
headers: {
"Content-Type": "text/xml; charset=utf-8",
SOAPAction: '"http://tempuri.org/GetUser"',
},
maxRedirects: 0, // Do not follow redirects
validateStatus: () => true, // Accept any status code
})
.then((res) => {
console.log(`Status: ${res.status}`);
console.log(`Content-Type: ${res.headers["content-type"]}`);
if (res.headers["content-type"]?.includes("text/html")) {
console.log("ERROR: Received HTML instead of XML");
console.log(`Body preview: ${res.data.substring(0, 500)}`);
}
});
Key: set maxRedirects: 0 to prevent silently following redirects.
The HTML title tag shortcut
When you get an HTML response, the <title> tag usually tells you the real problem in one line:
| Title tag content | Likely cause | Action |
|---|---|---|
| "Sign In" / "Login" | SSO redirect | Fix auth credentials or bypass |
| "Access Denied" / "403 Forbidden" | WAF or authorization | Check IP allowlisting or credentials |
| "404 Not Found" | Wrong URL | Verify <soap:address> in WSDL |
| "502 Bad Gateway" | Upstream down | Check service health |
| "503 Service Unavailable" | Upstream overloaded | Wait and retry |
| "Request Blocked" | WAF rule triggered | Review WAF logs |
This is much faster than parsing the SOAP client exception.
How SOAPless avoids this problem entirely
With SOAPless, the SOAP request is made server-side from SOAPless infrastructure. This means:
- The request never passes through the caller's corporate proxy, SSO gateway, or WAF
- SOAPless connects directly to the SOAP service endpoint registered in the dashboard
- If the upstream returns HTML instead of XML, SOAPless detects the content-type mismatch and returns a clear JSON error explaining what happened — instead of a cryptic content-type exception
- Your downstream consumers call a REST JSON endpoint and never encounter
text/htmlresponses
The entire class of "something between me and the SOAP service returned HTML" problems disappears because SOAPless removes those intermediate layers from the request path.