March 30, 2021
Estimated Post Reading Time ~

Efficient Error Handling with AEM, Dispatcher, and Apache Web Server

Let's start with a few basic assumptions, a good error page is:

  1. Branded
  2. Helpful
  3. Localized
  4. Fast

Out of the box, AEM uses Sling's default error page. Most projects, however, will overlay sling/servlet/errorhandler/404 to create a branded error experience. This can cause performance problems with Dispatcher. Let's look at a typical go-live example.

  1. A user requests /us/en/sup.html
  2. Dispatcher doesn't have it in cache, so it must...
  3. Ask Publish if it has the resource
  4. Publish returns our branded 404.
  5. Dispatcher will not cache any error documents.
  6. Apache sends the user the branded 404 at the original request path.

This creates several problems. The big one? The request lifecycle above is for every request. This means we're constantly talking to Publish when a document 404s. What happens if a marketer sends out a bad URL? You'll DDoS yourself.

By combining a few clever configs, we can achieve our four original tenets for a good error page. Let's look at the first request:

  1. Our user requests a page.
  2. Dispatcher does not have it in cache. It must check with Publish.
  3. Publish doesn't have the page in question.
  4. Publish serves up the default Sling error page (it's... not attractive).
  5. Dispatcher identifies the error, doesn't cache, but let's Apache handle the request with the ErrorDocument directive.
  6. Dispatcher makes another Publish request based on the ErrorDocument path.
  7. Publish responds with a 200 and our branded 404 error page.
  8. Dispatcher caches the new 404 page.
  9. Apache delivers our 404 content at the original location (with the correct 404 status code).

This approach may seem complex, but let's take a look at every subsequent request:

In every following request, Publish can be hyper-efficient responding to 404s with the default Sling error page, but dispatcher will continue to keep the branded and localized version in cache for fast delivery. No more rendering of components for every error.

So, how can we achieve this? Let's start with the AEM layer.

AEM

This is the easy part. Your 404 page can be a simple content page built with your components. You build this page as you would any other. Want search? Add a search component. Want a sitemap, add your sitemap component. Best of all, this can follow your typical localization process to propagate to all other locales and languages.

Dispatcher

With Dispatcher, we need to change our http.conf to let Dispatcher pass the error to Apache. Take a look at line 24:

LoadModule dispatcher_module modules/mod_dispatcher.so
<IfModule disp_apache2.c>
DispatcherConfig conf/dispatcher.any
DispatcherLog logs/dispatcher.log
DispatcherLogLevel warn
DispatcherDeclineRoot Off
DispatcherUseProcessedURL Off
# if turned to 1, the dispatcher does not spool an error
# response to the client (where the status code is greater
# or equal than 400), but passes the status code to
# Apache, which e.g. allows an ErrorDocument directive
# to process such a status code.
#
# Additionally, one can specify the status code ranges that should
# be left to web server to handle, e.g.
#
# DispatcherPassError 400-404,501
DispatcherPassError 1
</IfModule>

This let's us pass only our status code to Apache and tells it to deal with the error.

Important Note: I would strongly recommend only specifying the error codes you have HTML content for. There may be scenarios where you deliver a 500 and a JSON string to be handled client-side (handlebars, angular, etc.).

Apache

Our final piece. As of Apache 2.4.12, you can use 'variables' with an ErrorDocument. Here's how we can deliver our localized error pages:

SetEnvIfNoCase Request_URI "^/([^/]+)/([^/]+)" LOCALE=$1/$2
<If "tolower(%{ENV:LOCALE}) in {'hr/hr', 'il/he', 'mena/ar', 'ro/ro', 'sk/sk', 'si/si', 'ua/ua', 'si/en', 'ee/en', 'kr/ko', 'in/en', 'sea/en', 'tw/zh-Hant', 'hk/zh-Hant', 'hk/en', 'bg/en', 'lv/en', 'mx/es', 'gr/en', 'nl/nl', 'be/nl', 'lu/en', 'ca/en', 'sk/en', 'il/en', 'ch/fr', 'be/fr', 'lu/fr', 'mena/en', 'mt/en', 'se/sv', 'be/en', 'lt/en', 'ro/en', 'it/it', 'no/no', 'lu/de', 'za/en', 'at/de', 'de/de', 'es/es', 'cy/en', 'ru/ru', 'nz/en', 'ie/en', 'uk/en', 'dk/da', 'ca/fr', 'fi/fi', 'la/es', 'mena/fr', 'ua/en', 'rs/en', 'hr/en', 'br/pt', 'pt/pt', 'ch/de', 'pl/pl', 'jp/ja', 'fr/fr', 'ch/it', 'tr/tr', 'hu/hu', 'langmaster/en', 'au/en', 'us/en', 'cn/zh-Hans', 'cz/cs'}">
ErrorDocument 404 /%{ENV:LOCALE}/404.html
ErrorDocument 500 /%{ENV:LOCALE}/500.html
</If>
<Else>
ErrorDocument 404 /us/en/404.html
ErrorDocument 500 /us/en/500.html
</Else>

By using an if statement and routing our locale, we can insert the correct ErrorDocument for a given locale.

Additional Reading



By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.