499 Token Required or Client Closed Request

The 499 status code has two distinct meanings: ArcGIS uses 499 Token Required for missing Authentication tokens, and nginx logs 499 Client Closed Request when the client disconnects before receiving a response.

Usage specific to ArcGIS

The 499 Token Required code indicates the server expects an Authentication token with the HTTP request and received none. Resolving the error means resubmitting with a valid token.

As with the neighboring 498 code, ArcGIS deployments have been observed reporting the value inside the JSON error object of a 200 response rather than on the status line. A client branching on the HTTP status alone sees a success and proceeds with an error document.

The number collides with the nginx meaning described below, and the two behave in opposite ways. The ArcGIS code travels in a response body, while the nginx code never reaches a client at all.

Usage specific to nginx

The 499 Client Closed Request status code means the client closed the HTTP connection before the server finished processing. The final response never reaches the client. This code appears only in the nginx logs.

Common causes include client-side timeouts, users leaving the page, and load balancers closing idle connections.

Examples

ArcGIS Token Required

A client sends a request to an ArcGIS REST API endpoint without the required token. The server reports 499 Token Required in the response body while the observed HTTP status line carries 200 OK.

Request

GET /arcgis/rest/services/Map/MapServer?f=json HTTP/1.1
Host: www.example.re

Response

HTTP/1.1 200 OK
Content-Type: application/json

{
  "error": {
    "code": 499,
    "message": "Token required.",
    "details": []
  }
}

The observed status line reports success. Only the code field inside the error object identifies the failure.

nginx Client Closed Request

A client sends a long-running request but disconnects before the server responds. The nginx access log records the 499 status code.

Request

GET /api/heavy-report HTTP/1.1
Host: www.example.re

Response (nginx log entry only)

192.168.1.50 - - [02/Mar/2026:14:22:10 +0000] "GET /api/heavy-report HTTP/1.1" 499 0 "-" "python-requests/2.31.0"

No HTTP response is sent. The client disconnected before the server completed processing.

Diagnosing 499 in nginx

The code exists for logging alone. nginx never writes the value to the wire, and error_page 499 is rejected outright, failing a configuration test with an invalid value error. Among every code between 300 and 599, 499 is the only one the parser refuses, which reflects the fact no client is left to receive a response.

A rise in 499 usually points at upstream latency rather than at unreliable clients. The client gave up first, so the question is what caused the wait.

log_format latency '$remote_addr $status "$request" '
                   'rt=$request_time '
                   'uct=$upstream_connect_time '
                   'uht=$upstream_header_time '
                   'urt=$upstream_response_time';

Reading $request_time across the 499 rows separates the causes. Values scattered at random reflect visitors leaving before the response arrives. Values clustering at exactly 30 or 60 seconds point at a fixed timeout in a client library, a load balancer, or a CDN in front of nginx, and the number identifies which one.

The generating log line also sits at info level, and the default error log setting discards the entry.

error_log /var/log/nginx/error.log info;

The proxy_ignore_client_abort directive changes what nginx does when the client disappears. The default keeps the current behavior, closing the upstream connection immediately and logging 499. Turning the directive on holds the upstream request open to completion and logs whatever the upstream returned.

proxy_ignore_client_abort on;

Enabling the directive removes the code from the logs without addressing the cause, and holds worker connections and upstream capacity for work nobody awaits. Reducing proxy_read_timeout, or making the upstream answer faster, addresses the underlying latency instead.

Other proxies record the same condition without a status code. HAProxy marks a client-side disconnect in its termination state field, and Envoy reports a zero status alongside a downstream termination flag. nginx is unusual in surfacing the condition as a pseudo-status, which is why dashboards grouping by status code show a bucket other stacks lack.

How to fix

nginx Client Closed Request

Identify slow upstream targets first. Add $upstream_response_time and $upstream_connect_time to the nginx log format to measure backend latency:

log_format timed '$remote_addr $status '
  '$upstream_response_time '
  '$upstream_connect_time '
  '$request_uri';

Enable proxy_ignore_client_abort for endpoints where the backend operation must complete even after the client disconnects. This prevents nginx from terminating the upstream request when the client drops the connection:

location /api/long-task {
    proxy_ignore_client_abort on;
    proxy_read_timeout 300s;
    proxy_send_timeout 300s;
}

Increase proxy timeouts to match the expected backend processing time. The three relevant directives are proxy_connect_timeout (time to establish the upstream connection), proxy_read_timeout (time between two successive read operations from upstream), and proxy_send_timeout (time between two successive write operations to upstream).

Reduce backend response time. Slow database queries, unoptimized API endpoints, and resource contention on the upstream server are the root cause in most 499 cases. Profile and optimize the slow endpoints rather than increasing timeouts indefinitely.

Review the nginx access log for patterns. Filter for 499 entries and correlate by request URI, time of day, and response size to identify specific endpoints or traffic spikes triggering client disconnections.

For load balancer or CDN clients upstream of nginx, ensure the load balancer idle timeout exceeds the nginx proxy timeout. A load balancer closing the connection before nginx receives the upstream response produces 499.

ArcGIS Token Required

Include a valid Authentication token in the request. The ArcGIS server expects a token and rejects requests without one.

Generate a token using the generateToken REST endpoint and attach the value as a query parameter (?token=...) or in the X-Esri-Authorization header:

X-Esri-Authorization: Bearer <token>

The X-Esri-Authorization header is preferred over the query parameter. Query parameters appear in server logs and intermediate proxy logs, exposing the token.

Verify the secured service requires token-based Authentication. Services configured for anonymous access do not require a token. Check the service security settings in ArcGIS Server Manager or the ArcGIS Portal admin interface.

See also

Last updated: August 17, 2026