V2 404 page template on file_server

The existing posts I have read on this topic don’t have a working example. I have a simple file server where the root contains a 404.html that I want to be the body of the 404 response.

file_server
root * /srv/public/
try_files {path} {path}.html {path}/index.html

I already tried having /404.html at the end of my try files, but I don’t want to do that. I don’t want errors to have a 200 status code.

Some examples mention a status_code directive, but there is no status_code directive in v2. Maybe that was something that was considered that didn’t make it. Not sure.

I tried using respond and that didn’t seem to work either.

What am I missing?

When this is solved, it needs to be on the wiki.

And it looks like status_code is a directive that is being discussed and is part of a PR.

But that PR is closed and won’t be merged.

That proposal was superseded by the handle_errors directive: handle_errors (Caddyfile directive) — Caddy Documentation

And v2.1 will likely have an even simpler error_page directive if all you want to do is serve a static file for an error page. But the handle_errors directive gives you full control over handling errors any way you want, whether it be a static file, a reverse proxy, a templated response, or whatever else.

I read the doc for handle_errors. I still don’t find myself armed with the information on what to put in my Caddyfile to create the intended behavior.

When a GET request hits an unknown path, I was to respond with my 404.html body, and a status code of 404.

I don’t know how to make that happen with the available directives. Inside a handle_errors block or otherwise.

I don’t fully understand what I have here. I just copied it from another forum post. This works, but not without file_server in the handle_errors block. That’s counterintuitive to me. It seems that root propagated into the handle_errors block, but not file_server.

file_server
root * /srv/public/
try_files {path} {path}.html {path}/index.html
handle_errors {
  @404 {
    expression {http.error.status_code} == 404
  }
  handle @404 {
    rewrite 404.html
  }
  file_server
}

That looks about right, for what is currently available. v2.1 will likely have some mapping directive and an error_page directive to make this a little simpler.

The root directive sets a variable that lives on the request context, and is valid for the duration of the request. So once invoked, it is remembered even through error routes.

You can get a sense for this if you look at the adapted JSON (showing only relevant excerpt):

{
	"routes": [
		{
			"handle": [
				{
					"handler": "subroute",
					"routes": [
						{
							"handle": [
								{
									"handler": "vars",
									"root": "/srv/public/"
								}
							]
						},
						{
							"handle": [
								{
									"handler": "rewrite",
									"uri": "{http.matchers.file.relative}"
								}
							],
							"match": [
								{
									"file": {
										"try_files": [
											"{http.request.uri.path}",
											"{http.request.uri.path}.html",
											"{http.request.uri.path}/index.html"
										]
									}
								}
							]
						},
						{
							"handle": [
								{
									"handler": "file_server"
								}
							]
						}
					]
				}
			],
			"terminal": true
		}
	],
	"errors": {
		"routes": [
			{
				"match": [
					{
						"host": [
							"localhost"
						]
					}
				],
				"handle": [
					{
						"handler": "subroute",
						"routes": [
							{
								"handle": [
									{
										"handler": "subroute",
										"routes": [
											{
												"group": "group0",
												"handle": [
													{
														"handler": "rewrite",
														"uri": "404.html"
													}
												]
											}
										]
									}
								],
								"match": [
									{
										"expression": "{http.error.status_code} == 404"
									}
								]
							},
							{
								"handle": [
									{
										"handler": "file_server"
									}
								]
							}
						]
					}
				],
				"terminal": true
			}
		]
	}
}

This provides a high degree of flexibility and control you don’t get with other servers.

This was another good learning experience for me with Caddy. Thank you for your time, Matt.

I need to start digging into configuration by json.

1 Like

There is a lot to learn! Caddy 2 is no child’s toy :slight_smile:

This topic was automatically closed after 30 days. New replies are no longer allowed.