Securing my proxied server - advice please

OK, here comes one of my newbie questions again… sorry!

I’ve got my Caddy Web Server running nicely and was wanting to secure one of my servers. I deployed basic_auth which worked for 95% of my case scenarios. But when the site is accessed through an app client (fyi… Home Assistant Companion - ios + android ) - I get a 401 Unauthorised error. I suppose the app won’t allow the input of user credentials and just tries to force it’s way through.

I tried providing a URL as follows. but they failed also…

So I am now thinking perhaps a token-based approach might work, something like…

I have looked at the jwt and login plugins and they are a bit over my head and seeking help on the simplest way to deploy this security feature. The token-url will end up being hard coded into my Home Assistant configuration (in a place that is secure); so I am not too concerned with dynamically creating the tokens securely or applying expiries.

I basically want to create a token-url in a leave-and-forget situation and have caddy interpret the directive. What is the simplest way I can do this? Or should I be considering something else?


Can’t you just use the auth built into Home Assistant?

Hi. I am using that for access to Home Assistant, yes. I should have been clearer… the content I wish to protect is in an iframe served-up by Home Assistant.
So the problem still remains. Is what I am seeking doable?

Hi again. So I have read a lot of info on JWT and feel I have a basic understanding now. Alas, I still can’t get it to work. I’ll detail below how I am trying to implement it. Thanks.

(1) The Caddyfile  {
        proxy / {

       jwt {
          path /
          allow user Milster
          secret /var/caddy/secret.txt

        log /etc/caddy access.log
        errors /etc/caddy/error.log

(2) The secret.txt file


(3) Constructing a JWT


  "typ": "JWT",
  "alg": "HS256"


  "user": "Milster",
  "groups": "user"


As declared in secret.txt…

secret  # is used in the following equation
  base64UrlEncode(header) + "." +

(4) Validating the JWT with "URL Query Parameter"

Here’s my new URL…

I’m happy to see that I escaped 401, 403 or 303 - it would appear I passed validation. But I am greeted by a very dull white page with zero content being served. This is where my “basic understanding” has me scratching my head. The server that I should be seeing is a media player, which requires websocket connection. So it’s fairly dynamic content.

I think I was incorrectly assuming that JWT would be like a door which once passed would then restore a normal URL without the …/?token=eyJ0eXAiO… portion. Wrong-o!

Any advise on what to do? Thinking this is not possible now.
@francislavoie, any thoughts on another user I might tag that could help me resolve this? Thank you

G’day @milster,

Just popping in to suggest you look at the login plugin for Caddy v1:

It pairs up really well with the JWT plugin and issues cookies on a successful login with a neat little interstitial HTML login page.

In case the query params are messing with the page load, this might help.

As for further troubleshooting - get yourself authenticated, then open up a modern browser with the developer tools open and on the network tab; then load the page and note all the sub-requests and their responses. You’re getting a blank white page, so it’s getting some response, but obviously it’s not loading in the player element. The developer tools network tab should elucidate this discrepancy.

1 Like

Hey Matthew (@Whitestrake ). Thanks for the pointer. Can’t wait to find some time to give it a go. Thanks for the suggestion!!

1 Like

Haven’t been able to get this working yet. I started with something basic like…

jwt {
    path /
    allow sub bob

login / {
         simple bob=secret,alice=secret

Which is straight from the docs. I get a 401 message for my page, but also this worrying error which I am not sure how to interpret. Has the code changed?

WARN[0000] DEPRECATED: Please set the login path by parameter login_path and not as directive argument (/etc/caddy/Caddyfile:21) 
JWT middleware is initiated

This part - it’s chewing on your / here. I’d remove it.

Firstly, because the default login path is /login - which is to say that this is where the plugin will render the login page. You then configure jwt to redirect unauthorized clients to this page. I wouldn’t advise using the web root to permanently render the login form.

Secondly, because you should use the login_path subdirective for login instead. If you really do wanna render the login form at /, use login_path /.

Here’s a current example from one of my production servers:

  jwt {
    path /
    redirect /login?backTo={uri}
  # Loginsrv
  login {
    login_path /login
    redirect_check_referer false
    htpasswd file=/etc/.htpasswd
    jwt_expiry 12h

The cookie_domain is useful here because the site in question is on a subdomain, and this allows the token cookie to apply to any subdomain of


Thanks so much! Will try that soon. Much appreciated!

1 Like

Nailed it! Very happy customer. Thank you!


Excellent - glad to hear it!