Get started Dashboard
Troubleshooting ·

Fix Cloudflare “Error 500: internal server error”

Explore with AI

Error 500: internal server error

On Cloudflare, Error 500 almost always means your origin server failed the request, and Cloudflare is just passing on the 500 your server sent back. Less often it comes from a Worker that threw an exception or a Page Rule that points to a wildcard that no longer exists. Check the response body first: if it mentions cloudflare or cloudflare-nginx, contact Cloudflare support, and otherwise fix the origin, the Worker or the rule.

Ongoing incident? Our founding team can help you get your Cloudflare app back up.
On this page

You see this message in a browser, in a client that called your API through Cloudflare, or as a red 5xx bar in zone analytics. It might hit one URL or the whole site. A lot of the time it starts just after someone changed something: a deploy to the origin, a new Worker version, or an edit to a rule.

The wording looks like it comes from Cloudflare, but Cloudflare’s own docs say it points to a problem with your origin web server. So the first job is to find out which layer actually wrote the 500. The fix depends on the answer.

What “Error 500: internal server error” means behind Cloudflare

Cloudflare is a reverse proxy. A request reaches the Cloudflare edge, runs through your rules and any Worker on the route, and then goes on to your origin. A 500 can come from three places on that path:

  • Your origin. The app server, or something in front of it such as a load balancer, cache, proxy or firewall, returned a 500. Cloudflare passed it on. The docs give Error establishing database connection as a common example and treat it as an origin problem.
  • A Worker. Cloudflare’s Error 500 page lists a Worker that throws a runtime JavaScript exception as a cause, and links it to Error 1101.
  • Cloudflare’s configuration. The only known Cloudflare-side cause the docs list is a Page Rule with a broken wildcard placeholder. If the HTML body of the 500 contains cloudflare or cloudflare-nginx, Cloudflare asks you to contact its support team.

Custom Errors can also change how the default error pages look, so a branded error page doesn’t tell you which layer failed. The 5xx overview says this directly.

Finding which layer returned the 500

Having built and led the Workers observability team at Cloudflare, I start every 500 with one question: who wrote this response body? These checks answer it in a few minutes.

Decision flow for a Cloudflare Error 500: check the body, then Workers, then Page Rules, then the origin

graph TB A[Request returns Error 500] --> B{Body mentions cloudflare?} B -->|yes| C[Send trace output to Cloudflare support] B -->|no| D{Worker on the route?} D -->|yes| E{Exception in Workers Logs?} E -->|yes| F[Fix the exception in the Worker] E -->|no| G{Page Rule edited recently?} D -->|no| G G -->|yes| H[Remove the stale wildcard placeholder] G -->|no| I[Pause Cloudflare and test the origin] I --> J[Fix the origin app or database] style A fill:#fee2e2,stroke:#fca5a5,color:#7f1d1d style F fill:#d1fae5,stroke:#6ee7b7,color:#065f46 style H fill:#d1fae5,stroke:#6ee7b7,color:#065f46 style J fill:#d1fae5,stroke:#6ee7b7,color:#065f46

Read the response body

Fetch the failing URL and look for the strings Cloudflare’s docs name:

curl -s https://www.example.com/failing/path | grep -iE 'cloudflare|cloudflare-nginx'

No match means the body came from your origin or your Worker. A match is the case where Cloudflare asks you to contact its support team (see the section on that below).

Split edge errors from origin errors in analytics

In the dashboard, open HTTP Traffic for the zone, select Add filter, and filter by Edge status code or Origin status code for 500. If the origin status code shows 500s, your server produced them. Error analytics also shows the URLs, source IPs and Cloudflare data centres involved. These numbers come from a 1% traffic sample, so a rare 500 may not show up at all.

If your account has the Log Explorer add-on, go to Observability > Logs and filter on the Ray ID from the error page to see that one request with full context.

Test with Cloudflare out of the path

Cloudflare’s troubleshooting steps include pausing Cloudflare to see whether the problem is at the origin. You have two options:

  • Pause the whole zone: Account home > your domain > Overview > Advanced Actions > Pause Cloudflare on Site. The docs say this takes five minutes or less, and that rules, WAF and edge certificates stop working while it’s paused.
  • Unproxy one record: DNS > Records > Edit, and set Proxy Status to Off.

On a busy production site, try a direct request from your own machine before you pause anything. Point the hostname at the origin IP for a single request:

# Replace 203.0.113.10 with your origin IP
curl -i --resolve www.example.com:443:203.0.113.10 https://www.example.com/failing/path

If the origin returns HTTP/1.1 500 here too, Cloudflare isn’t the cause, and you can skip to the origin fixes.

Cause 1: the origin server returned the 500

This is the most common cause, and it’s the one Cloudflare’s docs lead with. Your app crashed while handling the request, or a dependency it needs was down.

How to confirm it

  • The origin status code filter in HTTP Traffic shows 500s.
  • A direct request to the origin, as shown above, also returns 500.
  • The body has your framework’s error page, or a message such as Error establishing database connection.

Fixing an origin 500

  1. Note the exact time, the timezone and the full URL of a failing request. Cloudflare asks for these details whenever you take a 5xx to your hosting provider.
  2. Search the origin’s application and web server logs for that timestamp and path. Look for the stack trace or the failed database call.
  3. If the origin logs are clean, check the logs of every load balancer, cache, proxy or firewall between Cloudflare and the origin. The docs warn that the cause doesn’t always show up in the origin server’s own logs.
  4. For a database connection error, check that the database is up and that the app’s credentials and host settings match. If the database is refusing new connections, the guide to Postgres reserved connection slots covers that case.
  5. If someone else hosts the origin, send them the error code, the time and timezone, and the URL, and ask them to check their side.
  6. Request the URL again, through Cloudflare and directly, and confirm both return 200.

If you see blank white pages and no error code, Cloudflare’s advice is the same: pause Cloudflare to confirm the origin is at fault, then work through it with your host.

Cause 2: a Worker threw an exception

When a Worker on the route throws and can’t return a response, the client gets an error page. Cloudflare’s Workers errors reference gives the code for this as 1101. The 500 page lists the same exception as a source of HTTP 500s. A Worker that catches an error and returns new Response(..., { status: 500 }) also produces a 500, and that one is your own code’s doing.

How to confirm it

  • Metrics. Go to Workers & Pages, select the Worker, and open the Errors by invocation status chart. It splits errors into Uncaught Exception, Exceeded CPU Time Limits, Exceeded Memory and Internal.
  • Workers Logs. Filter with $metadata.error EXISTS to see every log line that carries an error, or with $workers.outcome = "exception" to see only the requests that ended in an uncaught exception.
  • Live tail. Run the command below and send a request that fails. Exceptions show up in the exceptions field of the JSON.
npx wrangler tail

If the chart shows Exceeded CPU Time Limits, you are dealing with Error 1102, which has a separate fix.

Fixing the Worker exception

  1. Turn on source maps so the stack trace points at your original file and line. Framework builds have to generate the maps as well as upload them.
  2. Read the exception and match it to the patterns Cloudflare documents. The most common ones are below.
  3. Reproduce the failure with local development using the same request.
  4. Deploy the fix and keep wrangler tail running until the exceptions stop.

The script will never generate a response. Your code is waiting on a Promise that never resolves or rejects, or a WebSocket that is never closed. Find the Promise that blocks the Response and make sure it settles. The no-floating-promises ESLint rule catches these before they ship.

TypeError: Illegal invocation. You destructured a runtime method and lost its this. Call it on the object:

export default {
  async fetch(request, env, ctx) {
    // const { waitUntil } = ctx; waitUntil(p) throws Illegal invocation
    ctx.waitUntil(somePromise);
    return fetch(request);
  },
};

Cannot perform I/O on behalf of a different request. You stored a Request, Response or stream in global scope and used it in a later request. Keep only the data:

let cachedData = null;

export default {
  async fetch(request, env, ctx) {
    if (cachedData) return new Response(cachedData);
    const response = new Response("Hello, world!");
    cachedData = await response.text();
    return new Response(cachedData, response);
  },
};

Keeping the site up while you fix it

If the Worker adds extras such as logging or headers in front of a working origin, ctx.passThroughOnException() sends the request on to the origin when the Worker throws. It only covers unhandled exceptions in your code. It does not cover errors from the origin fetch(). Wrap that call in try...catch and return a 5xx yourself.

export default {
  async fetch(request, env, ctx) {
    ctx.passThroughOnException();
    return fetch(request);
  },
};

The Error 1101 guide goes deeper on Worker exceptions.

Cause 3: a Page Rule refers to a wildcard that was removed

This is the one Cloudflare-side cause the docs call out by name. A Forwarding URL rule with two wildcards can use $2 in its destination. If someone later edits the rule and deletes one of the wildcards from If the URL matches, $2 now points at a wildcard that isn’t there. Any URL that triggers the rule then returns Error 500 (Internal server error). The Page Rules troubleshooting page describes this exact case.

How to confirm it

  • The 500s only hit URLs that match one Page Rule.
  • The rule’s match pattern has fewer * wildcards than the highest $n in its forwarding URL.
  • The errors began when the rule was edited.

Fixing the rule

  1. Open Rules > Page Rules and find the forwarding rule for the failing URLs.
  2. Count the * wildcards in If the URL matches.
  3. Remove any $n placeholder higher than that count. With one wildcard, only $1 is valid.
  4. Save, then request a matching URL and confirm you get the redirect.

Cloudflare’s general troubleshooting steps also say to review any recent changes to Page Rules, Transform Rules or Workers. If a rule edit lines up with when the 500s started, undo it first and look into it after. Cloudflare also suggests moving Page Rules to the newer Rules products, which are easier to configure.

When the 500 page names cloudflare or cloudflare-nginx

If the HTML body of the 500 contains cloudflare or cloudflare-nginx, Cloudflare asks you to contact its support team with:

  • your domain name
  • the time and timezone of the 500
  • the output of /cdn-cgi/trace from the browser that saw the error
curl https://www.example.com/cdn-cgi/trace

Put your own hostname in place of www.example.com. If a Worker is involved, the Error 1101 page also asks for the Ray ID from the error page, the Worker name, recent code changes and steps to reproduce. Keep in mind that Cloudflare Support only helps the domain owner. If you are a visitor, report the problem to the site owner.

Stopping 500s from coming back

  • Watch origin 5xx on its own. Check HTTP Traffic filtered by Origin status code after every origin deploy. A rise in that filter points straight at your app. Remember the 1% sample when you read small counts.
  • Group Worker failures. Cloudflare’s errors reference links to Workers Issues, which detects and groups recurring production failures. The Workers Issues guide covers setup.
  • Log the errors you catch. A Worker that catches an error and returns a 500 hides it from exception counts. Log the error before you return. To run that logging after the response is complete, pass the logging promise to ctx.waitUntil().
  • Lint for floating promises. Turn on no-floating-promises in CI to stop the never-resolving Promise bug before it merges.
  • Check wildcards when you edit rules. Each time a forwarding rule changes, compare the number of * wildcards with the highest $n placeholder.
  • Tie errors to changes. Most of the causes here start with a change. Keeping deploy and rule-edit times next to your error graphs makes the cause clear fast. See how to correlate incidents with deploys.

Tracing Cloudflare 500s back to a cause with Polylane

Polylane detects production issues and fixes them by opening pull requests. It connects to Cloudflare, so when your site starts returning 500s, it works out why, and the fix arrives ready for your team’s normal review.

Running on Cloudflare? See how Polylane monitors Cloudflare in production.

Common questions.

Is Error 500 on Cloudflare usually Cloudflare's fault?

Usually not. Cloudflare's docs say Error 500 points to a problem with your origin web server. The main Cloudflare-side cases are a Page Rule with a broken wildcard placeholder, or a 500 page whose HTML body contains cloudflare or cloudflare-nginx.

How do I tell whether the origin or the edge returned the 500?

In the dashboard, go to HTTP Traffic and filter by Origin status code and then by Edge status code. Also send one request straight to the origin with curl --resolve. If the origin returns 500 directly, the problem is on your server. Error analytics is based on a 1% traffic sample, so rare errors may not show up there.

Why does my Worker cause a 500 or a 1101?

A Worker that throws an uncaught JavaScript exception gets an error page with code 1101, and Cloudflare's Error 500 page lists that exception as a cause of 500s too. A Worker that catches an error and returns status 500 itself also produces a 500. Filter Workers Logs with $workers.outcome = "exception" or run wrangler tail to find the exception.

What should I send Cloudflare support for a 500?

If the HTML body contains cloudflare or cloudflare-nginx, send your domain name, the time and timezone of the error, and the output of /cdn-cgi/trace on your hostname from the browser that saw it. For Workers, also include the Ray ID, the Worker name, recent code changes and steps to reproduce.

Is it safe to pause Cloudflare to test the origin?

Pausing takes five minutes or less, according to Cloudflare. While paused, Rules, WAF and edge certificates stop working. Turning off the proxy on one DNS record limits that effect to one hostname, though that record still loses Rules, WAF and SSL/TLS certificates while unproxied. A single request to the origin IP with curl --resolve changes nothing for other visitors.

Why did a Page Rule suddenly start returning 500s?

The forwarding URL most likely uses $2, but the match pattern now has only one wildcard, so $2 points at nothing. Edit the rule so its placeholders match the number of * wildcards. With one wildcard, only $1 is valid.

How can I keep my site up while a Worker keeps throwing?

Call ctx.passThroughOnException() at the top of the fetch handler. Requests that hit an unhandled exception then go on to the origin as if the Worker weren't there. It does not cover errors from the origin fetch() call, so wrap that in try...catch.

Sources (7)

  1. Cloudflare docs: Error 500
  2. Cloudflare docs: Cloudflare 5xx errors
  3. Cloudflare docs: Troubleshoot Page Rules - General
  4. Cloudflare docs: Error 1101
  5. Cloudflare docs: Pause Cloudflare
  6. Cloudflare docs: Workers errors and exceptions
  7. Polylane: Workers Issues guide

About the author

Boris Tane

Founder of Polylane

Boris Tane is the founder of Polylane. He previously founded Baselime, observability for the future of the cloud, which Cloudflare acquired. At Cloudflare he built and led the Workers observability team.

Related

Nobody should be on-call. Polylane watches your infra, finds what broke, and writes the fix.

Get started for free