Problem with old Kirby 2 Panel rewrites in Caddy v2

Caddy v2.0.0
PHP 7.4.5
caddy start --config Caddyfile

Having some problem with loading the admin panel (panel) on an old Kirby 2 CMS website. It loads from the /panel/index.php

Previously on Caddy v1, I’d use this:

# make panel links work
rewrite /panel {
    to {path} {path}/ /panel/index.php
}

On Caddy 2 I have tried

   rewrite /panel/* /panel/index.php

which works OK but doesn’t load the css and some js which load from inside the panel e.g.
http://domain.test/panel/assets/css/panel.min.css?v=2.5.13

Bit stuck, any pointers?

You’re looking for try_files functionality. In Caddy, this comes as a request matcher, but there’s also a shortcut directive that is a combination of that matcher with a rewrite. See here:

In your case, since you also need to limit this to a certain path, you’ll need to use the expanded form (see the link) and add a path matcher.

Specifically, you want to try {path} first if it exists as a file on disk, otherwise fallback to your index.

I must say though, that this rewrite might not even be necessary in Caddy v2 because the php_fastcgi shortcut includes its own try_files behaviour! Did you try without the rewrite?

1 Like

Thanks for you help Francis, I tried the following, but cannot get it to work. I’m obviously doidn gsomething wrong, but can’t quite see what… first time in Caddy 2

@panel_files {
    file {
      path /panel
      try_files {path} /index.php
    }
 }
 rewrite @panel_files {http.matchers.file.relative}

It does not work without this because it is like a seperate set of rewites and index.php under /panel. Of course the normal site with the index.php in the root works fine with the normal php_fastcg try_files` behaviour.

path needs to go outside of file. path is a separate matcher from file.

Are you sure about that? php_fastcgi does handle index.php files in subdirectories. See the expanded form:

Thanks Francis, so like this? If so I still have no luck 404.

 @panel_files {
    path /panel
   	file {
   		try_files {path} /index.php
   	}
 }
 rewrite @panel_files {http.matchers.file.relative}

I did remove this, cleared my browser cache and tried running without, but it dos not work. The previous version of Caddy v1 did of course work and required it.

Ah right, sorry. Path matchers are exact match, meaning /panel will only match requests to /panel and not /panel/somethingelse. You need to use /panel* or /panel/* instead.

Also you’ll probably need to use try_files {path} /panel/index.php instead, because try_files has no knowledge of the path matcher.

1 Like

Thanks again Francis for your help, I can get the admin ‘panel’ working with:

  try_files /panel* {path} {path}/ /panel/index.php?{query}

However it seems to ignore or not match the /panel* part as now all pages go to the admin panel!

To recap the root of the site has an index.php which coupled with the try_files {path}/ /index.php?{query} work fine for the site but not the /panel admin subdirectory, which also has an index.php.

So I guess I really need to run:
try_files /panel* {path} {path}/ /panel/index.php?{query}
when the path is /panel*
and
try_files {path}/ /index.php?{query}
when it is not?

Any ideas?

I don’t believe try_files accepts a matcher. The syntax is:

try_files <files...>

It’s not a true directive, in the sense that try_files does not directly configure its own specific module. Instead, it’s effectively a kind of common shorthand for a longer snippet of Caddyfile config:

The try_files directive is basically a shortcut for:

@try_files {
	file {
		try_files <files...>
	}
}
rewrite @try_files {http.matchers.file.relative}

try_files (Caddyfile directive) — Caddy Documentation

So you could use the long-form instead and add a path matcher to @try_files, which would get you what you’re after.

Basically, along the same kind of lines you were getting at here: Problem with old Kirby 2 Panel rewrites in Caddy v2 - #5 by WoofDog

Except, use /panel/* instead of /panel (path matching is exact, as @francislavoie mentioned in the comment immediately following yours). And use {path} {path}/ /panel/index.php?{query} (which you said works) instead of {path} /index.php (which you said doesn’t).

1 Like

Thanks Matthew for you help, do you mean like the code below? If so I’m getting a 404 error for ‘/panel’ links, or have I misunderstood what you were saying?

  @panel_files {
    path /panel*
  	file {
  		try_files {path} {path}/ /panel/index.php?{query} 
  	}
  }
  rewrite @panel_files {http.matchers.file.relative} 

Yes, the code you posted should be functionally identical to this:

try_files {path} {path}/ /panel/index.php?{query}

Except, limited only to request paths beginning with /panel.

Sadly, as stated it errors:
This works for the admin panel (/panel) but all the other links also goto admin…
try_files {path} {path}/ /panel/index.php?{query}

This errors (404) for /panel

 @panel_files {
    path /panel*
  	file {
  		try_files {path} {path}/ /panel/index.php?{query} 
  	}
  }
  rewrite @panel_files {http.matchers.file.relative} 

A bit confused now as it worked before in Caddy v1 with

rewrite /panel {
   to {path} {path}/ /panel/index.php
}

Okay, I think we need to back up here.

What’s your full Caddyfile? What’s the directory structure of the project you’re trying to serve? What are some example URLs that should work and which files should those URLs be rewritten to and serving?

Thanks Francis, let me know if you need anything else:

OK, slightly shortened Caddy File (just removed extra vhosts)

# Snippets

# Common server snippet use 'import common'
(common) {
  encode gzip
  php_fastcgi localhost:9000
  file_server
  header * {
    X-Frame-Options = "DENY"
		X-XSS-Protection "1; mode=block"
		X-Content-Type-Options "nosniff"
		X-Frame-Options "SAMEORIGIN"
	}
}

#---------------------
# Kirby 2 & 3 common snippet use 'import kirby'
(kirby) {  
  @blocked {
    path *.txt *.md *.mdown /site/* /kirby/*
  }
  redir @blocked /index.php
}

#---------------------
# Kirby 2 snippet use 'import kirby2'
(kirby2) {
  @panel_files {
    path /panel*
  	file {
  		try_files {path} {path}/ /panel/index.php?{query} 
  	}
  }
  rewrite @panel_files {http.matchers.file.relative} 
  #try_files {path} {path}/ /panel/index.php?{query}
  
  # Non /panel files
  try_files {path}/ /index.php?{query}
}

#---------------------

# vHosts - domains need to be added to hosts file
aap.test:80 {
  root * /Users/russellbaldwin/Sites/aap
  import common
  import kirby
  import kirby2
}
... More like the above

Directory Structure is (slightly abridged)

├── kirby
├── panel
│   ├── app
│   ├── assets
│   ├── composer.json
│   ├── gulpfile.js
│   ├── index.php
│   ├── license.md
│   ├── package.json
│   ├── phpunit.xml
│   ├── readme.md
│   └── tests
├── site
│   ├── accounts
│   ├── blueprints
│   ├── config
│   ├── controllers
│   ├── fields
│   ├── languages
│   ├── plugins
│   ├── snippets
│   └── templates
└── thumbs

URLs in admin panel, also load css and js which can live in this directory

http://aap.test/panel/pages/press

http://aap.text/panel/users

http://aap.test/panel/pages/contact/edit

Non panel URLs

http://aap.test/drink

http://aap.test/blog/food-and-lighting

That’s incorrect, should just be X-Frame-Options "DENY"

I don’t think you need this at all, because php_fastcgi does its own try_files for you already. See the expanded form:

I took a look at an nginx config example from here How to Setup Kirby CMS with Nginx on Ubuntu Linux - Geek Rewind

I think we could change it to something like this:

handle /panel* {
    try_files {path} {path}/ /panel/index.php?{uri}&{query}
}

Wrapping it in a handle is somewhat of a workaround to allow setting a matcher on a try_files directive which doesn’t otherwise allow a matcher. It’s closer to how the nginx config looks.

Once again Francis, thanks very much for all your help. I fixed the errors you pointed out and experimented with your ideas and ended up with this:

  # /panel files
  handle /panel* {
  try_files {path} {path}/ /panel/index.php?{uri}&{query}
  }
  
  # Non /panel files
  handle not /panel* {
    try_files {path}/ /index.php?&{query}
  }

I needed the try_files {path}/ /index.php?&{query} otherwise I get domains on the non-panel part of the site like:
http://domain.testindex.php/page
instead of
http://domain.test/page

Not sure why I had to wrap it is a handle not /panel* , but it would 404 the admin panel if I did not?

How does this look to you? Am I missing anything else from the Caddyfile or should I structure it differently?

interestingly enough the latest Kirby 3 needs pretty much none of this, just the blocking stuff.

This isn’t right, you can’t have a full matcher in that position. The only valid values for inline matchers are a literal *, a path matcher starting with /, or a named matcher starting with @. To actually do that type of match, you’d need to do this:

@notPanel {
    not /panel*
}
handle @notPanel {
  try_files {path}/ /index.php?&{query}
}

Surprisingly, the Caddyfile parser doesn’t complain, but it’s because the handle directive parser just ignores the arguments given (it doesn’t consider not a matcher in that position, but just a regular argument).

So you’re saying that it works as expected now? Great!

I don’t find this too surprising, modern PHP frameworks/apps all work somewhat the same, and the php_fastcgi directive is crafted to work as effectively as possible for the modern approach by default.

Hi Francis so I tried:

@notPanel {
    not /panel*
}
handle @notPanel {
  try_files {path}/ /index.php?&{query}
}

But get an error on the ‘not /panel*’ line (43):
validate: adapting config using caddyfile: Caddyfile:43 - Error during parsing: getting matcher module '/panel*': module not registered: http.matchers./panel*

Note sure what it means?
The way I tried in the previous post does validate and does work, but I’d like todo this properly if I can…

/panel* isn’t a matcher, it’s a path. You need the path matcher, i.e. not path <whatever path>.

1 Like

Thanks Matthew, that does the trick and I hope is a better config.

handle /panel* {
  try_files {path} {path}/ /panel/index.php?{uri}&{query}
}
  
@notPanel {
  not path /panel*
}

handle @notPanel {
  try_files {path}/ /index.php?&{query}
}

Where would be the best place to put a more complete example Caddyfile for Kirby 2 and Kirby 3, so other people can refer to it?

Thanks again to the both of you, I really appreciate your support!

1 Like

Whoops, my bad on that one :grin:

We have a wiki category here on the forums, feel free to make a writeup!