Using header/statuses and rewrites from v1 Caddyfile to v2 with adapt

1. My Caddy version (caddy -version):

(devel) v2 (compiled to fix the php socket bug)

2. How I run Caddy:

./caddy adapt --config ./Caddyfile --pretty > caddy.json

a. System environment:

Debian 10.2, go1.13.5

d. My complete Caddyfile:

halcyon.domain.tld {
  tls user@domain.tld
  php_fastcgi / 127.0.0.1:9000 php
  root /opt/halcyon
  status 404 {
    /config.ini
    /.bat
    /.git
    /.ini
    /.sh
    /.svn
    /.txt
    /.tpl
    /.xml
  }

header / {
	# Enable HTTP Strict Transport Security (HSTS) to force clients to always
	# connect via HTTPS (do not use if only testing)
	Strict-Transport-Security "max-age=31536000;"
	# Enable cross-site filter (XSS) and tell browser to block detected attacks
	X-XSS-Protection "1; mode=block"
	# Prevent some browsers from MIME-sniffing a response away from the declared Content-Type
	X-Content-Type-Options "nosniff"
	# Disallow the site to be rendered within a frame (clickjacking protection)
	X-Frame-Options "DENY"
}


  # Begin Main
  rewrite {
    r ^/home/?$
    to /
  }
  rewrite {
    r ^/intent/toot/?$
    to /share.php
  }
  rewrite {
    r ^/login/?$
    to /login/login.php
  }
  rewrite {
    r ^/auth/?$
    to /login/auth.php
  }
  rewrite {
    r ^/logout/?$
    to /login/logout.php
  }
  rewrite {
    r ^/terms/?$
    to /login/terms.php
  }
  rewrite {
    r ^/privacy/?$
    to /login/privacy.php
  }
  rewrite {
    r ^/imprint/?$
    to /login/imprint.php
  }
  # End Main

  # LTL
  rewrite {
    r ^/local/?$
    to /local.php
  }

  # FTL
  rewrite {
    r ^/federated/?$
    to /federated.php
  }

  # Notice
  rewrite {
    r ^/notifications/?$
    to /notifications.php
  }

  # Who to follow
  rewrite {
    r ^/whotofollow/?$
    to /who_to_follow.php
  }

  # Direct
  rewrite {
    r ^/direct/?$
    to direct.php
  }

  # Instance
  rewrite {
    r ^/instance/?$
    to instance.php
  }

  # Begin Lists
  rewrite {
    r ^/lists/?$
    to lists.php
  }
  rewrite {
    r ^/lists/(\d+)/?$
    to lists_view.php?id={1}
  }
  rewrite {
    r ^/lists/(\d+)/add/?$
    to lists_add.php?id={1}
  }
  # End Lists

  # Begin Search
  rewrite {
    r ^/search/?$
    to /search_hash_tag.php
  }
  rewrite {
    r ^/search/users/?$
    to /search_user.php
  }
  # End Search

  # Begin Settings
  rewrite {
    r ^/settings/?$
    to /settings_general.php
  }
  rewrite {
    r ^/settings/profile/?$
    to /settings_profile.php
  }
  rewrite {
    r ^/settings/appearance/?$
    to /settings_appearance.php
  }
  rewrite {
    r ^/settings/filters/?$
    to /settings_filters.php
  }
  rewrite {
    r ^/settings/media/?$
    to /settings_media.php
  }
  rewrite {
  		r ^/settings/followers/?$
    to /settings_accounts.php
  }
		rewrite {
  		r ^/settings/mutes/?$
    to /settings_accounts.php
  }
		rewrite {
  		r ^/settings/blocks/?$
    to /settings_accounts.php
  }
  # End settings

  # Begin user
  rewrite {
    r ^/@(.+)@(.+)\.([a-z]+)/?$
    to /user.php?user=@{1}@{2}.{3}
  }
  rewrite {
    r ^/@(.+)@(.+)\.([a-z]+)/status/(.+?)?$
    to /user.php?user=@{1}@{2}\.{3}&status={4}
  }
  rewrite {
    r ^/@(.+)@(.+)\.([a-z]+)/media/?$
    to /user_only_media.php?user=@{1}@{2}\.{3}
  }
  rewrite {
    r ^/@(.+)@(.+)\.([a-z]+)/with_replies/?$
    to /user_include_replies.php?user=@{1}@{2}\.{3}
  }
  rewrite {
    r ^/@(.+)@(.+)\.([a-z]+)/followers/?$
    to /user_followers.php?user=@{1}@{2}\.{3}
  }
  rewrite {
    r ^/@(.+)@(.+)\.([a-z]+)/following/?$
    to /user_following.php?user=@{1}@{2}\.{3}
  }
  rewrite {
    r ^/@(.+)@(.+)\.([a-z]+)/favourites/?$
    to /user_favorite.php?user=@{1}@{2}\.{3}
  }
  # End User

  # Begin Image
  rewrite {
    r ^/avatars/original/missing.png$
    to /assets/images/missing.png
  }
  rewrite {
    r ^/headers/original/missing.png$
    to /assets/images/missing_header.png
  }
  # End Image

  # 404
  rewrite {
    r ^/404/?$
    to /404.php
  }
}

3. The problem I’m having:

I’m trying to have the above Caddyfile rewritten to json for v2 usage, however it shows that the “status” directive is unrecognized and that the rewrites have a wrong argument count.

4. Error messages and/or full log output:

adapt: Caddyfile:5: unrecognized directive: status
same goes with header

(after commenting out the status/header brackets):
adapt: parsing caddyfile tokens for ‘rewrite’: Caddyfile:18 - Error during parsing: Wrong argument count or unexpected line ending after ‘rewrite’

It seems that the Caddyfile syntax also changed from v1 to v2, thought it was only having it parsed to json, if someone could change one or two of the rules above as an example and convert the status/header settings block It would be greatly appreciated. I’m currently moving 95 sites over to
caddy2 and understanding rewrites and statuses/header usage in v2 would be really helpful.

I saw that header was rewritten to headers and statuses is supposed to be respond, but I wonder how to put multiple urls to one status as shown in the config above. But I’m still clueless on how to convert the rewrites.

Hi @SolSoCoG, welcome to the Caddy community.

A lot of the work now can be done with matchers. Every directive works on a matched request, even if that matcher is “match everything” (an asterisk, *).

https://github.com/caddyserver/caddy/wiki/v2:-Documentation#matcher-tokens

For scenarios like regex replacements or having to handle multiple paths, first you define a complex matcher, then you run the directive on it.

https://github.com/caddyserver/caddy/wiki/v2:-Documentation#matcher

rewrite in particular can no longer be broken out into a multiline directive. You need a named complex matcher with a named regex check, e.g.:

matcher foo {
  path_regexp bar ^/lists/(\d+)/?$
}

Then you access the matched group with a placeholder:

If you are using regexp matchers, capture groups (both named and numeric) are available as well:

{http.matchers.PATH_REGEXP.PATTERN_NAME.CAPTURE_GROUP_NAME}

Replacing:

  • PATH_REGEXP with the name of the matcher that has the regular expression.
  • PATTERN_NAME with the lower-cased name you gave the pattern.
  • CAPTURE_GROUP_NAME with the name or index number of the capture group in the regular expression.

This will allow you to access capture groups from anywhere in your HTTP route.

https://github.com/caddyserver/caddy/wiki/v2:-Documentation#http-placeholders

So for the above regexp, the placeholder for the matched first group is {http.matchers.foo.bar.1} (yes - for now this part is a fair bit more complicated than v1. The developers are working on reducing some of this complexity). So, combined with the matcher, a fully functional rewrite for that one would be:

matcher foo {
  path_regexp bar ^/lists/(\d+)/?$
}
rewrite match:foo /lists_view.php?id={http.matchers.foo.bar.1}

Rinse and repeat for the other regex and rewrites.

To set a status on a number of paths, you can use another complex matcher:

matcher notfound {
  paths /config.ini /.bat /.git /.ini /.sh /.svn /.txt /.tpl /.xml
}
respond match:notfound 404

https://github.com/caddyserver/caddy/wiki/v2:-Documentation#respond

Your headers are universal, so you don’t need a complex matcher, you can use the catch-all. Use your existing block but replace header / with headers * to get it up-to-date with v2 syntax.

https://github.com/caddyserver/caddy/wiki/v2:-Documentation#headers

2 Likes

Thanks so far I managed to, more or less correctly, translate most of the rewrites, the page is halfway working.

However, It seems that I’m messing something up on the complex rewrites though:

rewrite {
r ^/@(.+)@(.+)\.([a-z]+)/?$
to /user.php?user=@{1}@{2}\.{3}
}

to

matcher rewr28 {
  path_regexp rewr28exp ^/@(.+)@(.+)\.([a-z]+)/?$
}
rewrite match:rewr28 /user.php?user=@{http.matchers.rewr28.rewr28exp.1}@{http.matchers.rewr28.rewr28exp.2}\.{http.matchers.rewr28.rewr28exp.3}

Am I getting something wrong here? Looking at the log I’m requesting /user.php?user=@@. :sweat_smile:

If you’re compiling from the latest on the v2 branch, it’s because the docs haven’t been synced with the latest source yet, oops. I updated them now, just for you: https://github.com/caddyserver/caddy/wiki/v2:-Documentation#http-placeholders

Updated pattern is shorter:

{http.regexp.REGEXP_NAME.CAPTURE_GROUP}

So in your case:

{http.regexp.rewr28exp.1}

etc.

1 Like

@SolSoCoG

Hi did you solve your issues with headers? I am running into the same issue like you with caddy2-beta10.

My caddyfile:

 nullday.de, www.nullday.de {
        root * /srv/nullday.de/
}

nspawn.org, www.nspawn.org {
        root * /srv/nspawn.org/
}

headers * {
        X-Frame-Options "SAMEORIGIN"
        X-Content-Type-Options "nosniff"
        X-XSS-Protection "1; mode=block; report=https://shibumi.report-uri.com/r/d/xss/enforce"
        Content-Security-Policy "default-src 'none'; base-uri 'self'; form-action 'none'; img-src 'self'; script-src 'self'; style-src 'self'; font-src 'self'; worker-src 'self'; object-src 'self'; media-src 'self'; frame-ancestors 'none'; manifest-src 'self'; report-uri https://shibumi.report-uri.com/r/d/csp/enforce"
        Referrer-Policy "strict-origin"
        Feature-Policy "geolocation 'none';midi 'none'; sync-xhr 'none';microphone 'none';camera 'none';magnetometer 'none';gyroscope 'none';speaker 'none';fullscreen 'self';payment 'none';"
        Expect-CT "max-age=604800, report-uri=\"https://shibumi.report-uri.com/r/d/ct/enforce\""
}

but everything what I get is:

validate: Caddyfile:23: unrecognized directive: X-Frame-Options

In the Caddyfile, the top level token is always the name of a site to serve. The headers directive must be placed inside site configuration, not outside of it.

In the Caddyfile you gave, have configured Caddy to serve headers and * as hostnames of a website. Inside the braces are supposed to go directives, but X-Frame-Options is not the name of a directive. If you removed that line, you’d encounter the exact same error with X-Content-Type-Options next, and so on…