{% Exit 404 %} doesn't work right with routes The {% exit 404 %} doesn't work right on routed templates. 

It's fine on pages that aren't affected by routes. Mysite.com/asdfasdf shows me the proper 404 page. 

I've got some routes configured like this:

URL: projects/type/*
Template: _projects/category.twig

URL: projects/*
Template: _projects/entry.twig


In the top of entry.twig, I've got code that says:

{% set entrySlug = craft.app.request.getSegment(2) %}
{% set entry = craft.entries.slug(entrySlug).one() %}

{% if entry is not defined or entry is null %}
    {% exit 404 %}
{% endif %}

{# regular page template below here #}


mysite.com/projects/WorkingProject - this works right.
mysite.com/projects/BrokenProjectLink - gives me a 500 error.

When I turn on dev mode, the Yii debugger gives me a 404 ( HTTP 404 – Not Found – yii\web\NotFoundHttpException ), which is the right thing. But turning devmode off, I don't get the 404 page, I get my 500 Internal Server Error page instead.

Again, 404s for pages that don't have any routing work fine. It's just things that are getting routed, and using the {% exit 404 %} code.

This all worked in v3.3.20 and earlier, but it doesn't seem to work the same in Craft 3.4.0.2.
When a route is accessed, entry isn't automatically defined.

The actual value for * is available in the any variable in your template—see this example:

Route: projects/type/(*)
Template: _test


{# templates/_test.twig #}
Substituted route param: {{ any }}


Do note that entry is not automatically set by Craft for any custom routes! It's your responsibility to take the incoming value (in your case, any) and look up the Entry, within the template, or bail if nothing is found:

{# We can totally skip the test for `any`, because this template is only loaded when there's a non-zero-length value in the route: #}
{% set entry = craft.entries({ title: any }).one() %}

{% if not entry %}
  {% exit 404 %}
{% endif %}

{# At this point, you've set `entry`, and can use it however you like! #}
{{ entry.myFieldHandle }}



  I'd recommend reviewing the requirements for your route, and consider using a more specific token in your route (i.e. (slug)), and matching based on a more stable value—titles can change, but IDs should stick around, and slugs are less prone to editing!