Troubleshooting Nginx: HTML Page Returned Instead of Expected HTTP Status Code

Abstract: Configure Nginx error_page directives to return plain text or JSON instead of HTML for rate-limiting status codes like 429 and 503.

Created: 2026-08-21 by
Nginx returns an HTML page instead of a raw HTTP status code because the limit_req_status directive defaults to 503, and Nginx serves its custom error page for that status. To fix this, you must configure error_page directives to return plain text or JSON responses for specific status codes like 429 or 503, ensuring API clients receive machine-readable data rather than formatted HTML.

Understanding the Default Behavior

When Nginx encounters a rate-limiting event triggered by the limit_req module, it typically rejects the request with a 503 Service Unavailable status code by default. This behavior is controlled by the limit_req_status directive. While many modern APIs prefer a 429 Too Many Requests status, Nginx’s default configuration often remains set to 503 unless explicitly changed.

The core issue arises not from the status code itself, but from how Nginx handles the response body. By default, when Nginx generates a 503 (or any error status), it looks for a custom error page defined in the configuration. If no custom error page is specified for that specific code, Nginx falls back to its built-in default error page. This built-in page is an HTML document containing styling, headers, and explanatory text, which breaks API integrations expecting a simple status line or JSON payload.

Configuring Custom Error Pages

To resolve this, you need to override the default error handling behavior using the error_page directive. This directive tells Nginx exactly what content to serve when a specific error occurs. For rate limiting issues, you should target the status code defined in your limit_req_status setting.

First, determine your desired status code. If you are using the standard 429 for rate limiting, ensure your limit_req block includes:

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
limit_req zone=one burst=5 nodelay;
limit_req_status 429;

Next, add an error_page directive in the same server or location block. You can return a plain text message directly within the configuration without needing external files:

error_page 429 = @rate_limited;
location @rate_limited { default_type text/plain; return 429 "Rate limit exceeded"; }

This approach uses a named location (@rate_limited) to handle the error internally. The default_type ensures the response header indicates plain text, and the return statement sends the exact string you want. This prevents Nginx from generating any HTML markup.

Handling 503 Errors Specifically

If you are stuck with the default 503 status code, the process is identical but targets code 503. Many legacy systems or monitoring tools might still expect 503 for temporary unavailability. You can customize the response similarly:

error_page 503 = @service_unavailable;
location @service_unavailable { default_type application/json; return 503 '{"status": "unavailable", "message": "Server overloaded"}'; }

Using JSON is particularly useful if your backend services consume these errors programmatically. Note that the = symbol in error_page 503 = @... is critical. It instructs Nginx to use the status code returned by the named location handler (which is 503) rather than changing the final response code to 200 or another value. Without the equals sign, Nginx might rewrite the status code based on the internal redirect.

Verifying the Configuration

After making changes, always test the configuration syntax before reloading. Run the following command in your terminal:

nginx -t

If the test passes, reload Nginx to apply the changes:

systemctl reload nginx

To verify the fix, use curl to trigger the rate limit. You can do this by sending multiple rapid requests or by temporarily lowering the rate limit in your config for testing purposes. Check the response headers and body:

curl -i http://your-domain.com/api/endpoint

You should see the correct HTTP status code in the first line and the plain text or JSON body in the response, without any HTML tags. If you still see HTML, check if another error_page directive elsewhere in your configuration is overriding this one. Nginx applies the most specific matching rule, so ensure your new directive is in the correct context (server vs. location).

Common Pitfalls and Debugging

Sometimes, even with correct error_page directives, browsers may cache old HTML error pages. Clear your browser cache or use incognito mode when testing. Additionally, ensure that your default_type is set correctly. If you omit it, Nginx might guess the type, potentially leading to unexpected content types being sent to the client.

Another common mistake is forgetting to include the nodelay parameter in the limit_req directive. While this doesn't affect the response format, it changes how bursts are handled. If you are troubleshooting intermittent HTML responses, ensure that your rate limiting logic is consistent and not conflicting with other modules like limit_conn.

For more complex setups involving reverse proxies, ensure that proxy headers are passed correctly so that the rate limiter identifies the correct client IP. Misconfigured proxy headers can lead to incorrect rate limiting decisions, causing legitimate users to hit the error pages unexpectedly.

Frequently Asked Questions

Why does my API client receive HTML instead of JSON?

Nginx serves its default error page as HTML when no custom error handler is defined for the specific status code. You must explicitly configure error_page to return plain text or JSON to prevent this default behavior.

Can I use a static file for the error response?

Yes, you can point error_page to a static file path, such as /custom_errors/rate_limit.html. However, returning inline text via a named location is often simpler and avoids filesystem I/O overhead during high-traffic events.

What happens if I don't specify a status code in error_page?

If you omit the status code, Nginx may return a 200 OK status for the error page content, which confuses clients expecting an error. Always use the = syntax followed by the desired status code to ensure the correct HTTP response code is sent.

Does this affect browser-based applications?

Browsers generally render HTML gracefully, so changing to plain text might make error messages less user-friendly in a web interface. Consider using different error handlers for browser traffic versus API endpoints if both are served by the same Nginx instance.

How do I debug why my error_page isn't working?

Check the Nginx error log for any permission issues or syntax errors. Also, verify that your error_page directive is not being overridden by a more general rule higher up in the configuration hierarchy.

Latest News

How to Display Output of xterm Command in Terminal Screen using Cron

Resolve the inability to display xterm output in a terminal screen from cron by redirecting logs or using wall instead of insecure X11 workarounds.

Read More
Expose Local Server on VPS Preserving Real Client IPs (OS Level)

Resolve VPS logging showing only gateway IPs by configuring reverse proxies to forward real client addresses via X-Forwarded-For headers.

Read More
How to Clear Recent Files History in Linux without Deleting Actual Files?

Clear recent files history in Linux by deleting ~/.local/share/recently-used.xbel without removing the actual files.

Read More
Best HDD Copy Strategy: Manually Syncing and Rewriting Scratch

Resolve data drift between two 4TB hard drives by using rsync with the --delete flag to manually sync and rewrite scratch data for an exact mirror.

Read More
Understanding Bash Prompt Abbreviations: An Overview

Resolve repetitive typing errors by creating and persisting Bash aliases in the .bashrc file for improved command-line efficiency.

Read More
Free Tools and Methods to Log Real-Time Performance Details in Task Manager

Log real-time CPU and memory metrics to CSV files using Windows Performance Monitor Data Collector Sets to troubleshoot system slowness.

Read More
Contact Qatar Airways Customer Service: Phone Number, Help Center, and More

Resolve urgent Qatar Airways reservation issues like flight changes and baggage claims using the US phone number +1801-855-5905 or online self-service tools.

Read More
Windows 11: Alt Codes Displaying Wrong Characters - Solutions

Resolve Windows 11 Alt codes showing wrong characters by verifying the US keyboard layout and enabling Num Lock.

Read More
Run Programs with Elevated Privileges: A Comprehensive Guide

Eliminates the need to type sudo for frequent administrative tasks by configuring .bashrc aliases or functions.

Read More
Dynamically Changing Dates in Excel Formulas Using Cells

Eliminate manual date updates in Excel by using cell references to create dynamic formulas that automatically adjust calculations when input dates change.

Read More
Granting Edit Access to a Specific Excel Sheet in an Office 365 User Email ID-Based Login

Excel Online lacks native sheet-level permissions, so users must split workbooks or use worksheet protection to isolate edit access for specific tabs.

Read More
Creating Network Load on Local Wi-Fi from One PC

Generate sustained Wi-Fi bandwidth load from a single PC using a VM SFTP server to test router stability and throughput.

Read More
Changing the Default Editor for Batch Files in Windows 11's File Explorer

Resolve the issue of batch files opening in Notepad by changing the default editor to a preferred application like VS Code.

Read More
Gain WiFi Connection Minutes Before Losing it by Changing MAC Address

Bypass MAC address filtering to restore WiFi connectivity temporarily when a device is blocked by network administrators.

Read More
Photos Folders Turned Black: Troubleshooting Picture Applications on Windows

Resolve black screens in Windows Photos by resetting the app, updating graphics drivers, and repairing system files.

Read More
Troubleshooting High CPU Temperature Values: A Step-by-Step Guide

Resolve high CPU temperatures and thermal throttling by monitoring with Speedfan, cleaning dust from cooling components, and replacing degraded thermal paste.

Read More
Make cURL establish TLS connection with CA unknown

Resolve cURL TLS connection failures with unknown or self-signed certificates by using the --insecure flag to bypass CA validation.

Read More
Troubleshooting Incorrect Network Metrics Values in Grafana, Prometheus, InfluxDB, and Telegraf

Fix incorrect network metrics in Grafana, Prometheus, InfluxDB, and Telegraf by verifying interface selection, resolving unit mismatches, and handling counter resets.

Read More
Start Another Terminal Window (cmd.exe) Windows Repair/Troubleshoot

Provides step-by-step instructions to access the Windows Command Prompt via bootable media for advanced system repair and troubleshooting.

Read More
Thinkpad P1 Gen 6: Sudden Thunderbolt 3 Dock USB-C Charging Stopped Working

Resolve sudden Thunderbolt 3 dock USB-C charging and internet failure on ThinkPad P1 Gen 6 by updating firmware, resetting the dock, and adjusting power settings.

Read More
ASUS Windows 11 Laptop: Internet Connection Issue with Hotspot

Resolve ASUS Windows 11 laptop spinning wheel issue when connecting to mobile hotspot by resetting network stack, disabling IPv6, and adjusting power settings.

Read More
Developing iOS Project with Firebase: Logging in and Sending Push Notifications

Integrates Firebase to enable secure user authentication and push notifications in iOS applications.

Read More
Troubleshooting Bash Script with Suspected Issues of Spaces, Dashes, Filenames and Quoting

Resolve Bash script failures caused by word splitting, globbing, and special characters in filenames by applying proper quoting techniques.

Read More
Populating OneDrive Folder for Local Backup without Affecting Remote Files

Prevent OneDrive from deleting remote files during Windows reinstallation by unlinking the account or using symbolic links to isolate local data.

Read More
Force First Frame as Keyframe Without Re-encoding: A Quick Solution

Resolve inaccurate seeking and playback glitches by re-encoding video to ensure the first frame is a valid keyframe.

Read More
Find Information in Database Using WSL (Ubuntu) on Windows-10

Resolve database connection issues from WSL Ubuntu on Windows 10 by configuring client tools and network access to query remote or local servers.

Read More
Configuring a New Router with Different Networks: One Family, One Guest

Resolve IP conflicts and isolate guest traffic by configuring a second router with a distinct subnet via its WAN port.

Read More
Quickly Open Multiple Stock Websites with a Single Command

Solve the problem of manually opening multiple stock research websites by creating a single command to launch them simultaneously on Windows, macOS, or Linux.

Read More
Improved Adobe Acrobat Search Experience: Access Advanced Search with Ease

Access advanced search filters like case sensitivity and regex in Adobe Acrobat by clicking More Options in the Find bar.

Read More
Port Forwarding VM via OpenVPN: A Comprehensive Guide

Configure OpenVPN server and iptables to forward specific ports from VPN clients to a virtual machine's internal IP address.

Read More
Understanding Partitions in the 1980s: A Look Back at MS-DOS and Master Boot Record

Explains how the Master Boot Record and partition table enabled MS-DOS to manage multiple disk volumes on a single physical drive.

Read More
Windows 10 Enterprise: Keyboard Not Detected in Managed Organization with Admin Privileges

Resolve Windows 10 Enterprise keyboard detection failures caused by Group Policy restrictions, driver corruption, or USB enumeration errors.

Read More
Can't Find x86_64 Versions of Applications on MacOS arm64 (M1)

Resolve missing x86_64 app issues on M1 Macs by installing Rosetta 2 and reinstalling apps as universal binaries.

Read More
How to Hide Installation Source for an Android App

Manage Android app installation source visibility by using USB transfers or file managers to reduce browser traces and isolate data via work profiles.

Read More
Connecting Momentus 4200.2 ST9808210A Hard Drive via USB Cable

Resolve inability to access Seagate Momentus 4200.2 ST9808210A hard drive by connecting it via USB using a compatible SATA-to-USB adapter and configuring the disk in Windows.

Read More