What the nginx 499 Status Code Means
errors
The nginx 499 status code means the client closed its connection before nginx finished returning a response. It is an nginx-specific, non-standard logging code, not an HTTP status sent back to the client. A few 499 entries can reflect visitors navigating away, but sustained or slow 499 requests often indicate that WordPress, PHP-FPM, an external dependency, or an intermediate proxy took longer than the caller was willing to wait.
Why This Happens
The WordPress request is slow
A plugin can run an expensive database query, rebuild a large cache, or call an external API from a request hook. Until that work finishes, PHP-FPM cannot return a response to nginx. A browser, mobile app, load balancer, or health checker may reach its own timeout first and close the connection.
The same pattern appears when the database is overloaded or PHP-FPM workers are saturated. The visible 499 is recorded at nginx, but the useful cause may be several layers downstream.
A long AJAX or REST action blocks
Requests to admin-ajax.php and custom REST endpoints sometimes perform imports, reports, media processing, or bulk updates synchronously. These operations may be legitimate work, but an interactive HTTP request is a fragile place to run them. Client libraries and proxies often have shorter deadlines than the job requires.
WordPress cron can also overlap with foreground traffic. A resource-heavy scheduled task may slow unrelated requests enough that their callers disconnect.
The client abandons a normal request
Visitors close tabs, follow another link, refresh, or double-click while a request is still running. Browsers can cancel speculative or superseded requests as part of ordinary operation. These 499s do not necessarily indicate a server fault, especially when request times are short and the rate is stable.
Timeouts do not align
An upstream load balancer may allow less time than nginx, or a JavaScript client may impose a deadline shorter than both. Increasing fastcgi_read_timeout cannot prevent a client with a shorter timeout from leaving. Timeout values should reflect the intended request budget across the full path.
Diagnosing 499 Requests
First confirm that the number comes from nginx access logs. Apache and other servers may describe an abandoned connection differently because 499 is not part of the HTTP specification.
-
Group 499 entries by route, request method, caller, and time. A concentration on one REST route is more useful than the site-wide count. Health-check user agents, mobile clients, and browser traffic may have different deadlines.
-
Include total and upstream timing in a diagnostic nginx access-log format:
log_format timed '$remote_addr "$request" $status ' 'request_time=$request_time ' 'upstream_time=$upstream_response_time'; access_log /var/log/nginx/access.log timed;$request_timemeasures elapsed request processing as nginx sees it.$upstream_response_timereports time spent receiving the upstream response and may contain multiple values when more than one upstream was tried. A 499 with a long upstream time suggests the caller waited on the origin; a short time may be an ordinary cancellation or a very aggressive client deadline. -
Enable the PHP-FPM slow log for a threshold appropriate to the workload:
request_slowlog_timeout = 5s slowlog = /var/log/php-fpm/www-slow.logConfirm the pool can write the path, reload PHP-FPM, and use the log only as long as needed. Slow-log stacks can identify the plugin function, database call, or external request active when the threshold was crossed.
-
Correlate nginx timestamps with PHP-FPM logs, database slow queries, application traces, and scheduled jobs. WordPress profiling tools can help in a controlled environment.
SAVEQUERIESrecords query details but adds overhead and should not be left enabled casually on production:define('SAVEQUERIES', true); -
Reproduce a suspect route while measuring it from the caller's side:
curl -sS -o /dev/null \ -w 'code=%{http_code} total=%{time_total}\n' \ https://example.com/wp-json/example/v1/reportA successful manual request does not rule out intermittent load, dependency latency, or a shorter timeout in the real client. Compare equivalent authentication, payload, and network path.
Fixing the Underlying Cause
Profile slow WordPress work
Identify the callback responsible before changing server limits. Optimize repeated queries, add appropriate indexes through a reviewed migration, eliminate unnecessary remote calls, and update or replace a plugin that performs expensive work on every request. Persistent object caching can reduce repeated computation, but it will not repair an inherently unbounded query.
The process also overlaps with 502 Bad Gateway diagnosis, where the proxy cannot obtain a usable upstream response rather than losing the client side of the connection.
Move long tasks out of the request
Queue imports, exports, email batches, and remote synchronization for background processing. Return a job identifier promptly, then let the browser poll a lightweight status endpoint. WordPress cron can be suitable for modest deferred work, while larger workloads may need a supervised queue with retry and idempotency controls.
Relieve PHP-FPM and database contention
Measure worker utilization, memory per child, database capacity, and queueing. Size the PHP-FPM pool within available memory, reduce slow code, and cache safe repeated results. Raising the worker count without enough memory can cause swapping or process termination and make latency worse.
Managed WordPress hosting can reduce operational drift around PHP-FPM and caching, while WordPress support is appropriate when a particular plugin or route needs profiling.
Align timeouts carefully
For legitimately long operations, set proxy and FastCGI read timeouts to accommodate the expected duration:
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_read_timeout 60s;
}
location /upstream-app/ {
proxy_pass http://application;
proxy_read_timeout 60s;
}
These values are examples, not universal recommendations. Align caller, CDN, load-balancer, nginx, and application deadlines with a small operational margin. Increasing timeouts is a mitigation for intentionally long requests, not a fix for work that should complete quickly.
Preventing Excessive 499s
Track 499 rates by route alongside latency percentiles, PHP-FPM saturation, database time, and dependency failures. Give outbound HTTP calls explicit deadlines and fallback behavior. Keep interactive endpoints bounded, move slow tasks to workers, and performance-test changes that add database queries or remote calls to common hooks.
How Nova Handles This
Nova manages the OpenLiteSpeed application layer behind Traefik ingress in an isolated Kubernetes namespace per tenant, providing separate proxy and application signals when a tenant request is abandoned.
FAQ
Did nginx send a 499 response to the visitor?
No. nginx writes 499 to its own log after it observes that the client connection closed. The client may report a timeout, cancellation, network error, or nothing at all.
Are all 499 entries a performance problem?
No. Browsers routinely cancel requests when users navigate or refresh. Investigate when the rate changes, request times are long, or entries cluster around an important route.
Will increasing fastcgi_read_timeout remove 499s?
Not necessarily. It only changes how long nginx waits for FastCGI, and the client or another proxy may still disconnect earlier. Optimize the slow work and align the complete timeout chain.
What is the most useful timing field?
Use $request_time and $upstream_response_time together. Their relationship helps distinguish time spent on the upstream from the total lifetime nginx observed, although logs and traces are still needed to identify the slow code.
See What's Included With Nova Managed Hosting
Nova runs every managed WordPress tenant in an isolated Kubernetes namespace with daily automated backups and managed core/plugin updates. If you're troubleshooting a specific issue on your own site, our team can help.