Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Grafana By James Joyner IV · · 8 min read

Grafana Error Guide: '413 Request Entity Too Large' — Fix Large Dashboard Saves

Quick answer

Fix Grafana 413 Request Entity Too Large when saving big dashboards or uploading images: raise reverse-proxy body limits, tune Grafana's own limits, and shrink the dashboard JSON.

  • #grafana
  • #observability
  • #troubleshooting
  • #errors
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Overview

Grafana serves and saves dashboards as JSON over HTTP. When a dashboard grows large — dozens of panels, big inline SVGs on a canvas panel, long variable lists, or a snapshot upload — the save request body can exceed a size limit set by a reverse proxy in front of Grafana (Nginx, a Kubernetes Ingress, a cloud load balancer) or by Grafana itself. The proxy rejects the request before Grafana ever processes it and returns a 413.

The literal errors you will see in the browser, the toast, or the network response:

413 Request Entity Too Large
{"message":"error reading the request body","traceID":""}
# Nginx-fronted Grafana returns an HTML body:
<html><head><title>413 Request Entity Too Large</title></head>
<body><center><h1>413 Request Entity Too Large</h1></center>
<hr><center>nginx</center></body></html>

The tell: the response Server header is usually nginx or the load balancer, not Grafana — the request died at the proxy, so nothing appears in Grafana’s own logs.

Symptoms

  • “Save dashboard” fails with a red toast, but small dashboards save fine.
  • Importing a large dashboard JSON or uploading a snapshot/image fails.
  • The failing request is a POST/PUT to /api/dashboards/db or /api/snapshots.
  • Grafana’s server log shows nothing for the failed save (the proxy blocked it).
  • The response body is an Nginx/HAProxy/Ingress error page, not Grafana JSON.

Common Root Causes

1. Reverse-proxy body-size limit (most common)

Nginx defaults client_max_body_size to 1 MB. A dashboard with many panels or an inline image easily exceeds that, and Nginx returns 413 before proxying to Grafana.

2. Kubernetes Ingress (nginx) proxy-body-size

The ingress-nginx controller applies its own proxy-body-size (default 1m) via annotation/ConfigMap, independent of any Grafana setting.

3. Cloud load balancer / API gateway limit

An ALB, GCP LB, or API gateway in front of the Ingress can impose its own request-size cap.

4. Genuinely bloated dashboard JSON

Base64 images embedded in canvas/text panels, thousands of static variable values, or accumulated __inputs/panel duplication inflate the payload to megabytes.

5. Snapshot / rendered-image uploads

Publishing a snapshot or an image-renderer payload sends a much larger body than a normal save.

Diagnostic Workflow

Step 1: Confirm the 413 comes from the proxy, not Grafana

Reproduce the save with devtools open and inspect the failing request, or replay it:

# Save a dashboard via the API and watch the status + Server header
curl -s -o /dev/null -w "%{http_code} %{size_upload} bytes\n" \
  -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  --data @big-dashboard.json \
  https://grafana.example.com/api/dashboards/db

# Read the response headers to see who answered
curl -sI -X POST -H "Authorization: Bearer $TOKEN" \
  --data @big-dashboard.json \
  https://grafana.example.com/api/dashboards/db | grep -iE "server|http/"

If Server: nginx and Grafana’s log is silent, the proxy rejected it.

Step 2: Measure the payload

# How big is the dashboard body actually?
du -h big-dashboard.json
jq '[.dashboard.panels[]] | length' big-dashboard.json    # panel count
# Find base64-embedded images bloating the JSON
grep -oE 'data:image/[a-z]+;base64,' big-dashboard.json | wc -l

Step 3: Raise the Nginx body limit

# nginx.conf (self-managed) — server or location block
location /api/ {
    client_max_body_size 25m;
    proxy_pass http://grafana_upstream;
}

Step 4: Raise the Kubernetes Ingress limit

# Ingress annotations for ingress-nginx
metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "25m"

Or globally in the controller ConfigMap:

# ingress-nginx controller ConfigMap
data:
  proxy-body-size: "25m"

Step 5: Check Grafana’s own limits

Grafana can also cap request bodies. Confirm these are not the bottleneck once the proxy is fixed:

# grafana.ini
[dataproxy]
# streaming/proxy body handling
[snapshots]
# snapshot uploads
[security]
# nothing here caps size directly, but keep an eye on reverse-proxy TLS buffers

Grafana behind its own subpath still needs root_url set correctly so the corrected request routes properly:

[server]
root_url = https://grafana.example.com/

Step 6: Shrink the dashboard if it is genuinely bloated

  • Move base64-embedded images out of panels; reference a URL instead.
  • Replace huge static custom variable lists with a query variable.
  • Split a mega-dashboard into linked dashboards.

Example Root Cause Analysis

A team’s platform dashboard grew to 60 panels including two canvas panels with embedded PNG logos. Saving it from the UI failed with a red “Request Entity Too Large” toast; small dashboards saved fine. Grafana’s server log showed nothing for the failed POST /api/dashboards/db.

Replaying the save with curl -sI returned Server: nginx and 413, proving the ingress-nginx controller blocked it. du -h showed the exported JSON at 3.1 MB, well over the ingress default proxy-body-size: 1m, driven mostly by two base64 PNGs (grep found the data:image/png;base64, markers).

Fix: set nginx.ingress.kubernetes.io/proxy-body-size: "25m" on the Grafana Ingress and move the logos to a hosted URL, which dropped the JSON to ~180 KB. The save succeeded immediately. The root cause was a proxy body limit, not Grafana — but the embedded images were the reason the payload was large enough to hit it.

Prevention Best Practices

  • Set proxy-body-size/client_max_body_size on every proxy in front of Grafana to a sane value (e.g. 25 MB) so normal large dashboards save; see more Grafana guides.
  • Keep the limit consistent across all layers (LB → Ingress → Nginx) so the smallest one doesn’t silently win.
  • Never embed base64 images in dashboard JSON; reference hosted assets.
  • Prefer query variables over huge static lists to keep JSON small.
  • Split very large dashboards into linked focused dashboards.
  • Provision dashboards from files so payload size is caught in review, not at save time.

Quick Command Reference

# Who returned the 413?
curl -sI -X POST -H "Authorization: Bearer $TOKEN" \
  --data @big-dashboard.json \
  https://grafana.example.com/api/dashboards/db | grep -iE "server|http/"

# Measure the payload
du -h big-dashboard.json
grep -oE 'data:image/[a-z]+;base64,' big-dashboard.json | wc -l

# Raise limits
# nginx:   client_max_body_size 25m;
# ingress: nginx.ingress.kubernetes.io/proxy-body-size: "25m"

Conclusion

A Grafana 413 almost always comes from a proxy, not Grafana. Fix it in order:

  1. Confirm the Server header and Grafana’s silent log to prove the proxy rejected it.
  2. Measure the dashboard body and find what’s bloating it.
  3. Raise client_max_body_size / proxy-body-size consistently across every proxy layer.
  4. Shrink the JSON by removing embedded images and static lists so you’re not just papering over bloat.

Raising the limit unblocks the save; trimming the JSON keeps it from creeping back.

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.