drewmoore drewmoore - 5 months ago 71
AngularJS Question

Nginx conf for prerender + reverse proxy to django + serving angular in html5 mode

The mouthful of a title says it all:

We've got an Angular frontend with a Django backend providing a REST API that is exposed independently at endpoints

example.com/api/v1/*


The Angular app runs in HTML5 mode, and we want hard-links to
example.com/foo/bar
to bring users into the app at the
foo.bar
state as if it were a static page rather than an app state (where
foo
is anything but
api
).

We're running behind nginx,and our basic strategy in the conf was to define locations at
^~ /scripts
,
/images
etc. for serving static content directly, as well as a
^~ /api/*
location that gets routed to django. Below that, we have a
location ~ ^/.+$
that matches any path not matched by any of the above and "sends it to Angular" - i.e. serves our index page to it and appends the path to the base url, allowing our angular router to handle it from there.

This is our conf in full:

upstream django {
server 127.0.0.1:8000 fail_timeout=0;
}

server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}

server {
listen 443;
server_name example.com;
client_max_body_size 10M;
ssl on;
ssl_certificate /etc/ssl/thawte/example_com.crt;
ssl_certificate_key /etc/ssl/thawte/example_com.key;
ssl_verify_depth 2;
gzip on;
gzip_types text/plain text/html application/javascript application/json;
gzip_proxied any;

index index.html;

location ^~ /index.html {
gzip_static on;
root /www/dist;
}
location ^~ /images/ {
expires max;
root /www/dist;
}
location ^~ /scripts/ {
expires max;
gzip_static on;
root /www/dist;
}

location ^~ /favicon.ico {
expires max;
root /www/dist;
}

location ^~ /api {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
proxy_pass http://django;
}

//Send anything else to angular
location ~ ^/.+$ {
rewrite .* /index.html last;
}


}

This has worked perfectly for us, but we now need to set it up to work with prerender.io. We've tried doing this several ways, making modifications on the official prerender nginx example, but none have worked - crawlers are getting the same code users are rather than cached pages.

How can we get this working?

(note: this is new territory for everyone involved here, so if the best way to handle this involves making different choices a few steps back, please suggest them)

Answer

So it turns out the config posted above was working the whole time.

I realized this when it finally occurred to me to try putting https://example.com/anything through the crawler debugger (instead of https://example.com, which is all I had been testing previously), and it worked - the crawler was served the cached page as expected.

This was simply because the greedy quantifier in:

location ~ ^/.+$ {

did not match the empty string. With an additional

location = / {
    try_files $uri @prerender;
 }

, my conf is working as expected.

Hopefully the handprint on my forehead d only been putting https://example.com through the crawler debugger - which was not working.

On the upside, I'm thinking I can turn this handprint on my forehead into a nice Halloween costume next weekend....

Still not sure I've gone about this the best way, and welcome alternative suggestions.