Caddy serves index.html instead of admin.html, or SPA don't work on non-root URLs

Hi. We have a trouble configuring Caddy. Please help!

Version: 0.10.4

I simplified config for the case.

We have 2 SPAs: main app and adm app. We want main app to open at dev.domain.net and adm app to open at admin.dev.domain.net

Also we need main app to serve all paths to index.html except some paths (in this snippet /user/ and /stat/) which it should proxy and adm app to serve admin.html (also some paths to proxy). Both index.html and admin.html lies in one root directory.

With this config it works correct except 404 error from the Caddy if i try to open some uri like admin.dev.domain.net/something, but it should serve admin.html. For the main app it works well.

I tried to change rewrite / admin.html to
rewrite / {
to {path} {path}/ /
}
in the admin.dev.domain.net block, but in this case Caddy serving index.html at admin.dev.domain.net instead of admin.html ! May be the index directive don’t work here?..

How to configure it properly? :slight_smile:

So Caddyfile:

http://dev.domain.net, http://admin.dev.domain.net {
  redir https://{host}{uri}
}

https://admin.dev.domain.net {
  log /var/log/caddy/access_admin.log
  errors /var/log/caddy/errors_admin.log

  gzip

  expires {
    match \.js$ 1y
    match \.png$ 1h
    match core\.[a-f\d]\.css$ 1y
  }

  rewrite {
    r ^/(user|stat)/(.*)$
    to /{1}/{2}/
  }

  root /var/www/front/dev/dist
  index admin.html
 
  rewrite / admin.html
 
  header / {
    X-Frame-Options "DENY"
  }

  basicauth / user password
}

https://dev.domain.net {
  log /var/log/caddy/access_main.log
  errors /var/log/caddy/errors_main.log

  gzip

  expires {
    match \.js$ 1y
    match \.png$ 1h
    match core\.[a-f\d]\.css$ 1y
  }

  rewrite {
    r ^/(user|stat)/(.*)$
    to /{1}/{2}/
  }

  root /var/www/front/dev/dist
  index index.html

  rewrite / {
    if {path} match ^/sitemap\.html$
    to {path} {path}/ /sitemap.html
  }

  rewrite / {
    if {path} match ^/sitemap\.xml$
    to {path} {path}/ /sitemap.xml
  }

  rewrite / {
    to {path} {path}/ /
  }

  header / {
    X-Frame-Options "DENY"
  }

  basicauth / user password
}

https://dev.domain.net/user/, https://admin.dev.domain.net/user/ {
  log /var/log/caddy/access_user.log
  errors /var/log/caddy/errors_user.log
  gzip
  proxy / http://127.0.0.1:3004 {
    transparent
    without user
  }
  cors / {
    allowed_headers Auth-Token
  }
}

https://dev.domain.net/stat/, https://admin.dev.domain.net/stat/ {
  log /var/log/caddy/access_stat.log
  errors /var/log/caddy/errors_stat.log
  gzip
  proxy / http://127.0.0.1:3005 {
    transparent
    without stat
  }
  cors / {
    allowed_headers Auth-Token
  }
}

Up. Nobody knows how to fix this?

There’s quite a lot going on here, and exactly what is being asked is a little confusing. You’ll probably get more help if you can reduce the complexity out of your situation by stripping it down as minimal as can be.

This seemed strange, so I tried to replicate it. I found the index directive to be working normally.

→ caddy -version
Caddy 0.10.4
→ cat Caddyfile
:2015
index admin.html
→ cat admin.html
Success!
→ cat index.html
Failure!
→ curl -i localhost:2015
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 8
Content-Type: text/html; charset=utf-8
Etag: "ou16ww8"
Last-Modified: Wed, 02 Aug 2017 00:05:20 GMT
Server: Caddy
Date: Wed, 02 Aug 2017 00:07:35 GMT

Success!

Suggest as @matt mentions, strip as much as you possibly can out of your Caddyfile and test. Then add extra directives one at a time. Let us know when you encounter behaviour that doesn’t match your expectation.

1 Like

I stripped config. Here domain.net is not a real domain, but a placeholder. In original config it differs.

https://admin.dev.domain.net {
  root /var/www/front/dev/dist
  index admin.html

  rewrite / admin.html
}

https://dev.domain.net {
  root /var/www/front/dev/dist
  index index.html

  rewrite {
    to {path} {path}/ /
  }
}

Files:

root@dev ~/ $ cat /var/www/front/dev/dist/admin.html 
admin ok
root@dev ~/ $ cat /var/www/front/dev/dist/index.html 
index ok

Requests:

felian@felian-VirtualBox:~/$ curl -i dev.domain.net
HTTP/1.1 301 Moved Permanently
Connection: close
Location: https://dev.domain.net/
Server: Caddy
Date: Wed, 02 Aug 2017 06:34:21 GMT
Content-Length: 59
Content-Type: text/html; charset=utf-8

<a href="https://dev.domain.net/">Moved Permanently</a>.

felian@felian-VirtualBox:~/$ curl -i admin.dev.domain.net
HTTP/1.1 301 Moved Permanently
Connection: close
Location: https://admin.dev.domain.net/
Server: Caddy
Date: Wed, 02 Aug 2017 06:34:25 GMT
Content-Length: 65
Content-Type: text/html; charset=utf-8

<a href="https://admin.dev.domain.net/">Moved Permanently</a>.

felian@felian-VirtualBox:~/$ curl -L dev.domain.net
index ok
felian@felian-VirtualBox:~/$ curl -L dev.domain.net/g
index ok
felian@felian-VirtualBox:~/$ curl -L admin.dev.domain.net
admin ok
felian@felian-VirtualBox:~/$ curl -L admin.dev.domain.net/g
404 Not Found

We want the last request to result with admin.html, not 404.

Also tried this config with

rewrite {
    to {path} {path}/ /
  }

instead of rewrite / admin.html in dev.admin.domain.net block. It leads last 4 curl requests to return index ok:

felian@felian-VirtualBox:~/$ curl -L dev.domain.net
index ok
felian@felian-VirtualBox:~/$ curl -L dev.domain.net/g
index ok
felian@felian-VirtualBox:~/$ curl -L admin.dev.domain.net
index ok
felian@felian-VirtualBox:~/$ curl -L admin.dev.domain.net/g
index ok
felian@felian-VirtualBox:~/$ curl -L admin.dev.domain.net/admin/
index ok

Listing of /var/www/front/dev/dist (root in config) directory :

root@dev ~/ $ ls /var/www/front/dev/dist/
admin.html           b.app.a2f0e111.js    b.game1.a4f18f87.js  b.game2.cb680127.js  sitemap.html
b.game3.2896ea39.js     b.core.87f10c2d.js   b.game4.d85919a0.js  favicon.ico         static
b.admin.266f034f.js  b.game5.6c582e87.js  b.game6.b0b57c63.js   index.html

Hope i described it clearly.

Also there is one another question: why it does 301?

The example domain names registered by the IETF, or perhaps the .example TLD, are both great for placeholder purposes.

Ahh, yep. In its one-liner format, rewrite / /admin.html only catches requests for exactly /, so it wouldn’t work on a request for /foo, or /g, etc.

You’ve already made the right change by using the blanket fallback rewrite (i.e. to {path} {path}/ /). You should end up with a request for /, which should serve what you specified with the index directive. This is the behaviour I’m experiencing, although I’m afraid I’ve updated to version 0.10.6 since the last test so it may not account for any bugs in 0.10.4 (upgrading is always recommended).

→ caddy -version
Caddy 0.10.6
→ cat Caddyfile
:2015
rewrite {
  to {path} {path}/ /
}
index admin.html
→ cat admin.html
This is admin.html.
→ cat index.html
This is index.html.

And the outcome was good for me:

→ curl localhost:2015
This is admin.html.

→ curl localhost:2015/g
This is admin.html.

Caddy’s Automatic HTTPS is responding to requests on port :80 with a redirect to HTTPS.

You solved this by using the -L flag, which instructs curl to follow Location headers and return the final result, much like a browser would. Combining the -i and -L flags would show you the chain of requests, like so:

→ curl -iL caddyserver.com
HTTP/1.1 301 Moved Permanently
Connection: close
Location: https://caddyserver.com/
Server: Caddy
Date: Wed, 02 Aug 2017 07:08:49 GMT
Content-Length: 59
Content-Type: text/html; charset=utf-8

HTTP/1.1 200 OK
Content-Security-Policy: style-src   'self' https://fonts.googleapis.com; 		                          script-src  'self' data: https://www.google-analytics.com https://checkout.stripe.com; 		                          img-src     'self' data: https:; 		                          font-src    'self' data: https: blob:; 		                          media-src   'self' https:; 		                          connect-src 'self' https:; 		                          object-src  'none';
Content-Type: text/html; charset=utf-8
Last-Modified: Fri, 19 May 2017 16:19:03 GMT
Referrer-Policy: no-referrer-when-downgrade
Server: Caddy
Strict-Transport-Security: max-age=31536000
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
Date: Wed, 02 Aug 2017 07:08:50 GMT
Transfer-Encoding: chunked

You would not need to use -L if you instead used the form curl -i https://example.com.

We will upgrade to the latest version of Caddy and test it, i will respond later soon. Thank you.

Upgraded to 0.10.6.

Behaviour did not changed.

I tried three variants of rewrite directive in admin.dev.domain.net block.
Details:

1 Code:

index admin.html
rewrite {
  to {path} {path}/ /
}

Results to: admin.dev.domain.net returns always index.html instead of admin.html from this block (seen in logs).

2 Code:

index admin.html
rewrite / admin.html

Results to: admin.dev.domain.net returns admin.html for / path and 404 for other paths.

3 Code:

index admin.html
rewrite {
  to {path} {path}/ /admin.html
}

Results to: admin.dev.domain.net returns index.html for / path and admin.html for other paths.


We need: admin.dev.domain.net returns admin.html for / path and admin.html for other paths.

Ahh, I think I know what the issue is.

If my reading of the index directive code is correct, by virtue of directly modifying the behaviour of Caddy’s static fileserver, the latest instance of the index directive in your Caddyfile will be authoritative for all sites served by that instance of Caddy.

In which case, I imagine the best way to proceed that would be to leave index as default, and rewrite twice in the admin subdomain - once to catch requests for / that would otherwise slip through to index.html, and one to fallback 404’s to the correct file.

admin.dev.example.com {
  ...
  # Catch index -> /admin.html
  rewrite / admin.html
  # Catch 404 -> /admin.html
  rewrite {
    to {path} {path}/ /admin.html
  }
}

dev.example.com {
  ...
  # Catch 404 -> /index.html
  rewrite {
    to {path} {path}/ /
  }
}

Certainly the documentation isn’t abundantly clear on whether index is universal, so this might be a good candidate for a Github issue to either implement per-site index usage as a feature request, or improve the documentation.

2 Likes

opened an issue: https://github.com/mholt/caddy/issues/1801

1 Like

Thank you. Your snippet solved the problem.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.