Redirect upon 404

Hey,

Is there a way to trigger a redirect to a different URL when a 404 is triggered? It seems like the error handling currently only serves a file.

I’ve tried what was suggested here Rewrite / Redirect for 404 Error - #5 by gnanakeethan, to no avail.

If not, what is the rule for two matching label blocks? Does it match the first one?

Thanks in advance!

Hi @mrcncpt, welcome to the Caddy community.

rewrite has higher precedence than redir, so you should be able to rely on the presence of the {rewrite_uri} placeholder to check whether rewrite fell back to its last target (after failing all preceding targets).

rewrite {
  # Check for a file, then a folder
  # If neither exists, we would usually issue a 404
  # Instead, here we rewrite to /redir-target/
  to {path} {path}/ /redir-target/
}
redir {
  # /redir-target/ would usually still issue a 404
  # So manually redir from this path if it was rewritten to
  if {rewrite_uri} is /redir-target/
  # Modify the destination and status as required
  / http://www.example.com/some/path 301
}

The only caveat is that this only works if Caddy is the file server, because we’re not checking for an actual 404 but rather the circumstances that would usually produce a 404. So if you’re proxying, and want to capture a 404 from upstream and convert it to a 301 to the client, I can’t think of a solution (other than re-configuring the upstream).

https://caddyserver.com/docs/redir
https://caddyserver.com/docs/rewrite
https://caddyserver.com/docs/placeholders

Hi @Whitestrake,

Thanks for your comprehensive answer.

This only works with paths that do not exist anywhere on the server - For example, I am serving /folder1/folder2, so with the above configured navigating to / or /folder/ still return 404 responses.

Hmm!

In the rewrite, use to {path} /redir-target/ instead if you don’t want it to check for existing folders.

That didn’t seem to make a difference.

To clarify, I’m not actually trying to serve folder1, but I am trying to serve folder1/folder2. So the root URL, and root/folder1 should redirect to another domain, but root/folder1/folder2 should serve the files as expected.

Not sure if that helps explain my predicament!

Disregard my last comment entirely, I’d mistaken how rewrite validates its targets…

/folder1/ is unfortunately a valid target in your case. Hmm.

Do you want to be able to serve the index of /folder1/folder2/, or only real files within?

Does this solution have to be totally generic, or are you able to configure in advance which directories you want to not serve? Or maybe the folder depth? Trying to think of another way you might be able to achieve this.

Its only the index, but its more like folder1/folder2/folder3/folder4.

I’d prefer it to be generic, as I’m currently serving around 100 folders. I’m trying to serve the index of a certain depth only - they’re all the same depth if this makes it easier.

Could this not be added to the error directive, to redirect to a page, instead of serving a file?

My other thought was to create two label blocks, but I’m not sure how they’re matched or prioritised.

It does. This would redirect any request that DOESN’T HAVE exactly three path elements:

redir {
  if {path} not_match ^/[^/]+/[^/]+/[^/]+/?$
  / example.com/some/redirect 301
}

Alternately, this would redirect any request that DOES HAVE exactly two path elements:

redir {
  if {path} match ^/[^/]+/[^/]+/?$
  / example.com/some/redirect 301
}

Both are untested, but you get the idea.

It might be a good feature request. The best place for that would be github.com/mholt/caddy.

Site definitions are selected by the longest matching site label, I believe.

1 Like

Thanks @Whitestrake, I’ll give that a go.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.