Error Page returns HTTP 200. Don't suger-coat it - I want a 500

I followed the instructions here for creating a pretty ‘Internal Server Error’ for my site

Those instructions propose creating an ‘Error’ controller which can respond to erroring requests and redirect to a templated content page on the site

public class ErrorController : Controller
{
	[Route("Error")]
	public IActionResult Index()
	{
		if (Response.StatusCode == StatusCodes.Status500InternalServerError)
		{
			return Redirect("/status/internal-server-error"); // Standard templated content page
		}

The problem with this approach is that the Redirect interferes with the ability to return a 500 return status. Instead, we end up with a 302 for the redirect, then a 200 for the content page.

I saw an old version 8 package which would let you configure an http status code for each page but it looks obsolete. https://our.umbraco.com/packages/website-utilities/http-status-code/

The reason I really care is that I have some custom controllers at a few reserved paths in my umbraco site that I’m using as API endpoints called from the browser. When those controllers throw an exception, the exception handler kicks in and the browser javascript’s ‘fetch’ call thinks the API call went through just fine when in reality it didn’t.

I may be able to make all my API endpoints throw a certain type of custom exception which I could handle in middleware differently.

Anybody got any other suggestions?

This is a common ‘mistake’ when dealing with 404 and 500 error pages. I personally thing the tutorial is wrong. You need to consider this from a browser perspective. Let’s take an example URL that gives produces a 500 error when visited: https://website.com/some-page.

So in your example, you redirect to another page. From a browser perspective - but also from a search engine perspective, the https://website.com/some-page page returns a 302 because you get redirected to https://website.com/status/internal-server-error. This page now gives a 200 status. You can change this to 500, BUT what you are saying to the browser and search engines is that https://website.com/status/internal-server-error has a 500 error code. Not that they will get there, because they will not follow 302 redirects. And that is obviously not correct.

In the case of a 500 it’s not that big of a deal, but take a 404 error for example. If you do the same as above, but with 404, you never tell the browser/ search engine that a certain URL has a 404 status but only that it has a temporary redirect (302) and that the 404 page itself was not found…?

So the thing is, in my opinion you should NOT redirect. You want to ‘stay’ on the URL (https://website.com/some-page), give a 500 error response and write the 500 page to the response of the current request instead of redirecting to it.

I’m not sure exactly how to do that in this case, but I guess ChatGpt or CoPilot can help with that :wink: AndI’m also curious what other Umbraco developers think about this.

As I see it, you would probably not do a redirect for it to hit a error page.
To me this seems like a bad practice, since that error is occurring in that specific place, you should not move a visitor somewhere else.
Let a page fail, keep the url etc. intact for swap the View to a error page result.

What I usually do is just add the app.UseExceptionHandler("/error"); and then have a Error.cshtml to render a basic page.
I don’t tend to make any integration into Umbraco data etc. since I can’t guarantee that Umrbaco works when this page is hit.
It still produces a 200OK result, but that I fine since otherwise, as Luuk wrote, the browser would handle the rendering differently.
This could cause your HTML to just be ignored and the browser default error page to show.

Also when you say this:

I can’t help but think that if you have an API Controller you maintain, then why not wrap the body in a try/catch and return a ProblemDetails response?
I may be assuming too much, but to me it sounds like you make an API call, in which case I don’t think you should show a whole view page.

:face_vomiting:

These docs are not a great solution.

Below is the approach I’ve taken for this:

app.UseExceptionHandler(new ExceptionHandlerOptions
{
    ExceptionHandler = async (context) =>
    {
        var errorPageService = context.RequestServices.GetService<ErrorPageService>();
        var errorPageHtml = await errorPageService.GetErrorPageHtml();

        await context.Response.WriteAsync(errorPageHtml);
        await context.Response.CompleteAsync();

    }
});

Then the logic for your ErrorPageService is up to you, and there’s a few different approaches you can take for that:

  • Static error page with no moving parts that’s just read from local disk.
  • Static error page that’s generated using Umbraco content on save.
  • Dynamic error page that’s just a “normal” Umbraco content page (with optional caching)
  • A combination of the above with fallbacks if errors are encountered.

Best option largely depends on how complex you need that error page to be. While a static error page is fine a lot of exceptions are specific enough that the site is still working so having a more complete/interactive error page makes a lot of sense.