基于Angularjs单页面应用的SEO优化

在之前的文章我曾提到基于Angularjs的单页面应用在用户体验上的种种好处。然而任何事情都不是完美的,Angular和类似的框架通过应用内做页面路由的实现给SEO(也俗称搜索引擎优化)带来了不少麻烦。

首先,我们来看看页面内路由是如何实现的。默认Angularjs生成的页面uri类型如下,

http://mydomain.com/#/app/page1

浏览器请求上面这个uri的时候,实际发送给服务器的请求地址是http://mydomain.com/, web服务器会将默认的页面响应给浏览器,比如index.htmlindex.php等。

浏览器返回的页面里面引入了Angularjs和其他应用需要的JS库。Angularjs应用开始执行后,尝试处理路由**/app/page1**。如果应用定义了该路由,将加载必要的JS库和其他html片段来完成页面的渲染。

理解了Angularjs页面内路由的原理后,我们知道了对浏览器或搜索引擎爬虫而言,单页面应用所有的页面对浏览器和搜索引擎都是一个网址,比如http://mydomain.com/。这样对爬虫抓取站内链接造成了困难,因为所有应用内的链接都被认做了同一个链接。

我们理解了uri http://mydomain.com/#/app/page1给SEO造成的麻烦,接下来就是讨论如何针对SEO来作的优化。

最理想的情况当然是搜索引擎爬虫变的更加智能,它能理解网站的框架,并且针对此种情况做出优化。但截止到目前,包括Google在内的所有爬虫都无法做到这点。那我们SEO的优化只能在应用这边来做了。

Angularjs提供了一种HTML5 mode模式可以利用HTML5 History API来实现页面内路由。打开的方法如下,

1$locationProvider.html5Mode(true);

同时在index.html页面加上如下标签,

1<base href="/">

在打开HTML5 mode后的Angularjs应用的链接看起来就是这样了,

http://mydomain.com/app/page1

新的链接模式和站内跳转通过访问网站主页请求将没有任何问题。然而直接在浏览器请求如上链接的话,Web服务器将尝试请求/app/page1,通常会得到404的页面响应。因为服务器上并没有部署页面/app/page1

这时就需要在Web应用服务器或应用里面实现URL Rewrite。将/app/page1的请求转到单页面应用html文件上。

下面是一些Web服务器或应用的参考配置,

  • Apache Rewrites

     1	<VirtualHost *:80>
     2	    ServerName my-app
     3	
     4	    DocumentRoot /path/to/app
     5	
     6	    <Directory /path/to/app>
     7	        RewriteEngine on
     8	
     9	        # Don't rewrite files or directories
    10	        RewriteCond %{REQUEST_FILENAME} -f [OR]
    11	        RewriteCond %{REQUEST_FILENAME} -d
    12	        RewriteRule ^ - [L]
    13	
    14	        # Rewrite everything else to index.html to allow html5 state links
    15	        RewriteRule ^ index.html [L]
    16	    </Directory>
    17	</VirtualHost>
    18	
  • Nginx Rewrites

     1	server {
     2	    server_name my-app;
     3	
     4	    root /path/to/app;
     5	
     6	    location / {
     7	        try_files $uri $uri/ /index.html;
     8	    }
     9	}
    10	
  • Azure IIS Rewrites

     1	<system.webServer>
     2	  <rewrite>
     3	    <rules> 
     4	      <rule name="Main Rule" stopProcessing="true">
     5	        <match url=".*" />
     6	        <conditions logicalGrouping="MatchAll">
     7	          <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />                                 
     8	          <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
     9	        </conditions>
    10	        <action type="Rewrite" url="/" />
    11	      </rule>
    12	    </rules>
    13	  </rewrite>
    14	</system.webServer>
    15	
  • Express Rewrites

     1	var express = require('express');
     2	var app = express();
     3	
     4	app.use('/js', express.static(__dirname + '/js'));
     5	app.use('/dist', express.static(__dirname + '/../dist'));
     6	app.use('/css', express.static(__dirname + '/css'));
     7	app.use('/partials', express.static(__dirname + '/partials'));
     8	
     9	app.all('/*', function(req, res, next) {
    10	    // Just send the index.html for other files to support HTML5Mode
    11	    res.sendFile('index.html', { root: __dirname });
    12	});
    13	
    14	app.listen(3006); //the port you want to use
    15	
    
  • ASP.Net C# Rewrites

    1	private const string ROOT_DOCUMENT = "/default.aspx";
    2	
    3	protected void Application_BeginRequest( Object sender, EventArgs e )
    4	{
    5	    string url = Request.Url.LocalPath;
    6	    if ( !System.IO.File.Exists( Context.Server.MapPath( url ) ) )
    7	        Context.RewritePath( ROOT_DOCUMENT );
    8	}
    9	
comments powered by Disqus