406 Not Acceptable OpenResty | Fast Fixes And Causes

A 406 Not Acceptable OpenResty error means OpenResty rejected the request’s headers or format; fixing it is often a header, rule, or rewrite change.

You click a link and get “406 Not Acceptable.” If the page mentions OpenResty, the refusal happened at the reverse-proxy layer (OpenResty runs on Nginx) before your app had a chance to speak.

That detail narrows the hunt. A 406 is usually triggered by content negotiation (headers like Accept), a security rule that dislikes a header or query string, or a rewrite that routes a request into a blocked location.

This guide helps you pin down the trigger fast, then fix it without turning off protections or guessing in the dark.

What A 406 Response Means On OpenResty

HTTP 406 means the server can’t produce a response that matches what the client said it will accept. In plain terms, the client asked for a format the server refuses to serve for that request.

On OpenResty, you’ll see 406 in a few common patterns:

  • Accept header mismatch — The browser, bot, API client, or plugin sends an Accept header that your config or upstream rejects.
  • Security rule trip — A WAF rule decides a header, cookie, URL, or body looks suspicious and returns 406 as a “soft block.”
  • Rewrite or location trap — A rewrite sends the request into a location block with tight rules (method limits, content-type limits, or custom checks) that end in 406.

If you want a standards reference for the status code itself, the HTTP semantics spec documents 406 as “Not Acceptable.” You can link this in your WordPress editor as a trust anchor: RFC 9110 (HTTP Semantics).

Why You See 406 Not Acceptable OpenResty On Your Site

The phrase “406 Not Acceptable OpenResty” shows up when OpenResty generates the error page directly, or when an upstream returns 406 and OpenResty passes it through with its default branding.

Most site owners run into it after a change, even a small one:

  • New CDN or WAF setting — A managed rule starts blocking a pattern in your URLs, headers, or cookies.
  • Plugin or app update — A request header changes (often for API calls), or a new endpoint returns a content-type the proxy rejects.
  • Server config edit — A tightened location block, a new map rule, or a header filter adds a reject path.
  • Bot traffic shift — A crawler starts sending unusual Accept headers, odd user agents, or long query strings.

Before you change anything, get one clean signal: does it happen for everyone, or only for one browser, one country, one device, one URL pattern, or one request type (GET vs POST)? That single split often points right at the cause.

Quick Checks That Narrow The Cause Fast

Start with checks you can run in minutes. You’re trying to catch the exact request that triggers the 406, then compare it to a request that works.

Confirm It’s Really A 406 From The Edge

  • Open DevTools — In your browser’s Network tab, click the failing request and confirm the status is 406, not a cached HTML page that only looks like one.
  • Check Response Headers — Look for hints like a WAF header, a CDN header, or an internal request ID you can trace in logs.
  • Retry In A Clean Profile — Use a private window with extensions off to rule out a header injected by an addon.

Reproduce With Curl Using The Same Headers

Curl gives you a sharp way to test headers. Copy the failing request’s headers from DevTools and replay them from your terminal. Two headers are the usual suspects: Accept and User-Agent.

  • Test With A Plain Accept — Send Accept: */* and see if the 406 disappears.
  • Test With The Browser Accept — Send the exact long Accept header your browser used and compare results.
  • Test A Different User-Agent — Swap to a standard browser UA to see if a bot-like UA triggers a block.

Use This Small Triage Table

This table gives you a quick match between what you see and where to look first.

Symptom Likely Trigger Where To Check
Only fails on one URL pattern Rewrite or location rule Nginx/OpenResty config
Fails for bots, works for browsers User-Agent or Accept filter WAF rules, header maps
Fails after enabling a WAF rule Managed rule block CDN/WAF event logs
Fails only for API calls Content-type or Accept mismatch Upstream app + proxy headers
Fails only on POST/PUT Body inspection rule WAF body rules, size limits

Fixing 406 Not Acceptable In OpenResty With Targeted Changes

This is the part where people are tempted to “just turn things off.” Don’t. A safe fix keeps your site protected while removing the false positive or mismatch.

Adjust Accept Handling For Real Clients

If your site or API rejects certain Accept headers, tighten the logic to the endpoints that need it, not the whole server. A common pattern is a strict check meant for an API route that ends up catching normal web pages.

  • Scope The Rule — Apply strict Accept checks only under the API location block, not at server level.
  • Allow A Fallback — Treat Accept: */* as valid for HTML endpoints so normal browsers and tooling keep working.
  • Return A Clearer Code For APIs — If an API needs JSON, return 415 or 400 with a small JSON error body instead of a blanket 406 page.

Stop Rewrites From Falling Into A Blocked Location

Rewrites can route a harmless URL into a location that blocks methods, blocks extensions, or runs custom Lua checks. That’s when a normal page load suddenly ends in 406.

  • Log The Final URI — Temporarily log the rewritten URI so you can see where the request ended up.
  • Match Locations Precisely — Use exact or prefix matches for sensitive locations so broad patterns do not catch normal pages.
  • Keep Error Handling Consistent — If a location is restricted, return 403 for permission blocks and reserve 406 for true negotiation issues.

Handle WAF False Positives Without Opening The Gates

Many WAFs return 406 for a “not acceptable” request even when the real reason is a rule hit. The fix is almost never “disable the WAF.” It’s to carve out a narrow exception.

  • Find The Rule ID — In your WAF event log, locate the rule that matched and capture its ID, match string, and request field.
  • Exclude A Single Parameter — If a specific query param triggers the hit, exclude only that param for that route.
  • Allow A Known Good Path — If the block happens on a webhook or callback URL, allow that endpoint while leaving global rules intact.

Common Root Causes And Clean Fix Patterns

Once you have a reproduction case, these are the fixes that solve most 406 incidents on OpenResty sites.

CDN Or Proxy Adds A Header Your Rules Reject

CDNs may add headers for tracing, caching, or client hints. If you have strict header filters, a new header can trip them.

  • Compare Working vs Failing — Diff request headers from a working route and the failing route to spot what changed.
  • Allow The New Header — If you filter headers, allow known CDN headers rather than blocking unknown ones by default.
  • Keep A Size Cap — If the risk is header bloat, enforce total header size limits instead of blocking by name.

Bot Or Scanner Triggers Strict Accept Rules

Some bots send wild Accept headers, like long vendor MIME lists. If your server checks Accept too strictly, it can reject requests you’d rather just serve as HTML.

  • Default To HTML For Pages — On normal page routes, treat unknown Accept values as HTML and continue.
  • Gate APIs With Auth — If you only want certain clients calling JSON endpoints, enforce auth and rate limits instead of MIME policing.
  • Block Bad Bots With 403 — If you truly want to block, use a clear access status so your logs tell the truth.

ModSecurity Rule Hits On A Parameter Or Cookie

With ModSecurity or a managed WAF, 406 can be a default block code. A common trigger is a search query, a filter param, or a cookie value that looks like an attack string.

  • Sanitize Inputs Upstream — If your app reflects user input into headers or redirects, clean it before it touches the proxy layer.
  • Narrow The Exclusion — Exclude only the field that causes the hit, and only on the endpoint where it’s needed.
  • Watch For Encoded Payloads — Double-encoded strings can look hostile; ensure clients encode once, not twice.

App Returns A Content-Type The Client Didn’t Ask For

API clients can send tight Accept headers, like JSON only. If your upstream returns HTML on errors, a strict proxy may reject it.

  • Return JSON Errors On JSON Routes — Keep API error bodies consistent so Accept checks don’t misfire.
  • Set Content-Type Explicitly — Ensure upstream responses set correct Content-Type, not a default.
  • Use Vary Carefully — If you vary on Accept, keep caching rules aligned so one client’s response does not leak to another.

Preventing Repeat 406 Surprises During Updates

Once you’ve cleared the active error, lock in a few habits so “406 not acceptable openresty” doesn’t pop up again after the next change.

Keep A Tiny Set Of Header Regression Tests

You don’t need a giant test suite. You need a small set of requests that reflect real traffic: a normal browser page, an API call, a webhook post, and a bot-like request.

  • Save Curl Commands — Store four or five curl calls that replay the headers you care about.
  • Run Tests After Each Change — Test after CDN toggles, WAF rule changes, plugin updates, and config reloads.
  • Record Expected Status Codes — Note what “good” looks like, including redirects, so failures stand out.

Log Enough To Trace A Block Without Storing Extra Data

Good logs turn a mystery 406 into a two-minute fix. Aim for request ID, URI, method, status, upstream status, and a short list of headers that matter (like Accept and User-Agent). Keep it lean.

  • Add A Request ID — Pass a request ID from edge to upstream so you can trace one failing request end to end.
  • Capture Negotiation Fields — Log Accept, Content-Type, and upstream Content-Type for API routes.
  • Store WAF Match Metadata — Keep rule ID and match location if your WAF provides it.

Use Vendor Docs As A Backstop

When you see 406 on OpenResty, the fastest confirmation often comes from primary docs. These links are safe, stable references you can keep in your own notes:

  • Read The HTTP Definition — Use RFC 9110 for the standard meaning of 406.
  • Check Nginx Behavior — Use nginx.org documentation for header and location handling.
  • Check OpenResty Notes — Use OpenResty docs for Lua phase behavior and request filtering patterns.

If you’re still stuck, return to the basics: capture one failing request, replay it with curl, and remove one variable at a time. In most cases, the fix ends up being a narrow rule change, a scoped rewrite, or an Accept handling tweak, not a sweeping server change.