[Solved] Rails application behind Caddy reverse Proxy

1. My Caddy version (caddy version):

v2.0.0-rc.1 h1:DxUlg4kMisXwXVnWND7KEPl1f+vjFpIOzYpKpfmwyj8=

2. How I run Caddy:

caddy start

a. System environment:

Distributor ID: Debian
Description: Debian GNU/Linux 9.9 (stretch)
Release: 9.9
Codename: stretch

d. My complete Caddyfile or JSON config:

ts.hasinet.at {

reverse_proxy * {
        to 172.16.56.40:80
        transport http {
                read_buffer 8192
}
}
}

3. The problem I’m having:

This is an Rails application which has its own Unicorn Server, so Caddy is only here for Reverse Proxy and SSL, Unicorn does Basic Auth for the application. If i try to update an record inside the Rails application i get the following Error:

[ERROR] disposals#update (ActionController::InvalidAuthenticityToken) "ActionController::InvalidAuthenticityToken"

Do I have to set some headers for Reverse proxying?

4. Error messages and/or full log output:

Rails debug log:

-------------------------------
Request:
-------------------------------

  * URL        : http://ts.hasinet.at/admin/disposals/9615
  * HTTP Method: PATCH
  * IP address : 127.0.0.1
  * Parameters : {"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"kUKiq0RsWOnvr/ajTp/a0aV2dyYB2khWpwH9ji6j/y91lAs8ynlHstk+IBERxnSr+tBuueO7eoRlCl7C+6AFJw==", "commit"=>"Speichern", "disposal"=>{"notes"=>"angeliefertes Gewicht 1000 kg", "waste_information_id"=>"20202020", "cleared"=>"1", "material"=>"Beton", "source"=>"Gseeb Gem Seebenstein"}, "printer"=>"1 terminal", "controller"=>"admin/disposals", "action"=>"update", "id"=>"9615"}
  * Timestamp  : 2020-04-08 16:59:51 +0200
  * Server : wwdh
  * Rails root : /home/app/wwdh/releases/20160903071427
  * Process: 1896

-------------------------------
Session:
-------------------------------

  * session id: "2b2875b4a776ad956fbc70db90f5de9a"
  * data: {"session_id"=>"2b2875b4a776ad956fbc70db90f5de9a",
   "_csrf_token"=>"5Napl44VH1s2kdayX1muel+mGZ/iYTLSwgujTNUD+gg="}

-------------------------------
Environment:
-------------------------------

  * CONTENT_LENGTH                                 : 402
  * CONTENT_TYPE                                   : application/x-www-form-urlencoded
  * HTTP_ACCEPT                                    : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
  * HTTP_ACCEPT_ENCODING                           : gzip, deflate, br
  * HTTP_ACCEPT_LANGUAGE                           : en-US,en;q=0.5
  * HTTP_AUTHORIZATION                             : Basic d3dkaDp3d2Ro
  * HTTP_CONNECTION                                : close
  * HTTP_COOKIE                                    : experimentation_subject_id=ImYyYzA2Zjg2LWY2Y2MtNDI4NS1hOWE3LTRmY2QwYmEyYjc3MiI%3D--7f07ab7079a4364138c4294d67d6e242a63d8098; _wwdh_session=MzZGY2t6MnNIenRRQTJLVUpXeWVNeEpjeTRabnlHcVlIR2Q0OGlhK3hvdFI1dkMwcm82NUJYN2V5SkNONUk1SUlYUm1xQ3NMTXd0WkpjaGpxa1g1ZWhveVZob3hzM21ZVnBUZnZwMzBJTUhkUWNCcXJnS3hMQzN5TW9qRG42NTBQUENJTzdTNXJKVkxhSzdmQkJXdzhBPT0tLTVtNmNxTTNmVnRpMFJaMXRKVldDOWc9PQ%3D%3D--4c912bc41476652a84128948be4226ebaf8619dc
  * HTTP_DNT                                       : 1
  * HTTP_HOST                                      : ts.hasinet.at
  * HTTP_ORIGIN                                    : https://ts.hasinet.at
  * HTTP_REFERER                                   : https://ts.hasinet.at/admin/disposals/9615/edit
  * HTTP_TE                                        : trailers
  * HTTP_UPGRADE_INSECURE_REQUESTS                 : 1
  * HTTP_USER_AGENT                                : Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
  * HTTP_VERSION                                   : HTTP/1.0
  * HTTP_X_FORWARDED_FOR                           : 172.16.0.1, 172.16.0.2
  * ORIGINAL_FULLPATH                              : /admin/disposals/9615
  * ORIGINAL_SCRIPT_NAME                           : 
  * PATH_INFO                                      : /admin/disposals/9615
  * QUERY_STRING                                   : 
  * REMOTE_ADDR                                    : 127.0.0.1
  * REQUEST_METHOD                                 : PATCH
  * REQUEST_PATH                                   : /admin/disposals/9615
  * REQUEST_URI                                    : /admin/disposals/9615
  * ROUTES_70038129124980_SCRIPT_NAME              : 
  * SCRIPT_NAME                                    : 
  * SERVER_NAME                                    : ts.hasinet.at
  * SERVER_PORT                                    : 80
  * SERVER_PROTOCOL                                : HTTP/1.0
  * SERVER_SOFTWARE                                : Unicorn 5.0.1
  * action_controller.instance                     : #<Admin::DisposalsController:0x007f660f8fa468>
  * action_dispatch.backtrace_cleaner              : #<Rails::BacktraceCleaner:0x007f66099a8758>
  * action_dispatch.cookies                        : #<ActionDispatch::Cookies::CookieJar:0x007f660f8eb418>
  * action_dispatch.cookies_digest                 : 
  * action_dispatch.cookies_serializer             : json
  * action_dispatch.encrypted_cookie_salt          : encrypted cookie
  * action_dispatch.encrypted_signed_cookie_salt   : signed encrypted cookie
  * action_dispatch.http_auth_salt                 : http authentication
  * action_dispatch.key_generator                  : #<ActiveSupport::CachingKeyGenerator:0x007f660d5d9dd0>
  * action_dispatch.logger                         : #<ActiveSupport::Logger:0x007f660b8e9680>
  * action_dispatch.parameter_filter               : [:password]
  * action_dispatch.redirect_filter                : []
  * action_dispatch.remote_ip                      : 127.0.0.1
  * action_dispatch.request.content_type           : application/x-www-form-urlencoded
  * action_dispatch.request.formats                : [#<Mime::Type:0x007f6609a121d0 @synonyms=["application/xhtml+xml"], @symbol=:html, @string="text/html", @hash=-4165152267936131272>]
  * action_dispatch.request.parameters             : {"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"kUKiq0RsWOnvr/ajTp/a0aV2dyYB2khWpwH9ji6j/y91lAs8ynlHstk+IBERxnSr+tBuueO7eoRlCl7C+6AFJw==", "commit"=>"Speichern", "disposal"=>{"notes"=>"angeliefertes Gewicht 1000 kg", "waste_information_id"=>"20202020", "cleared"=>"1", "material"=>"Beton", "source"=>"Gseeb Gem Seebenstein"}, "printer"=>"1 terminal", "controller"=>"admin/disposals", "action"=>"update", "id"=>"9615"}
  * action_dispatch.request.path_parameters        : {:controller=>"admin/disposals", :action=>"update", :id=>"9615"}
  * action_dispatch.request.query_parameters       : {}
  * action_dispatch.request.request_parameters     : {"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"kUKiq0RsWOnvr/ajTp/a0aV2dyYB2khWpwH9ji6j/y91lAs8ynlHstk+IBERxnSr+tBuueO7eoRlCl7C+6AFJw==", "commit"=>"Speichern", "disposal"=>{"notes"=>"angeliefertes Gewicht 1000 kg", "waste_information_id"=>"20202020", "cleared"=>"1", "material"=>"Beton", "source"=>"Gseeb Gem Seebenstein"}, "printer"=>"1 terminal"}
  * action_dispatch.request.unsigned_session_cookie: {"session_id"=>"2b2875b4a776ad956fbc70db90f5de9a", "_csrf_token"=>"5Napl44VH1s2kdayX1muel+mGZ/iYTLSwgujTNUD+gg="}
  * action_dispatch.request_id                     : fa1a6188-7909-4acf-a793-bdba0453e03c
  * action_dispatch.routes                         : #<ActionDispatch::Routing::RouteSet:0x007f660b9d4ce8>
  * action_dispatch.secret_key_base                : a7b959ead12b283ae385bd904eb439513d013467cafc02a368f32858ffea5dc30ea40c052eb0e6a385acb839bdebd13ecc18f4fdc0882d88ca90abb3287c41ec
  * action_dispatch.secret_token                   : 
  * action_dispatch.show_detailed_exceptions       : false
  * action_dispatch.show_exceptions                : true
  * action_dispatch.signed_cookie_salt             : signed cookie
  * rack.errors                                    : #<File:0x007f6608649b30>
  * rack.hijack                                    : #<Unicorn::HttpParser:0x007f6608c46fd8>
  * rack.hijack?                                   : true
  * rack.input                                     : #<Unicorn::TeeInput:0x007f660f90ba60>
  * rack.logger                                    : #<Logger:0x007f66090eab70>
  * rack.methodoverride.original_method            : POST
  * rack.multiprocess                              : true
  * rack.multithread                               : false
  * rack.request.cookie_hash                       : {"experimentation_subject_id"=>"ImYyYzA2Zjg2LWY2Y2MtNDI4NS1hOWE3LTRmY2QwYmEyYjc3MiI=--7f07ab7079a4364138c4294d67d6e242a63d8098", "_wwdh_session"=>"MzZGY2t6MnNIenRRQTJLVUpXeWVNeEpjeTRabnlHcVlIR2Q0OGlhK3hvdFI1dkMwcm82NUJYN2V5SkNONUk1SUlYUm1xQ3NMTXd0WkpjaGpxa1g1ZWhveVZob3hzM21ZVnBUZnZwMzBJTUhkUWNCcXJnS3hMQzN5TW9qRG42NTBQUENJTzdTNXJKVkxhSzdmQkJXdzhBPT0tLTVtNmNxTTNmVnRpMFJaMXRKVldDOWc9PQ==--4c912bc41476652a84128948be4226ebaf8619dc"}
  * rack.request.cookie_string                     : experimentation_subject_id=ImYyYzA2Zjg2LWY2Y2MtNDI4NS1hOWE3LTRmY2QwYmEyYjc3MiI%3D--7f07ab7079a4364138c4294d67d6e242a63d8098; _wwdh_session=MzZGY2t6MnNIenRRQTJLVUpXeWVNeEpjeTRabnlHcVlIR2Q0OGlhK3hvdFI1dkMwcm82NUJYN2V5SkNONUk1SUlYUm1xQ3NMTXd0WkpjaGpxa1g1ZWhveVZob3hzM21ZVnBUZnZwMzBJTUhkUWNCcXJnS3hMQzN5TW9qRG42NTBQUENJTzdTNXJKVkxhSzdmQkJXdzhBPT0tLTVtNmNxTTNmVnRpMFJaMXRKVldDOWc9PQ%3D%3D--4c912bc41476652a84128948be4226ebaf8619dc
  * rack.request.form_hash                         : {"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"kUKiq0RsWOnvr/ajTp/a0aV2dyYB2khWpwH9ji6j/y91lAs8ynlHstk+IBERxnSr+tBuueO7eoRlCl7C+6AFJw==", "commit"=>"Speichern", "disposal"=>{"notes"=>"angeliefertes Gewicht 1000 kg", "waste_information_id"=>"20202020", "cleared"=>"1", "material"=>"Beton", "source"=>"Gseeb Gem Seebenstein"}, "printer"=>"1 terminal"}
  * rack.request.form_input                        : #<Unicorn::TeeInput:0x007f660f90ba60>
  * rack.request.form_vars                         : [FILTERED]
  * rack.request.query_hash                        : {}
  * rack.request.query_string                      : 
  * rack.run_once                                  : false
  * rack.session                                   : #<ActionDispatch::Request::Session:0x007f660f8fb570>
  * rack.session.options                           : #<ActionDispatch::Request::Session::Options:0x007f660f8fb4f8>
  * rack.tempfiles                                 : []
  * rack.url_scheme                                : http
  * rack.version                                   : [1, 2]
  * unicorn.socket                                 : #<Kgio::Socket:0x007f660f90be20>

5. What I already tried:

Not much really since with Nginx reverse Proxy’ing it did work.

old NGINX config:

proxy_set_header Accept-Encoding "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Which headers are set in reverse_proxy directive in caddy?
maybe i have to add some explicitly?

6. Links to relevant resources:

Solution:

ts.hasinet.at {

reverse_proxy * {
        to 172.16.56.40:80
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-Proto {scheme}
        header_up Access-Control-Allow-Origin *
        header_up Access-Control-Allow-Credentials true
        header_up Access-Control-Allow-Headers Cache-Control,Content-Type
        transport http {
                read_buffer 8192
}
}
}
1 Like

By the way, I recommend you use the caddy fmt command to clean up your Caddyfile. A properly formatted Caddyfile is much easier to read!

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

I had the same problem. Please note that the solution listed above enables CORS, which can have security implications for your site.

I found that the only line necessary to fix the InvalidAuthenticityToken problem was the protocol line.

reverse_proxy ... {
  header_up X-Forwarded-Proto {scheme}
}
2 Likes

(Mod note: The X-Forwarded-Proto header is already sent to backends by default, so specifying that line should be unnecessary.)