Implementing a SEO friendly solution for a SPA application typically requires server side rendering or possible pre-building static HTML files. This ensures the meta tags are populated when crawled by by search engines like Google, Bing, and Yahoo and when shared with social media sites like Facebook, LinkedIn, and Google+.
Note: There are some trends of search engines being more forgiving of SPA applications, but this is not true of all of them. Anecdotally, I have found Google and Facebook very tolerant, but LinkedIn to be terribly intolerant.
- Angular Universal + angular-cli - Ultimately this marriage would be ideal, though it鈥檚 not clear when this will happen.
- Host using a different technology - For example, for a .NET application, we could use an index.aspx and server side data-binding to add the meta tags based on the initial server request. The one drawback is this data would need to come from somewhere and match the routing table of our client side code.
- Use Existing Solution - This would be ideal as we don't have to rely on 3rd parties. Since angular-cli is already using Express and Handlebars (what?), we could use server side rendering to generate the index.html based on some pre-determined data. This data can come from a configuration file which mimics the client side routing (probably not ideal), or reads existing Angular routing classes that have been decorated with SEO properties. While we could create a standard for this, perhaps some kind of hook that would allow an angular-cli middleware plug-in would be ideal.
Sample SEO Tags
title tag: Example:
meta tag names: Example:
I'm not sure what you mean by a hook that would allow an angular-cli middleware plug-in or how you would conditionally select between the pre-determined meta tags. This doesn't sound very much plausible nor different than setting the tags programmatically from a component or service, similarly from pre-determined router data. Could you try to explain how it should work a little bit more, in-detail?
This looks pretty ok for now though: https://github.com/vinaygopinath/ng2-meta
Not sure about LinkedIn, no issues with Google/Facebook/Twitter.
Do you have any data to show as a result of testings, perhaps?
It's very widely documented in many other places that SEO does not always work in client side Javascript applications (see References below). It is easy enough to tweak meta tags client-side, but many search engines social media sites will never see the rendered markup. Even the link you provided had this to say:
_While Google executes Javascript and extracts meta tags set by ng2-meta, many bots (like Facebook and Twitter) do not execute Javascript. Consider defining fallback meta tags in your HTML for such bots. The fallback content is overridden by ng2-meta in Javascript-enabled environments._
The _hook_
As far as a hook is concerned, angular-cli is already using this approach with the --proxy-config parameter which used in conjunction with a proxy.conf.json file. This is relayed to webpack which then uses the http-proxy-middleware.
angular-cli - proxy-to-backend
https://github.com/angular/angular-cli#proxy-to-backend
For SEO, we could take a similar approach. For example, we could have an --seo parameter which points to a configuration file (or routing configuration) which is then used by a webpack plugin. For example, the popular html-webpack-plugin can serve an index.html file based on some code and templating logic.
html-webpack-plugin
https://www.npmjs.com/package/html-webpack-plugin
References
SEO for JavaScript applications, 2016 edition
http://mono.software/2016/02/18/SEO-for-javascript-applications/
How to make your JavaScript apps SEO-friendly
http://odino.org/how-to-make-your-javascript-apps-seo-friendly/
"Precomposing" a SPA may become the Holy Grail to SEO
http://www.analog-ni.co/precomposing-a-spa-may-become-the-holy-grail-to-seo
The only serving the CLI does is for development purposes. The output of the CLI needs to be hosted and served by a third party.
Hosting requires a 3rd party, but the code for serving is generated by angular-cli. This is output into the dist directory and is generated by angular-cli, which in turn uses Webpack and several dozen Webpack plugins.
I understand this is still in beta, but SEO is a common requirement on any public facing site. While I agree this is not a high priority issue, it should be considered at some point in the future.
The output of the CLI is a handful of static files that must then be served by the user's preferred server or hosting platform.
Exactly! Those static files include the server files for serving via node. This is where code for handling SEO would need to happen, if not at a higher level.
BTW: ember-cli (on which angular-cli is based) is looking into using fastboot as a solution for SEO.
https://github.com/ember-cli/ember-cli/issues/6350
I think you are mistaken. No server code is generated.
This discussion is clearly not going anywhere. I will look into another solution.
As @clydin said, no server files are generated. The dist/
dir generated via ng build
contains only static files, that can be served in whatever setup one has. We do not recommend that ng serve
is used to serve files in production, that's a very bad idea. One should use a static server instead.
It is true that SEO concerns are important and that the CLI should facilitate it if possible, but bear in mind that dynamically composing an index.html
file with extra meta tags (or whatever else is needed) is something that happens on the server, before serving a request.
That is why it is hard to have a simple solution to the SEO problem from the CLI side: it requires server actions and the CLI doesn't do anything related to your server.
Thus the solution isn't as simple as changing something on the generated app... in fact, the built app is almost irrelevant for SEO optimization, since the server does all the work.
I'd like to keep this issue open though because it's a very relevant discussion.
@Filipe Silva - Bravo and thanks. ;) I'm certainly not advocating a specific solution here, but this is clearly a common problem. Given SEO is synonymous with what Google is and does and Angular is managed and promoted by Google, I would think this should warrant some minimal consideration. ;)
There's also some alternate solutions using protractor to crawl your site page by page and outputting the rendered html. @jeffbcross could discuss more about it.
Personally I really like that one because it makes very little assumptions about the server, and still allows you to serve a fully static site.
Quick solution: http://prerender.io
prerender.io can't be used if you are using ES6, since it relies on PhantomJS, which doesn't have support for ES6. Giving lots of dependencies on angular2 project are build with ES6 and since you can't use Babel to translate it to e.g. ES5 (no way or overriding webpack config in angular-cli), you are left with no choice really.
Is there any timeline on when universal is coming to angular-cli? Looks pointless at this stage, I don't find a way to index the site without sever side rendering.
Do you have any guidance or good example of using protractor to crawl our site pages?
I have been trying to integrate universal for a while now, I tried to use other seed project, but no success unfortunately
Amazing pre-render alternative https://www.roast.io/
Runs your whole app on a headless chrome and caches the response onto a CDN!
Seams great, I have just checked it out.
Only thing is roast.io ribbon at top corner of your site, when you are just starting and not yet getting traction on your site, so you have to be subscribed immediately. Shame, I might wait for angular-cli to finally add support for server side rendering or wait for prerender.io to switch to Chrome as well.
Thanks anyways!
You don't need to worry about ES6 with prerender because generally you're building and serving an application transpiled to ES5. We're using prerender ourselves until support lands in CLI: https://github.com/angular/angular-cli/pull/5547
Chrome Headless should be much better than Phantom though for prerendering :)
Unfortunately, it doesn't work for us. Check my comment above, application is not being transpiled to ES5.
Since we're talking about prerender.io and friends, I'd like to also suggest Usus:
https://medium.com/@gajus/pre-rendering-spa-for-seo-and-improved-perceived-page-loading-speed-47075aa16d24
https://github.com/gajus/usus
I had a go at it a couple of weeks ago and it was nice.
Here's a quick way to prerender the main page.
npm install -D http-server usus
"prerender": "ng build --prod && concurrently \"http-server dist\" \"usus render --url http://localhost:8080 --inlineStyles true > dist/prerender-index.html\" --kill-others --success first && mv dist/prerender-index.html dist/index.html"
The script does a bunch of things:
--prod
http-server
usus
over the built app and outputs the prerendered page into dist/prerender-index.html
usus
is doneindex.html
with prerender-index.html
.You can check out the prerendered result by running http-server dist/
again (maybe put it in another npm script like "basic-serve": "http-server dist/"
).
This isn't a super complete solution, and it's only prerendering the main page, but it's something you can do right now.
Notes:
The npm script assumes that >
and mv
exist in your system. Normal windows command prompt doesn't have it, but Git Bash does.
If you get the error below, do npm i [email protected]
const map = async (values, mapper, configuration) => {
^
SyntaxError: Unexpected token (
can be closed since the CLI supports Universal?
https://github.com/angular/angular-cli/wiki/stories-universal-rendering
if I run
ng serve --app 1
I am getting
ng serve for platform server applications is coming soon!
@Toxicable any idea when we will get that support?
Closing this in as Universal builds are supported.
@chintan13 serve is not coming in the next release, but you can already do it yourself. There are tutorials online for it.
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
_This action has been performed automatically by a bot._
Most helpful comment
It's very widely documented in many other places that SEO does not always work in client side Javascript applications (see References below). It is easy enough to tweak meta tags client-side, but many search engines social media sites will never see the rendered markup. Even the link you provided had this to say:
_While Google executes Javascript and extracts meta tags set by ng2-meta, many bots (like Facebook and Twitter) do not execute Javascript. Consider defining fallback meta tags in your HTML for such bots. The fallback content is overridden by ng2-meta in Javascript-enabled environments._
The _hook_
As far as a hook is concerned, angular-cli is already using this approach with the --proxy-config parameter which used in conjunction with a proxy.conf.json file. This is relayed to webpack which then uses the http-proxy-middleware.
angular-cli - proxy-to-backend
https://github.com/angular/angular-cli#proxy-to-backend
For SEO, we could take a similar approach. For example, we could have an --seo parameter which points to a configuration file (or routing configuration) which is then used by a webpack plugin. For example, the popular html-webpack-plugin can serve an index.html file based on some code and templating logic.
html-webpack-plugin
https://www.npmjs.com/package/html-webpack-plugin
References
SEO for JavaScript applications, 2016 edition
http://mono.software/2016/02/18/SEO-for-javascript-applications/
How to make your JavaScript apps SEO-friendly
http://odino.org/how-to-make-your-javascript-apps-seo-friendly/
"Precomposing" a SPA may become the Holy Grail to SEO
http://www.analog-ni.co/precomposing-a-spa-may-become-the-holy-grail-to-seo