The new brains of Raise Your Flag
In my previous article I talked about the new face of Raise Your Flag. I mostly covered the new redesign as well as performance improvement when using React and CDN with a Meteor application.
In this article I will talk about how we set up our Nginx server for optimal SSL, redirects and having a Ghost blog from a different server point to `/blog`. Also I will touch on setting up SEO with React and Meteor to get the content to show up nicely when you share it on social media.

SEO Optimization in React
Our platform has a lot of content we want to be crawled. For example, we want our career paths to be crawl-able so that we can improve our organic search results when someone is looking for “How to become a custom bike builder” or “Careers that don’t need a degree”.

Usually, Meteor — like many web-app platforms — doesn’t have great SEO since everything is rendered via JavaScript so when you view the source of a page, you get something like`<body></body>` and that’s not good for a Google crawler. I am using FlowRouter SSR and Kadira’s DocHead Package and it’s dead simple to setup. All you gotta do is make sure all your SEO declarations go inside `componentWillMount()` so that when the crawler hits your page, it has those loaded in.
componentWillMount() {// Get the header image to display in the facebook/twitter shares
var image = Meteor.absoluteUrl() + this.state.backgroundPic.url({store: ‘smallCareerImages’});
DocHead.setTitle("put your title here");
DocHead.addMeta({name: “description”, content: “description here”});DocHead.addMeta({property: “og:url”, content: get your url});DocHead.addMeta({property: “og:image”, content: image});
DocHead.addMeta({property: “twitter:image”, content: image});},
This way you get stuff like this:


Further testing will be using Google’s structured data patterns to improve results even more.
Setting up Nginx for SSL and Ghost as a subfolder
Raise Your Flag is now hosted on DigitalOcean and so far it’s been great comparing to Modulus. I get a lot more freedom with my server setup, yet it’s really easy to get up and running with a Meteor application using MeteorUP.
There are tons of resources out there about how to set up an initial install on DO but I had some trouble looking at specific setups for production configurations. Here are some snippets of code that got us up and running.
First we had to make sure we get redirects happening from non-www to www. This is how I configured Nginx
server {
server_name raiseyourflag.com;
return 301 $scheme://www.raiseyourflag.com$request_uri;
}Now we do the same for SSL
server {
listen 443 spdy;ssl_certificate /path/to/crt;
ssl_certificate_key /etc/nginx/ssl/moxles.com.key;
server_name raiseyourflag.com;
return 301 https://www.raiseyourflag.com$request_uri;
}
And now for the actual setup of Meteor
# HTTP
server {
listen 80 default_server; # if this is not a default server, remove “default_server”
listen [::]:80 default_server ipv6only=on;
server_name www.raiseyourflag.com;
# redirect non-SSL to SSL
location / {
rewrite ^ https://$server_name$request_uri? permanent;
}
}
# HTTPS server
server {
listen 443 ssl spdy; # we enable SPDY here
server_name www.raiseyourflag.com;
ssl_certificate /etc/nginx/ssl/_main_ssl.crt;
ssl_certificate_key /etc/nginx/ssl/moxles.com.key;
location / {
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # allow websockets
proxy_set_header Connection $connection_upgrade;
add_header Access-Control-Allow-Origin *;
proxy_set_header X-Forwarded-For $remote_addr; # preserve client IP
proxy_set_header X-Forward-Proto https;
# this setting allows the browser to cache the application in a way compatible with Meteor
# on every applicaiton update the name of CSS and JS file is different, so they can be cache infinitely (here: 30 days)
# the root path (/) MUST NOT be cached
if ($uri != ‘/’) {
expires 30d;
}
}
}
Then if you’re using MUP, in your `mup.json` file you specify the port to be the same as nginx and you should be good to go with SSL and redirects.
SSL setup

The following configuration resulted in a pretty decent SSL score. If I have time, I will compare it with Modulus and Galaxy later but nginx on DigitalOcean gave me full control to play around with it and I got it from a capped C to an A with the following inserted after the reference to my SSL keys:
# performance enhancement for SSL
ssl_stapling on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
# safety enhancement to SSL: make sure we actually use a safe cipher
ssl_prefer_server_ciphers on;
ssl_stapling_verify on;
ssl_protocols TLSv1.2;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
# config to enable HSTS(HTTP Strict Transport Security) https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security
# to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping
add_header Strict-Transport-Security “max-age=31536000; includeSubdomains”;
For the `ssl_dhparam /etc/ssl/certs/dhparam.pem;` piece, all you have to do is run `openssl dhparam -out dhparam.pem 4096` on your server inside `/etc/ssl/certs`.
Ghost in a subdirectory
Like mentioned before, we strongly rely on organic discovery so it was important for us to check off all SEO’s best practices, one of them suggesting that everything kept under one domain (www) is better than splitting off into multiple domains like `blog.yourawesomesite.com`, etc. So here’s our blog if you want to check it out.
So since we were already hosting a Ghost blog on DigitalOcean in a separate droplet, it was really easy to set it up under `/blog`. Following this article will get you pretty much there, all you have to do is make sure you point the location of Ghost in your Meteor nginx setup to its droplet’s IP address like so:
# pass to Ghost
location ^~ /blog {
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_set_header X-NginX-Proxy true;
proxy_pass https://yourghostdropletip;
proxy_redirect off;
}
And you’re set.
If there is anything I missed or if you have any questions or suggestions, feel free to hit me up on twitter.
Original post lives here.
[By: Sergiy Dybskiy — CTO, Raise Your Flag]