Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request origin does not match request base_url #22965

Closed
tpbowden opened this issue Jan 7, 2016 · 29 comments
Closed

Request origin does not match request base_url #22965

tpbowden opened this issue Jan 7, 2016 · 29 comments

Comments

@tpbowden
Copy link

@tpbowden tpbowden commented Jan 7, 2016

I am running the Rails 5.0.0 beta1 and after deploying to production have noticed something odd. My application is running being an Nginx reverse proxy which decrypts SSL.

Every time I try to submit a form, a ActionController::InvalidAuthenticityToken is raised. I have managed to narrow this down to line 399 in ActionController. The problem is that the request origin has https:// as the protocol and the request base_url has http:// as the protocol.

I have tried setting the X-Forwarded-Proto header and using config.force_ssl = true but neither made any difference.

I have not yet figured out a way to isolate a failing test case but am happy to look into it if anyone has any suggestions.

@tpbowden
Copy link
Author

@tpbowden tpbowden commented Jan 8, 2016

Fix by adding more headers in Nginx (X-Forwarded-Ssl on, X-Forwarded-Port 443 and X-Forwarded-Host hostname)

@tpbowden tpbowden closed this Jan 8, 2016
@afair
Copy link
Contributor

@afair afair commented Jan 19, 2016

I also ran into this issue. I'll describe it differently to help with future searches.

Submission of any form to Nginx proxying to a Puma unix socket running a Rails 5.0.0.beta1 app responds with:

Can't verify CSRF token authenticity

and throws:

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken)

even though the App's CSRF is set up correctly. The app works in development (puma or thin, without Nginx), and worked in Nginx/Puma/Rails-4.2. I don't know if this is specific to using Puma, but seems like the same problem could exist with other servers (unicorn, passenger, etc.)

I don't know if this behavior change is an error or intended.

I can confirm @tpbowden's solution worked for me as well. In my nginx configuration, I setup the app to proxy to puma listening on a unix socket. Here are the relevant parts of the configuration.

upstream myapp {
  server              unix:///path/to/puma.sock;
}
...
location / {
  proxy_pass        http://myapp;
  proxy_set_header  Host $host;
  proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header  X-Forwarded-Proto $scheme;
  proxy_set_header  X-Forwarded-Ssl on; # Optional
  proxy_set_header  X-Forwarded-Port $server_port;
  proxy_set_header  X-Forwarded-Host $host;
}

@tpbowden
Copy link
Author

@tpbowden tpbowden commented Jan 19, 2016

The issue isn't related to Puma and would happen to any web server running behind a reverse proxy which isn't passing the correct headers through. The way I managed to figure out how this issue was being caused was by following the error down the stack trace.

The root of the issue is the code in request.origin == request.base_url, which compares the Origin header's value against the base_url which is build up by a Rack::Response. There are 3 parts to this value, the scheme, the host and the port. All three of these are calculated based on a series of headers which not present by default when using a reverse proxy.

You can follow the source for Rack::Request#base_url here and it will become clear by looking in your web logs which headers are missing.

@tpbowden
Copy link
Author

@tpbowden tpbowden commented Jan 19, 2016

I've just done a bit more research and it is definitely the intended behaviour. It can be disabled in your app config using config.action_controller.forgery_protection_origin_check = false. It is enabled by default however.

@jonnyparris
Copy link

@jonnyparris jonnyparris commented Mar 11, 2016

@tpbowden Did you find a solution that doesn't disable forgery protection in the end? I think I'm facing the same problem and really struggling to make 'clean' progress.

@tpbowden
Copy link
Author

@tpbowden tpbowden commented Mar 11, 2016

Yes the problem was not passing enough headers through my reverse proxy (nginx) for Rails/Rack to determine whether the request was originally made using https or not (since it was already decrypted by the time it hit Rails)

@jonnyparris
Copy link

@jonnyparris jonnyparris commented Mar 11, 2016

ahh, so how did you find out what was missing...and how did you add the missing headers? Thanks so much for the quick reply!

@tpbowden
Copy link
Author

@tpbowden tpbowden commented Mar 11, 2016

If you look at @afair's comment above he was referenced a correct Nginx config, you just need to add the x-forwarded headers related to ssl. I figured out which ones were missing by digging though the source code of Rack's request object to find out how it determine's the protocol of the request.

@jonnyparris
Copy link

@jonnyparris jonnyparris commented Mar 11, 2016

Alas @afair's config doesn't work for me. Moreover, my logs have been annoyingly sparse since adding my Puma config (but that's another story.)
Thanks anyway, I'll think on it.

@jonnyparris
Copy link

@jonnyparris jonnyparris commented Mar 12, 2016

I narrowed my issue down to nginx not playing well with 2 virtual hosts running at the same time. I'm trying to run a staging VPS and a production VPS using jwilder's nginx-proxy.
Everything works fine using the standard generated conf file when only staging OR production is running. However I run into the ActionController::InvalidAuthenticityToken errors on form submissions when both are running at once.
I haven't solved it yet, but thought I'd leave this here in case anyone else follows a similar trail of blunders.

@straight-shoota
Copy link

@straight-shoota straight-shoota commented Feb 7, 2017

Thanks @tpbowden and @afair I was desperatly trying to make any sense of this oddly failing CSRF protection... then I found this issue 👍

@tripitakit
Copy link

@tripitakit tripitakit commented Feb 17, 2017

Many thanks @tpbowden and @afair, you've made my day!

@steady-daddy
Copy link

@steady-daddy steady-daddy commented Mar 29, 2017

@afair You, Sir, have saved a lot of my time. Thank you.

@marisveide
Copy link

@marisveide marisveide commented Jun 29, 2017

Thanks @tpbowden! You made my day!
This helped! Yeah! ;)

@stefatkins
Copy link

@stefatkins stefatkins commented Sep 14, 2017

Thanks so much :
I was missing this line :
proxy_set_header X-Forwarded-Proto $scheme;

@1c7
Copy link

@1c7 1c7 commented Oct 5, 2017

@afair fixed my problem! thank you!

@nunosilva800
Copy link

@nunosilva800 nunosilva800 commented Oct 12, 2017

My problem was similar, using heroku and cloudflare, solution was:

Make sure you have working SSL and HTTPS on Heroku (or wherever you're serving your Rails application.) Turn Cloudflare SSL to Full mode. Problem solved.

from: http://til.obiefernandez.com/posts/875a2a69af-cloudflare-flexible-ssl-mode-breaks-rails-5-csrf

@nicobobb
Copy link

@nicobobb nicobobb commented Dec 18, 2017

@Onumis Thank so much! I turn Cloudflare SSL to Full mode and fixed the problem.

ngzm added a commit to long-live-net/france that referenced this issue Feb 10, 2018
Add http header on nginx reverse proxy setting.
See also rails/rails#22965
ngzm added a commit to ngzm/g-link that referenced this issue Feb 10, 2018
Add http header on nginx reverse proxy setting.
See also rails/rails#22965
@dchandekstark
Copy link

@dchandekstark dchandekstark commented Jul 20, 2018

For those using Apache, this worked for me:

RequestHeader set X-Forwarded-Proto "https"

@eloyesp
Copy link

@eloyesp eloyesp commented Oct 9, 2019

I've just found this issue while using browser-sync as a proxy, fixed it with the following config:

module.exports = {
  "files": 'app',
  "proxy": {
    "target": "localhost:3005",
    "proxyReq": [
      function (proxyReq) {
        proxyReq.setHeader('Host', 'localhost:3005')
        proxyReq.setHeader('Origin', 'http://localhost:3005')
      }
    ]
  },
  "open": false,
  "reloadDebounce": 500
}

This made the trick for me.

@woooooojianjie
Copy link

@woooooojianjie woooooojianjie commented Oct 23, 2019

Thanks @tpbowden! This helped me !

@tonytonyjan
Copy link
Contributor

@tonytonyjan tonytonyjan commented Dec 24, 2019

It might not be related but just for your information.

I recently encountered the same issue. For those who do not use Nginx reverse proxy, you can create a middleware to rewrite X-Forwarded-Proto based on Cloudflare CF-VISITOR header, here is my solution:

# frozen_string_literal: true

require 'json'

class CloudflareProxy
  def initialize(app)
    @app = app
  end

  def call(env)
    return @app.call(env) unless env['HTTP_CF_VISITOR']

    env['HTTP_X_FORWARDED_PROTO'] = JSON.parse(env['HTTP_CF_VISITOR'])['scheme']
    @app.call(env)
  end
end

In config/application.rb:

config.middleware.use CloudflareProxy

You can also rewrite X-Forwarded-Port and X-Forwarded-Ssl and X-Forwarded-Host by the same technique. In my case, rewrote only X-Forwarded-Proto solved my problem.

A reference for CF-Visitor header:

https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-

CF-Visitor

A JSON object containing only one key called scheme. The value is identical to that of X-Forwarded-Proto (either HTTP or HTTPS). CF-Visitor is only relevant if using Flexible SSL.

abhchand added a commit to abhchand/girder-ansible that referenced this issue Feb 5, 2020
Rails application in production was complaining that

```
Request origin does not match request base_url
```

This is due to nginx headers being set incorrectly. It was forwarding
`http` instead of `https` which resulted in a url mismatch

See rails/rails#22965
abhchand added a commit to abhchand/reely-ansible that referenced this issue Feb 19, 2020
Rails application in production was complaining that

```
Request origin does not match request base_url
```

This is due to nginx headers being set incorrectly. It was forwarding
`http` instead of `https` which resulted in a url mismatch

See rails/rails#22965
viktorsmari added a commit to fablabbcn/smartcitizen-api that referenced this issue Apr 30, 2020
Was causing http urls instead of https. see:
#166

Solution is to force use SSL, and forward it from Nginx to the container
See more: rails/rails#22965
canriquez added a commit to canriquez/portfolio that referenced this issue Jun 11, 2020
@canriquez
Copy link

@canriquez canriquez commented Jun 11, 2020

I also ran into this issue. I'll describe it differently to help with future searches.

Submission of any form to Nginx proxying to a Puma unix socket running a Rails 5.0.0.beta1 app responds with:

Can't verify CSRF token authenticity

and throws:

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken)

even though the App's CSRF is set up correctly. The app works in development (puma or thin, without Nginx), and worked in Nginx/Puma/Rails-4.2. I don't know if this is specific to using Puma, but seems like the same problem could exist with other servers (unicorn, passenger, etc.)

I don't know if this behavior change is an error or intended.

I can confirm @tpbowden's solution worked for me as well. In my nginx configuration, I setup the app to proxy to puma listening on a unix socket. Here are the relevant parts of the configuration.

upstream myapp {
  server              unix:///path/to/puma.sock;
}
...
location / {
  proxy_pass        http://myapp;
  proxy_set_header  Host $host;
  proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header  X-Forwarded-Proto $scheme;
  proxy_set_header  X-Forwarded-Ssl on; # Optional
  proxy_set_header  X-Forwarded-Port $server_port;
  proxy_set_header  X-Forwarded-Host $host;
}

This worked great. thanks!

@ufukayyildiz
Copy link

@ufukayyildiz ufukayyildiz commented Dec 22, 2020

I also ran into this issue. I'll describe it differently to help with future searches.

Submission of any form to Nginx proxying to a Puma unix socket running a Rails 5.0.0.beta1 app responds with:

Can't verify CSRF token authenticity

and throws:

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken)

even though the App's CSRF is set up correctly. The app works in development (puma or thin, without Nginx), and worked in Nginx/Puma/Rails-4.2. I don't know if this is specific to using Puma, but seems like the same problem could exist with other servers (unicorn, passenger, etc.)

I don't know if this behavior change is an error or intended.

I can confirm @tpbowden's solution worked for me as well. In my nginx configuration, I setup the app to proxy to puma listening on a unix socket. Here are the relevant parts of the configuration.

upstream myapp {
  server              unix:///path/to/puma.sock;
}
...
location / {
  proxy_pass        http://myapp;
  proxy_set_header  Host $host;
  proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header  X-Forwarded-Proto $scheme;
  proxy_set_header  X-Forwarded-Ssl on; # Optional
  proxy_set_header  X-Forwarded-Port $server_port;
  proxy_set_header  X-Forwarded-Host $host;
}

Thank you so much. Worked for me too.

@fguillen
Copy link

@fguillen fguillen commented Apr 30, 2021

Thanks so much :
I was missing this line :
proxy_set_header X-Forwarded-Proto $scheme;

Also works without the socket thing:

upstream rails_app {
  server app:3000;
}

...
server {
  ...
  location @rails {
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://rails_app;
  }
}

@Furqanameen
Copy link

@Furqanameen Furqanameen commented May 29, 2021

Thank you so much buddy it's worked for me.
location @rails { proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://rails_app; }

evazion added a commit to danbooru/danbooru-infrastructure that referenced this issue Sep 14, 2021
Fix issue described in [1]. We were incorrectly overriding the
X-Forwarded-Proto header from the ingress, which caused CSRF token
validation in Rails to fail.

1: rails/rails#22965.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet