Forum: NGINX try_files, POST, and redirecting requests to Passenger

Posted by Jason R. (jason_r17)
on 2013-01-09 00:13
The application I'm working on (CMS) has a few interesting requirements:

* Custom user domains
* Very heavily page cached.
* Any page can be POSTed to (page has a form on it)

In Apache, this was easily handled. If GET, then look in the cache, then
fall to Passenger. Otherwise, just go straight to Passenger.

I have been unable to get nginx working for my needs and am wondering if
anyone else has any insight into how to solve this problem.

Basically what I want is the following (but can't because try_files
can't be in an if):

  location / {
    if ($request_method ~* ^(GET|HEAD)$) {
      try_files /cache/$domain/$uri
                   /cache/$domain/$uri.html
                   /cache/$domain/$uri/index.html
                   /maintenance.html
                  @passenger;
      break;
    }

    try_files /maintenance.html @passenger;
  }

  location @passenger {
    passenger_enabled on;
  }

I initially had the idea that try_files was more of a switch-statement,
and tried to do something like:

  try_files @cache maintenance.html @passenger;

then in @cache simply break if the request is not a GET, but that
obviously only ever went to @passenger, because @cache wasn't a real
file on the system.

I've tried the error_page 405 = @passenger route, but that has a very
severe problem in that it turns my POST request into a GET, and I was
unable to find out how to stop that. Is there a way?

I've also tried doing an internal redirect to a different location
block, something like:

  location /post {
     internal;
     passenger_enabled on;
  }

but then I end up with $uris that have /post/(uri i want) and don't know
how to tell nginx to ignore /post when sending down to passenger. Would
also appreciate ideas here if there are any.

Any other suggestions? I'm almost to resorting to a number of if
statements and really don't want to end up there.

I am using nginx release: 1.2.6 on ubuntu 12.04 and Passenger
Enterprise.

Thanks

Jason
Posted by Jonathan Matthews (Guest)
on 2013-01-09 02:32
(Received via mailing list)
On 8 January 2013 23:13, Jason R. <lists@ruby-forum.com> wrote:
>     if ($request_method ~* ^(GET|HEAD)$) {
>
>   location @passenger {
>     passenger_enabled on;
>   }

Does try_files accept a variable generated from a map based on the
$request_method variable, perhaps?

That's the way I usually avoid ifs.

Jonathan
--
Jonathan Matthews // Oxford, London, UK
http://www.jpluscplusm.com/contact.html
Posted by "António P. P. Almeida" <appa@perusio.net> (Guest)
on 2013-01-09 09:41
(Received via mailing list)
On 9 Jan 2013 00h13 CET, lists@ruby-forum.com wrote:

> I have been unable to get nginx working for my needs and am
> /cache/$domain/$uri/index.html
> }
You're mixing different things. break is a rewrite phase
directive, like if. So they're executed well before try_files and the
content phase handlers.

You should use the map directive.

At the http level:

map $request_method $idempotent {
    default 0;
    GET 1;
    HEAD 1;
}

then at the server level (vhost config):

location / {

    error_page 418 = @idempotent;

    if ($idempotent) {
        return 418;
    }

    try_files /cache/$domain/$uri /cache/$domain/$uri.html
    /cache/$domain/$uri/index.html /maintenance.html @passenger;
}

location @idempotent {
   try_files /maintenance.html @passenger;
}

location @passenger {
   passenger_enabled on;
}

Try it.
--- appa
Posted by Jason R. (jason_r17)
on 2013-01-09 15:22
"António P. P. Almeida" <appa@perusio.net> wrote in post #1091574:
> On 9 Jan 2013 00h13 CET, lists@ruby-forum.com wrote:
>
>     error_page 418 = @idempotent;
>
>     if ($idempotent) {
>         return 418;
>     }
>
> location @idempotent {
>    try_files /maintenance.html @passenger;
> }

I never knew about the map directive, that's quite interesting. One 
question though, will this make sure that a POST that hits the 
error_page 418 stays a POST when it goes through the @idempotent 
location?

Thanks for the info!

Jason
Posted by "António P. P. Almeida" <appa@perusio.net> (Guest)
on 2013-01-09 16:47
(Received via mailing list)
On 9 Jan 2013 15h22 CET, lists@ruby-forum.com wrote:

>> try_files /maintenance.html @passenger;
>> }
>
> I never knew about the map directive, that's quite interesting. One
> question though, will this make sure that a POST that hits the
> error_page 418 stays a POST when it goes through the @idempotent
> location?

Perhaps I misunderstood. The way it is configured above is such that
the @idempotent location will only be used for GET and HEAD requests.
All other requests are handled by the / location using the lenghty
try_files.

I thought that was your desired config.

> Thanks for the info!

You're welcome.

--- appa
Posted by Jason R. (jason_r17)
on 2013-01-09 20:39
"António P. P. Almeida" <appa@perusio.net> wrote in post #1091605:
> On 9 Jan 2013 15h22 CET, lists@ruby-forum.com wrote:
>
>>> try_files /maintenance.html @passenger;
>>> }
>>
>> I never knew about the map directive, that's quite interesting. One
>> question though, will this make sure that a POST that hits the
>> error_page 418 stays a POST when it goes through the @idempotent
>> location?
>
> Perhaps I misunderstood. The way it is configured above is such that
> the @idempotent location will only be used for GET and HEAD requests.
> All other requests are handled by the / location using the lenghty
> try_files.
>
> I thought that was your desired config.
>
>> Thanks for the info!
>
> You're welcome.
>
> --- appa

Sorry if I mispoke then, what I want is the exact opposte because POST 
should never hit a cache file.

We ended up going a slightly different route in that we hack the cache 
file location according to the request type (we have $cache_host because 
there's some other processing we do, not relevant here):

  # Set cache_path to a non-existant directory so try_files fails if we 
cannot
  # serve the request from the cache.
  set $cache_path "no-cache";
  set $cache_host $host;

  if ($request_method ~* ^(GET|HEAD)$) {
    set $cache_path "cache";
  }

  try_files
    /$cache_path/$cache_host/$uri
    /$cache_path/$cache_host/$uri.html
    /$cache_path/$cache_host/$uri/index.html
    /maintenance.html
    @passenger;

This way there's no possible way a valid file is found when POST-ing. 
This was the simplest solution we could come up with at this time.

Thanks for everyone's help.

Jason
Posted by "António P. P. Almeida" <appa@perusio.net> (Guest)
on 2013-01-10 14:05
(Received via mailing list)
On 9 Jan 2013 20h39 CET, lists@ruby-forum.com wrote:


> cannot
> /$cache_path/$cache_host/$uri.html
> /$cache_path/$cache_host/$uri/index.html
> /maintenance.html
> @passenger;
>
> This way there's no possible way a valid file is found when
> POST-ing.  This was the simplest solution we could come up with at
> this time.
>
> Thanks for everyone's help.

It's even simpler then. No need for map.

location / {
    error_page 418 = @post;

    if ($request_method = POST) {
        return 418;
    }

    try_files /cache/$domain/$uri /cache/$domain/$uri.html
    /cache/$domain/$uri/index.html /maintenance.html @passenger;
}

location @post {
   try_files /maintenance.html @passenger;
}

location @passenger {
   passenger_enabled on;
}

--- appa
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.