Omit file name ending when rendering Markdown in Caddy

1. Caddy version (caddy version):

2.4.1

2. How I run Caddy:

Official docker container

a. System environment:

Ubuntu 20.04.2 LTS

b. Command:

docker-compose up

c. Service/unit/compose file:

version: '3.7'
services:
  web:
    image: caddy:2.4.1
    restart: unless-stopped
    ports:
     - "443:443"
     - "80:80"
    volumes:
     - $PWD/Caddyfile:/etc/caddy/Caddyfile
     - $PWD/www:/srv

d. My complete Caddyfile or JSON config:

x.x.x.x:80

encode zstd gzip
root * /srv
file_server
templates

log {
  output stderr
  level DEBUG
}

try_files {path}.md {path}
rewrite * index.html

3. The problem I’m having:

In caddy 1.x I could serve Markdown files without the md file ending, now I want to replicate that in Caddy 2.x but was not successful yet. It will render the file with the template in index.html if I add the .md ending but gives me a 404 if I omit the ending.

Examples:
http://x.x.x.x/agb
http://x.x.x.x/impress
…
Should all render the respective agb.md, impress.md, xxx.md file.

4. Error messages and/or full log output:

5. What I already tried:

The following config entry did not work as expected.

try_files {path}.md {path}

6. Links to relevant resources:

Please don’t forget to make a volume for /data! See the notes on Docker

I think the problem you’re having is with the directive order of the directives you defined. The Caddyfile sorts directives according to this predetermined order:

But I think what you want to do would be easier to implement by constructing the path inside your template file, similarly to how the Caddy docs do it.

See the index.html from the docs site, and its Caddyfile, as noted in the example for templates in the docs:

2 Likes

Thank you very much for your answer, that’s what I already did. :slight_smile: I just thought there would be a more “Caddy” v1 like way to do it. My config back than looked like this:

markdown / {
  template /template.html
}
ext .md .html

Rendering markdown got a lot harder in v2…

Almost nobody used the markdown directive in v1, because it wasn’t very useful rendering markdown in isolation. Rendering markdown as part of a larger template system is much more practical and useful.

Hi @matt, thanks for chiming in. I can imagine that it was a niche feature only used by some people. Still very useful for the ones that used it. :slight_smile:
My only critique is, that it was pretty difficult to come up with the appropriate template code/config combination for somebody who is not accustomed to golang templates (no possibility to debug what values these variables hold) compared to the former V1 config code snippet which I could incorporate in some minutes into my setup whereas I needed at least 2h to come up with a working solution with V2.

Thanks @francislavoie for this reminder, I only used http for testing and just started to use https after my post here and added the data folder to my compose setup.

@matt Just one more thing, after setting up the template to load markdown files without an ending I now get an infinite loop after calling a file with the md ending in my browser (Safari):

https://xxxxx/agb works as expected
https://xxxxx/agb.md gets me an infinite loop

Template code:

{{$pathParts := splitList "/" .OriginalReq.URL.Path}}
{{$markdownFilename := default "index" (slice $pathParts 1 | join "/")}}
{{$markdownFilenameWithMD := printf "%s.md" $markdownFilename }}
{{$markdownFile := (include $markdownFilenameWithMD | splitFrontMatter)}}

{{$title := default $markdownFilename $markdownFile.Meta.title}}
{{$body := $markdownFile.Body}}

Error log:

{"level":"error","ts":1622528506.8743768,"logger":"http.log.error.log0","msg":"template: index.html:4:20: executing \"index.html\" at <include $markdownFilenameWithMD>: error calling include: open /srv/agb.md.md: no such file or directory","request":{"remote_addr":"94.216.24.44:61049","proto":"HTTP/2.0","method":"GET","host":"","uri":"/agb.md","headers":{"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"],"Accept-Language":["en-us"],"Accept-Encoding":["gzip, deflate, br"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","proto_mutual":true,"server_name":""}},"duration":0.000547147,"status":500,"err_id":"dtsy5nu0q","err_trace":"templates.(*Templates).executeTemplate (templates.go:315)"}
{"level":"error","ts":1622528506.8744237,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_addr":"94.216.24.44:61049","proto":"HTTP/2.0","method":"GET","host":"","uri":"/agb.md","headers":{"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"],"Accept-Language":["en-us"],"Accept-Encoding":["gzip, deflate, br"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","proto_mutual":true,"server_name":""}},"common_log":"94.216.24.44 - - [01/Jun/2021:06:21:46 +0000] \"GET /agb.md HTTP/2.0\" 500 0","duration":0.000547147,"size":0,"status":500,"resp_headers":{"Server":["Caddy"],"Etag":["\"qu0ecqvu\""],"Content-Type":["text/html; charset=utf-8"],"Last-Modified":["Tue, 01 Jun 2021 06:07:38 GMT"],"Accept-Ranges":["bytes"],"Content-Length":["1146"]}}

curl output:

* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
* HTTP/2 stream 0 was closed cleanly, but before getting  all response header fields, treated as error
* stopped the pause stream!
* Connection #0 to host left intact
curl: (92) HTTP/2 stream 0 was closed cleanly, but before getting  all response header fields, treated as error
* Closing connection 0

What you can do about that is use a path_regexp matcher to capture the part of the path before the .md and respond with a redirect to the path without .md to enforce the way that actually works.

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