Anyone is kind enough to share his Caddy 2 Config?

Trying to use Caddy 2 as a reverse proxy for Nextcloud 19. I have tried almost all configs (Caddyfile) I can find on the internet, but most of them just do not work. The one that works keeps telling me my password is wrong? Thanks.

This Caddyfile works for me. Running Nextcloud 22 on vps with Ubuntu 20.04

cloud.mynextcloud.server { 
	root * /var/www/nextcloud/
	php_fastcgi unix//run/php/php7.4-fpm.sock

	redir /.well-known/carddav /remote.php/carddav 301
  	redir /.well-known/caldav /remote.php/caldav 301
    # Stop acccess from internet
    respond /config/* "Access denied!" 403
    respond /data/* "Access denied!" 403
    respond /.htacces "Access denied!" 403
    respond /db_structure/* "Access denied!" 403
    respond /.xml "Access denied!" 403
    respond /README "Access denied!" 403

    header {
		Strict-Transport-Security max-age=31536000;

@buskjan Thanks for sharing your config. Have you been able to use pretty urls with Caddy?

I can’t seem to get away from and have like with Apache.


Seemed to just have needed to add env front_controller_active true to the php_fastcgi section.

	php_fastcgi unix//run/php-fpm/php-nextcloud.socket {
		env front_controller_active true

EDIT 2: Although it seems to work in mot places, Rainloop does not work with “pretty” URL’s.

When I click the Rainloop icon I get 500 Internal Server Error on Request path is /apps/rainloop/app/?OwnCloudAuth

If I remove the env config the request path is /index.php/apps/rainloop/app/?OwnCloudAuth and it loads OK.

I am wondering also if we need to handle the other rewrite rules specified in the htdocs/.htaccess file?

<IfModule mod_headers.c>
  <IfModule mod_setenvif.c>
    <IfModule mod_fcgid.c>
       SetEnvIfNoCase ^Authorization$ "(.+)" XAUTHORIZATION=$1
       RequestHeader set XAuthorization %{XAUTHORIZATION}e env=XAUTHORIZATION
    <IfModule mod_proxy_fcgi.c>
       SetEnvIfNoCase Authorization "(.+)" HTTP_AUTHORIZATION=$1

  <IfModule mod_env.c>
    # Add security and privacy related headers

    # Avoid doubled headers by unsetting headers in "onsuccess" table,
    # then add headers to "always" table:
    Header onsuccess unset Referrer-Policy
    Header always set Referrer-Policy "no-referrer"

    Header onsuccess unset X-Content-Type-Options
    Header always set X-Content-Type-Options "nosniff"

    Header onsuccess unset X-Download-Options
    Header always set X-Download-Options "noopen"

    Header onsuccess unset X-Frame-Options
    Header always set X-Frame-Options "SAMEORIGIN"

    Header onsuccess unset X-Permitted-Cross-Domain-Policies
    Header always set X-Permitted-Cross-Domain-Policies "none"

    Header onsuccess unset X-Robots-Tag
    Header always set X-Robots-Tag "none"

    Header onsuccess unset X-XSS-Protection
    Header always set X-XSS-Protection "1; mode=block"

    SetEnv modHeadersAvailable true

  # Add cache control for static resources
  <FilesMatch "\.(css|js|svg|gif|png|jpg|ico)$">
    Header set Cache-Control "max-age=15778463"

  # Let browsers cache WOFF files for a week
  <FilesMatch "\.woff2?$">
    Header set Cache-Control "max-age=604800"
<IfModule mod_php7.c>
  php_value mbstring.func_overload 0
  php_value default_charset 'UTF-8'
  php_value output_buffering 0
  <IfModule mod_env.c>
    SetEnv htaccessWorking true
<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteCond %{HTTP_USER_AGENT} DavClnt
  RewriteRule ^$ /remote.php/webdav/ [L,R=302]
  RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
  RewriteRule ^\.well-known/carddav /remote.php/dav/ [R=301,L]
  RewriteRule ^\.well-known/caldav /remote.php/dav/ [R=301,L]
  RewriteRule ^remote/(.*) remote.php [QSA,L]
  RewriteRule ^(?:build|tests|config|lib|3rdparty|templates)/.* - [R=404,L]
  RewriteRule ^\.well-known/(?!acme-challenge|pki-validation) /index.php [QSA,L]
  RewriteRule ^(?:\.(?!well-known)|autotest|occ|issue|indie|db_|console).* - [R=404,L]
<IfModule mod_mime.c>
  AddType image/svg+xml svg svgz
  AddEncoding gzip svgz
<IfModule mod_dir.c>
  DirectoryIndex index.php index.html
AddDefaultCharset utf-8
Options -Indexes
<IfModule pagespeed_module>
  ModPagespeed Off

ErrorDocument 403 /
ErrorDocument 404 /
<IfModule mod_rewrite.c>
  Options -MultiViews
  RewriteRule ^core/js/oc.js$ index.php [PT,E=PATH_INFO:$1]
  RewriteRule ^core/preview.png$ index.php [PT,E=PATH_INFO:$1]
  RewriteCond %{REQUEST_FILENAME} !\.(css|js|svg|gif|png|html|ttf|woff2?|ico|jpg|jpeg|map|webm|mp4|mp3|ogg|wav)$
  RewriteCond %{REQUEST_FILENAME} !/core/ajax/update\.php
  RewriteCond %{REQUEST_FILENAME} !/core/img/(favicon\.ico|manifest\.json)$
  RewriteCond %{REQUEST_FILENAME} !/(cron|public|remote|status)\.php
  RewriteCond %{REQUEST_FILENAME} !/ocs/v(1|2)\.php
  RewriteCond %{REQUEST_FILENAME} !/robots\.txt
  RewriteCond %{REQUEST_FILENAME} !/(ocm-provider|ocs-provider|updater)/
  RewriteCond %{REQUEST_URI} !^/\.well-known/(acme-challenge|pki-validation)/.*
  RewriteCond %{REQUEST_FILENAME} !/richdocumentscode(_arm64)?/proxy.php$
  RewriteRule . index.php [PT,E=PATH_INFO:$1]
  RewriteBase /
  <IfModule mod_env.c>
    SetEnv front_controller_active true
    <IfModule mod_dir.c>
      DirectorySlash off

This is also something I am in need of!

I am bit baffled by the fact that - to the best of my knowledge and search-foo - there is no complete(ly) working example for caddy-v2 + nextcloud on the internet. I didn’t test the one with docker-compose labels instead of caddyfile (see my setup as a reason below), but that seems similarily incomplete too.

I have similar issues, although my setup is a bit different:

  • I run a docker(-compose)'d nextcloud, the -fpm variant (without the apache webserver in the container)
  • for now, there is a docker-composed nginx, that is the file_server (for static files) and reverse-proxy for nextcloud (and some other sites)
  • I am trying to replace this container’d nginx with a caddy on the host

Your “Access denied!” part has serious and stylistic issues! I think mine is better there.

  • serious: Your denied list of pathes is far from complete (AFAIK)!
  • quasi-serious: 403 does leak information that the requested resource exists, most example use 404 therefore.
  • stylistic, but also a maintenance burden: You should use named matcher here too, and set respond on that. This way for example, you can replace 403 with 404 at a single place!

Otherwise, I am in a bit of a bind here, as I am not versed in webserver config to put it very lightly: I very much struggle with nginx (and I would not touch apache with a ten-foot pole, partly because its config files).
There should be a fickin’ good caddy v2 example somewhere by now, that tries to faithfully replicate the example apache/nginx configs. There is an official example caddy v1 config, that seems to be a mostly equivalent rewrite of some contemporary nginx/apache config, but it seems that although caddy v1 is “deprecated”, v2 is not really there to be a good replacement - yet. Maybe it lacks just documentation-wise…

Here is my caddyfile, mostly copied from some example.
I tram the non-relevant parts and sites {
  # I run caddy on the host, and this is the root folder of nextcloud there
  root * /mnt/repo-base/volumes/nextcloud/html
  encode gzip

  # This is the opinionated shorthand, that is most likely needs custom overrides
  # php_fastcgi {
  # root /var/www/html
  # }

  # This is the long form of the php_fastcgi, mostly as seen here:
  route {
    # Add trailing slash for directory requests
    @canonicalPath {
      file {path}/index.php
      not path */
    redir @canonicalPath {path}/ 308

    # CUSTOM ADDITION TO php_fastcgi
    # Remove trailing slashes from dav protocols
    @trailingSlashDavPath {
      path_regexp davslash /((?:remote|public)\.php/(?:web|cal|card)?dav(?:/[^/]+)?)(?:/+)$
    rewrite @trailingSlashDavPath /{re.davslash.1}

    # If the requested file does not exist, try index files
    @indexFiles file {
      try_files {path} {path}/index.php index.php
      split_path .php
    rewrite @indexFiles {http.matchers.file.relative}

    # Proxy PHP files to the FastCGI responder
    @phpFiles path *.php
    # Host port 9001 is mapped to nextcloud's 9000, for container'd caddy use <container_name>:9000
    reverse_proxy @phpFiles {
      transport fastcgi {
        split .php
        # CUSTOM ADDITION TO php_fastcgi
        # This is the nextcloud root as seen/mounted inside the container
        root /var/www/html
        # CUSTOM ADDITION TO php_fastcgi - not really working
        # env front_controller_active true

  header {
  # enable HSTS
  # Strict-Transport-Security max-age=31536000;

  redir /.well-known/carddav /remote.php/dav 301
  redir /.well-known/caldav /remote.php/dav 301

  # .htaccess / data / config / ... shouldn't be accessible from outside
  @forbidden {
    path /.htaccess
    path /.xml
    path /3rdparty/*
    path /README
    path /config/*
    path /console.php
    path /data/*
    path /db_structure
    path /lib/*
    path /occ
    path /templates/*
    path /tests/*
  respond @forbidden 404
} {
1 Like

In case anyone wants to run caddy V2 as well as nextcloud all on the same host without docker and nextcloud in a subdirectory, then this snippet from my Caddyfile might help:

	redir /.well-known/carddav /nextcloud/remote.php/dav 301
	redir /.well-known/caldav /nextcloud/remote.php/dav 301
	redir /.well-known/webfinger /nextcloud/index.php{uri} 301
	redir /.well-known/nodeinfo /nextcloud/index.php{uri} 301

	handle /nextcloud/* {
		header {
			Referrer-Policy "no-referrer" defer
			X-Content-Type-Options "nosniff" defer
			X-Download-Options "noopen" defer
			X-Frame-Options "SAMEORIGIN" defer
			X-Permitted-Cross-Domain-Policies "none" defer
			X-Robots-Tag "none" defer
			X-XSS-Protection "1; mode=block" defer
		php_fastcgi unix//usr/local/var/run/php-fpm.sock {
			try_files {path} {path}/index.php /nextcloud/index.php index.php
			env front_controller_active true
			header_down -x-powered-by
		@forbidden {
			path /nextcloud/.htaccess
			path /nextcloud/data/*
			path /nextcloud/config/*
			path /nextcloud/db_*
			path /nextcloud/.xml
			path /nextcloud/README
			path /nextcloud/AUTHORS
			path /nextcloud/COPYING
			path /nextcloud/3rdparty/*
			path /nextcloud/lib/*
			path /nextcloud/templates/*
			path /nextcloud/occ
			path /nextcloud/console.php
			path /nextcloud/cron.php
		respond @forbidden 404

I use fastcgi via unix sockets here, this is not entirely understood by nextcloud as the proxy setup check does not pass (although it works fine), as there is no IP address to check in trusted_proxies.