10

I'd like to make my 404 page editable through Craft, but when I create a single for it, a URI is required.

Is there a way to make a single with no URI, or is the best route to setup globals for managing this content? Or is there a better way that I don't know about?

18

I would create a Single with the URI set to "404", and also set its Template to "404". That entry will automatically get loaded if you go to http://example.com/404, and load your 404.html template, so at the top of the template you should add this to it:

{#
 #  Fetch the 404 Single entry, if we don’t already have it.
 #  (requests to /404 will already have it.)
 #}
{% if entry is not defined %}
    {% set entry = craft.entries.uri('404').first() %}
{% endif %}

Craft 3 note: As of Craft 3 the 404.html template is only loaded if devMode is disabled in the current environment. If devMode is enabled, you'll continue get a Yii error message.

  • That works pretty well, only problem is if you hit /404 it doesn't have the 404 header. Not the end of the world, but would be nice. – Jeremy Gimbel Jun 21 '14 at 21:28
  • 1
    You could add a redirect to a non-existing page (e.g. 404-not-found), if last segment is 404. Not a clean solution, but you'd get a 404 header ;) – Victor In Jun 21 '14 at 21:30
  • 2
    @JeremyGimbel That was already the case, and it’s pretty common behavior (if you’re trying to directly access the 404 page, and you get the 404 page, you technically got exactly what you wanted). See github.com/404 for example. If you really don’t like that, you could add this before that conditional: {% if entry is defined %}{% exit 404 %}{% endif %} – Brandon Kelly Jun 21 '14 at 21:33
  • Does this not work in Craft 3? I've tried adding templates/404.html and templates/_404.html. Either way when I go to mysite.com/not-a-page I get a "Template not found: not-a-page" error. – Simon Kuran Jul 20 '18 at 18:23
  • 1
    @SimonKuran good idea! Just updated that. – Brandon Kelly Aug 4 '18 at 18:31
2

404 redirect:

You could set up your single to use 404-not-found (example) as a uri, and add a redirect:

{% redirect '/404-not-found' %}

to the /404.html file. That way every 404-page gets redirected to the single.

Downside: You will loose the 404-header, and the uri in the address-bar will be lost.

.

Load Single:

Set up a single, and load the single entry into the /404.html file:

{% set entry = craft.section('404-single-handle').first() %}

Now you can use the entry-var just like you would in a normal single template.

You will have to use a single-uri, but you could add a conditional to the single template (not the 404.html), which redirects to index if it gets accessed directly:

{% redirect '/' %}

.

EDIT: I didn't know a single can be set to 404 without breaking the functionality, so Brandon's answer is the way to go ;)

1

This is something I discuss in the Handling Errors Gracefully in Craft CMS article, not just for 404s, but for a number of different status codes/errors.

The actual templating code then ends up looking something like this for error.twig:

{# @var craft \craft\web\twig\variables\CraftVariable #}
{% set entry = craft.entries.section("genericError").one() %}

{% extends "_layouts/errors" %}

{% block _inline_css %}
    <style>
        {{ source ("errors/error_critical.min.css", ignore_missing = true) }}
    </style>
{% endblock %}

I use a separate layout template for all of the errors (because they mostly look the same, an image and a message coming from a Single), so here is the errors.twig layout:

{# The layout template for error pages #}
{# @var craft \craft\web\twig\variables\CraftVariable #}
{% extends "_layouts/site" %}

{% block main %}

    <section>
        <div class="bg-white flex flex-wrap mt-30 md:mt-85 xl:mt-85"
             id="properties">
            <div class="w-full mt-45 md:mt-120 xl:mt-180 text-center">
                <img src="{{ entry.errorImage.one().url }}"/>
            </div>
        </div>
    </section>

    <section>
        <div class="bg-white flex flex-wrap" id="properties">
            <div class="w-full my-45 md:my-120 xl:my-180">
                <h3 class="text-center font-surv-disp font-normal text-ss-blue-dark text-24 md:text-30 lg:text-38 mb-45">
                    {{ entry.errorMessage }}
                </h3>
            </div>
        </div>
    </section>

{% endblock %}

{% block _inline_js %}
{% endblock %}

Which just displays the generic information, and extends the overall site.twig layout.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.