Fastcgi regex path match

(Aleks) #1
  • I trying to match *.php in my caddyfile because I have a lot of php files in a directory and don’t want to add all php files into a dedicated fcgi block.
  • show what you have already tried, {

  root /my/php-fpm/root-dir

  fastcgi /index.php php {
    read_timeout 30s
  fastcgi /dashboard/index.php php {
    read_timeout 30s

  fastcgi /div_findnext.php php {
    read_timeout 30s


When I now call the request goes to php.
When I now call the request goes not php it displays the configuration :astonished:

Any idea how I can deduce the fastcgi lines and make the setup more secure?
Thanks for help

(Matthew Fay) #2

The php preset already specifies that the requested path should have the extension .php, or otherwise be a directory containing index.php, before proxying to the FastCGI listener.

Have you tried this on its own?

fastcgi / php {
  read_timeout 30s

(Aleks) #3

Thanks, I haven’t tried it yet will do it in the evening at none production time.

Just to clarify, as for me is the doc not that clear.

When the path is / and the ext match then will the request passed to the fastcgi backend

Is this true?

This could be the relevant code line

Is this the right doc source link to contribute to the doc?

(Matthew Fay) #4

I don’t think the docs are unclear, per se; they quite explicitly outline the function of ext, split, and index subdirectives, and the fact that the php preset sets them.

Perhaps it wouldn’t hurt to summarize the behaviour, but the expectation of the documentation is that you read and understand it all.

As for the right place to contribute to the docs, I thought the current repository was made private… I don’t know if it would be appropriate to open an issue on the main repo to suggest documentation changes; cc @matt might be able to give you an answer?

(Aleks) #5

For people like me which are used to configure haproxy, nginx, apache and some other stuff is the statment

ext specifies the extension which, if the request URL has it, would proxy the request to FastCGI

not that clear.

I see with that syntax (=nginx) that every request goes to the fastcgi backend.

location ~ ^(.+\.php)(.*)$ { 
  fastcgi_split_path_info       ^(.+\.php)(.*)$;
  fastcgi_index  index.php;
  fastcgi_pass   upstream-php;

I would like to add such a example to the doc that this is similar to

fastcgi / upstream-php php 

because a lot questions in this forum looks to me that I’m not the only person which haven’t understand the fastcgi syntax right.

Another common question is how this works in caddy

location / {
          try_files $uri /index.php$is_args$args;

which is a combination of rewrite and fastcgi as far as I understood caddy.

@matt please can you tell me how I can contribute to the docs, thank you.

From my point of view I get used with regex and don’t using regex in config files is uncommon for me, yes I’m quite old school :wink:

As soon as I have switched to caddy I will write a blog and share the link here if anyone is interested.

(Matthew Fay) #6

Then surely you know that this:

quite literally means,

where the extension is .php? Caddy has the exact same behaviour as the example nginx configuration you’ve provided posted; nginx is sending .php requests, not all requests, to the FastCGI backend.

We do indeed see this one a lot; since there’s no literal equivalent to try_files, you have to use Caddy’s rewrite. Once you understand the differences in how they work, though, it’s pretty simple, and there are great examples in the rewrite docs at

It’s not a combination of rewrite and fastcgi, though; much like in nginx, location / { try_files ... handles which files to attempt to use, and location ~ ^(.+\.php)(.*)$ { handles the FastCGI proxy, so too does rewrite handle which file and fastcgi handles the FastCGI proxy. The try_files equivalent in rewrite can be used in scenarios other than PHP, too.

(Aleks) #7

@Whitestrake you are right.

This works

fastcgi / php {
    read_timeout 30s

I have now tried to exclude the config.php to be served via fastcgi via this line but I’m sure I will need to exclude it somewhere else.

fastcgi / php {
    except /config.php
    read_timeout 30s

I think the module is the right one to save sensitive data/files be accessible from http(s), right?

For the archive this is now my caddyfile

(netlify-ips) {

  realip {

(sensitive-data) {
  status 404 {
} {
  tls off
  redir / https://{hostonly}/
  redir /registry
} {

  import netlify-ips
  import sensitive-data

  redir /registry

  root /home/phpapp/app/web-app-dev56

#   log / stdout "{remote} {>X-Bb-Ip} {user} [{when}] \"{method} {uri} {proto}\" {status} {size} \"{>Referer}\" \"{>User-Agent}\" REQUEST: {request} <> {request_body}"
  log / stdout "{remote} - {user} [{when}] \"{method} {uri} {proto}\" {status} {size} \"{>Referer}\" \"{>User-Agent}\""

  fastcgi / php {
    except /config.php
    read_timeout 30s

Some questions to this config.

  • can I use snippets for exceptline?
  • is this file good from caddy point of view?
  • is the position of a directive relevant in caddy, like in apache or irrelevant like in nginx, except for regex matches?

(Matthew Fay) #8

No, unfortunately snippets need to be top / directive level as they’re called via import. They can’t be used as a subdirective.

The use of status is indeed one good way of protecting sensitive files. internal is another. In your case they’re mostly equivalent.

I would note that except /config.php won’t protect the file, it just means Caddy will serve it directly (i.e. in plain text) instead of sending it to be processed as a PHP script and returning the result; if it’s anything like wp-config.php, for example, it’s probably got plenty of sensitive information even in plain text. If you’re using status to protect it, the except subdirective won’t ever be relevant anyway; status turns away the client long before fastcgi operates.

Instead of redir / https://{hostonly}/, I’d probably redirect to https://{host}{uri}, unless you want to drop port and path information in the course of this redirect.

Lastly, I personally prefer to use the format rather than or tls off; I think it reads a little better, is easier to recognize, and it also respects when you change the port with the command line -http-port flag; that’s purely my preference, though.

The order of the Caddyfile is theoretically NOT important. At startup, the Caddyfile is parsed and each directive processes its own configuration blocks; each directive has a set order in the middleware chain, and the chain is always executed in that order, e.g. rewrite always completes its operations before any redir is executed.

Practically, the order of multiple instances of the same directive can be impactful; for example, in instances of multiple rewrite statements, one rewrite that satisfies its if and regex conditions and has the first, longest base path is chosen for each request. That means multiple unconditional rewrites from / should result in the top-most rewrite occurring each time.

This isn’t true of all directives, though, in some cases it is not relevant to pick a single configuration option, and others use different methods for matching which configuration to select for a request.

You can see the current directive ordering here - for each request, the chain is executed top-to-bottom:

(Aleks) #9

thanks for all you answer.

thanks for confirmation

Thanks for the hint but I want that the user goes to the start page.

That’s a really good input :wink:

From my point of view is the question answered.
Thank you @Whitestrake for your help an passion