Try_files, POST, and redirecting requests to Passenger

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

On 8 January 2013 23:13, Jason R. [email protected] 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 M. // Oxford, London, UK
http://www.jpluscplusm.com/contact.html

On 9 Jan 2013 00h13 CET, [email protected] 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

On 9 Jan 2013 15h22 CET, [email protected] 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

“António P. P. Almeida” [email protected] wrote in post #1091574:

On 9 Jan 2013 00h13 CET, [email protected] 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

On 9 Jan 2013 20h39 CET, [email protected] 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

“António P. P. Almeida” [email protected] wrote in post #1091605:

On 9 Jan 2013 15h22 CET, [email protected] 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