Enforcing trailing slashes v2

1. Caddy version (caddy version): 2

2. How I run Caddy:

caddy run

a. System environment:

ubuntu 18.04 lts

b. Command:

Paste command here.

c. Service/unit/compose file:

Paste full file contents here.
Make sure backticks stay on their own lines,
and the post looks nice in the preview pane.

d. My complete Caddyfile or JSON config:

http://localhost:3000 {
  root * /dist/
  try_files {path} {path}.html
  redir /admin /admin/
  redir /wx /wx/

3. The problem I’m having:

My issue is that I’m generating static html files like /admin.html and /wx.html and I want for enforce a trailing slash because these are vue/vue-router apps so I want the urls to look like /wx/#kcrg not /wx#kcrg. I don’t know why I care so much, but I do. Right now I’m using “external enforcement” from the docs, but I can’t add a line item for each file I generate… that’s unmaintainable. What else can I do?

4. Error messages and/or full log output:

5. What I already tried:

6. Links to relevant resources:

Well, having the trailing slashes would break file_server because then Caddy would be looking in a directory at /dist/wx/ (and subsequently a file index.html inside that directory) instead of a file /dist/wx (with try_files rewriting to add .html if it exists, of course).

Do you have a /dist/wx/index.html that file_server ends up serving?

But anyways, in general you can use the path_regexp matcher plus the file matcher to do this (it’s sorta funky though because the order in which matchers are run is not guaranteed, and the file matcher needs to use the result of the path_regexp matcher so you must use route blocks to enforce the order they end up running in):

@firstSegment path_regexp first ^/([^/]+).*
route @firstSegment {
	@htmlExists file /{re.first.1}.html
	redir @htmlExists /{re.first.1}/

So basically this will match paths which have a first path segment (which will end up being all requests except exactly / in practice) and capture just the first part not including a / (i.e. [^/]+ meaning one or more of any character except /) and this will be accessible in the {re.first.1} placeholder for use afterwards. Then a file matcher is used to check if a file actually exists on disk with .html added to it, and if so, redirects to that path but with a / added.

Maybe it would be cleaner to render all html as /dir/index.html

Trailing slashes aren’t just optional decoration – they are significant in terms of hyperlinks and web application programming, But they can become just decoration if you design your web application and hrefs accordingly; you just have to write the HTML/application in a way that supports your desired custom use of trailing slashes. But usually just removing or adding them willy-nilly will cause trouble.

1 Like

I really value both your opinions on this, Matt and Francis. All the pages on this project are Vue apps that also use Vue Router. If I don’t enforce the trailing slash the app paths can look either of these two ways.


It doesn’t make any functional difference, so I guess you can describe my desire to enforce trailing slashes “willy nilly.” But I do prefer to enforce it in the case of this project. I’m going to try Francis’ suggestion and I’ll let you know how it works out.

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