Comments
-
Greg Hurrell
I was going to say that perhaps the existing page model could be abused for this purpose.
We currently have the ability to pull wikitext markup or HTML out of the database for the product pages.
A third option could be to somehow reference the content on the disk (sendfile style or something, ugh).
But really, the whole point of having this kind of static content is to allow nginx to serve it without Rails ever being touched.
Not really sure though if the rewrite trickery that would be required is worth it.
-
Greg Hurrell
nginx notes
Will need to check this, but I believe nginx will only hit Rails if the requested filename does not exist:
if (-f $request_filename) { break; }
ie. if the directory exists, but no file does, it will still hit Rails.
This is actually what we want.
That is, given these directories
/public/products/synergy/
, we don't want nginx to try to serve those itself and effectively mask theproducts#show
action for URLs like https://wincent.dev/products/synergy.But we do want it to serve a static file like
/public/products/synergy/docs/index.html
if one is uploaded.Still, like I said, must test it. The config does have this set up:
index index.html;
So I don't know whether the presence of the directory alone will be enough to make it look for
index.html
and if it can't find it, return a 404 rather than forwarding the request to Rails.Static page types
There are really two types of static content that we might wish to serve:
- stand-alone HTML pages: we want these to be served by nginx without hitting Rails at all if possible
- HTML fragments, to be embedded in the existing site layout: by necessity, these need to be mediated by Rails itself
The alternative
If the nginx thing doesn't work out for the first page type, there is an alternative.
We can use the "asset host" trick to give us URLs which at least in part look similar to the product URLs on the main site; eg.
- use: http://assets.example.com/products/synergy/doc (or whatever other subdomain may seem more appropriate than "assets")
- matching: https://wincent.dev/products/synergy/
So will do some more investigation and see which option we have to pursue.
-
Greg Hurrell
Ah, silly me. I just remembered that the way page caching works under nginx, I already know the answer to my doubts above.
For example, we have a bunch of static cache files like:
/twitter.html
/twitter.atom
/twitter/1.html
So, yes, in the absence of an
/twitter/index.html
file, requests for/twitter
still wind up at the Railstwitter#index
action whenever the cache files are cleared.The only question remaining, then, for this type of static file, is how do we preserve such files across deployments (following the Capistrano pattern, everything in
public
is blown away (including all the cache files) except for stuff underpublic/system
which is actually a symlink elsewhere.The question about what to do about the other type of static file (the one we want to render embedded in the Rails-provided layout) still remains. But I have to pop out now, so no time to think it through.
-
Greg Hurrell
Thinking about it, switching to an "assets" subdomain would have a pretty big downside: would lose the ability to enter "relative" URLs in links (that is,
/products/foo/baz
rather thanhttp://assets.example.com/products/foo/baz
). -
Greg Hurrell
One idea to make the deployments easier would be to have a parallel hierarchy which nginx checks first. This would slow it down, but the thing is so fast that it might not even be measurable.
ie. given Rails app at
/rails/current
and static files at/rails/static
, nginx would do something like:# check static area first if (-f $static_root/$request_filename) { break; } # serve static content without hitting Rails location ~ ^/(images|javascripts|stylesheets)/ { expires 10y; } if (-f $request_filename) { break; } # show maintenance page (added by deploy script) if present if (-f $document_root/system/maintenance.html) { rewrite ^(.*)$ /system/maintenance.html last; break; } # cached pages: only for GET requests set $cache_extension ''; if ($request_method = GET) { set $cache_extension '.html'; } # the above is a hack because nginx doesn't allow nested or ANDed ifs if (-f $request_filename$cache_extension) { rewrite (.*) $1.html break; } # everything else goes to Unicorn if (!-f $request_filename) { proxy_pass http://unicorn; break; }
The above is based on quite an old config, and I suspect it could be simplified at least in part by using the
try_files
directive. -
Greg Hurrell
Ok, have a first cut at this working now:
location / { set $cache_extension ''; if ($request_method = GET) { set $cache_extension '.html'; } if (-f $request_filename) { break; } if (-f $request_filename$cache_extension) { rewrite (.*) $1.html break; } if (-f $static_root$uri) { root $static_root; break; } if (-f $static_root$uri$cache_extension) { root $static_root; rewrite (.*) $1.html break; } if (!-f $request_filename) { proxy_pass http://unicorn; break; } }
My initial stab at this didn't work because I tried to incorporate
try_files
and that seems to interact horribly withif
blocks (see "If Is Evil" on the nginx wiki). So it seems there is no choice but to have this string ofif
blocks testing one thing after another.Note that this will give us access to, for example,
/products/wikitext/rdoc/index.html
but not/products/wikitext/rdoc
(with automatic fallback toindex.html
). I am not sure whether this can be done safely:- test for directory at
$static_root$uri
-
if $uri is just
/
, then test succeeds (not confirmed whether trailing slash matters yet) -
if we then set
root
, we rule out the possibility of a match happening in the real root (although, not sure I'll ever want anindex.html
there anyway)
Perhaps I could just add another test:
if (-f $static_root$uri/index.html) { root $static_root; rewrite $static_root$uri/index.html break; }
Again, want to find out what happens with trailing slashes here.
And looking at it, can probably drop the
$cache_extension
block when looking under$static_root
. - test for directory at
-
Greg Hurrell
Status changed:
- From: new
- To: closed
Add a comment
Comments are now closed for this issue.