How Axios Handles Unexpected HTML Error Pages

When consuming REST APIs, clients typically expect JSON responses. However, web servers, reverse proxies, and firewalls often return HTML error pages (such as 404, 502, or 504 status pages) during outages, route misconfigurations, or authentication redirects. This article explains how the Axios HTTP client processes unexpected HTML responses when expecting JSON, what happens behind the scenes, and how to handle these scenarios reliably in your application.

Default Axios Response Parsing

By default, Axios sets the responseType configuration to 'json'. Under the hood, Axios uses a default response transformer that attempts to parse incoming response data using JSON.parse().

If the server responds with an HTML payload, the behavior depends on the HTTP status code:

  1. Error Status Codes (4xx / 5xx): By default, Axios rejects promises for any status code outside the 2xx range. The resulting error object contains an error.response property. Because the payload is HTML, the default transformer fails to parse it as JSON and falls back to returning the raw HTML string inside error.response.data.
  2. Success Status Codes (200 OK): Some servers, proxies, or captive portals return an HTML page with a 200 OK status (such as a login redirect or single-page app fallback). In this scenario, Axios resolves the promise successfully. However, because JSON.parse() fails silently inside Axios's default transformer, response.data is returned as a plain HTML string rather than a JavaScript object.

Common Causes of HTML Responses

Detecting and Handling HTML Responses

To prevent application crashes when expecting structured JSON data, implement safeguards using response headers or global interceptors.

1. Checking the Content-Type Header

Verify that the content-type header matches application/json before processing data:

axios.get('/api/data')
  .then(response => {
    const contentType = response.headers['content-type'];
    if (!contentType || !contentType.includes('application/json')) {
      throw new Error('Received non-JSON response from server.');
    }
    // Safe to use response.data as JSON
  })
  .catch(error => {
    if (error.response) {
      console.error(`Status: ${error.response.status}`);
      console.error('Payload:', error.response.data);
    }
  });

2. Global Handling via Axios Interceptors

You can implement an Axios response interceptor to validate content types across all requests globally and normalize error reporting:

axios.interceptors.response.use(
  (response) => {
    const contentType = response.headers['content-type'] || '';
    if (typeof response.data === 'string' && contentType.includes('text/html')) {
      return Promise.reject(new Error('Unexpected HTML response received.'));
    }
    return response;
  },
  (error) => {
    if (error.response && typeof error.response.data === 'string') {
      if (error.response.data.trim().startsWith('<!DOCTYPE html>') || error.response.data.includes('<html')) {
        error.message = `Server returned an HTML error page with status ${error.response.status}`;
      }
    }
    return Promise.reject(error);
  }
);

By inspecting the response content type and payload structure, you can gracefully handle HTML responses and avoid runtime errors caused by unexpected data types.