pushState with server-side rendering for SEO

Has anyone had success using Pushstate for SEO-friendly URLs while also having their application behave like this:

  • http://app.example.com/some/path accessed directly: Pre-render the content that the Ember templates would display at that route, embed it in the normal Ember application HTML, and return.
  • http://app.example.com/some/path navigated to from within Ember application: Have Ember do its normal thing using pushState, no server page request required.

The big challenge I see here is getting my web server to use the same handlebars templates as my Ember application, but I wanted to see if others had any other pitfalls to report.


The server doesn’t have to use the same templates. The server just renders the content into a <noscript> tag. All you need is a basic HTML structure so the search engines know how to weight the content. Discourse does it this way. (Discourse is the open source software running this forum.)

I’m also trying to get something like this to work. I have an ember/rails application, and I want the page to have a dynamic <title> element. I want to render the <title> in Rails, so it’s picked up by search engine crawlers. However, only the Ember part of the app knows about the correct state… I’ll post here once I come up with something.

That’s interesting. So basically, crawlers ignore the JS part of the app and use what is within the <noscript> tag, but ordinary users with JS see the app itself. So in essence, you “mirror” the contents of each page in the <noscript>?

Do you have any links I can take a look at explaining this approach (pros/cons) in more detail?

Try disabling Javascript when browsing this forum :wink:

1 Like

D’oh! Thanks, definitely helps to see it in practice.

You can read more about it in this blog post.

1 Like

One way you could solve this would be to render the page using PhantomJS before serving pages to crawlers. Updating document.title will also update the title tag contents when you retrieve the rendered HTML contents from PhantomJS.

I wrote a gem that is basically Rack middleware that does this, have been running it in production for a while now.

Wouldn’t it make more sense to serve different content based on the X-Requested-With HTTP header? In other words, serve markup with pre-render templates if the request is a vanilla HTTP request, make sure the AJAX is configured to include the header with each request, then serve just the necessary JSON if not?