skip to Main Content

Currently, my application runs on Backbone.js.

The application works fine with the # fragments but it is not crawlable by Google bots because of the # in the URL.

So, I decided to remove the # to make it more SEO friendly. I enabled the History pushState API and added code to prevent the default action. Here is the code snippet that I have while I initialize my router instance.

Backbone.history.start({pushState: true});

$(document).on("click", "a", function(e)
        {

            var href = $(e.currentTarget).attr('href');

            var res = Backbone.history.navigate(href,true);
            //if we have an internal route don't call the server
            if(res)
                e.preventDefault();

        });

Also, I modified my Apache config to enable mod_rewrite in order to process stateless requests like refreshing the page or opening the page into a new browser window. Here is my Apache config snippet:

 <IfModule mod_rewrite.c>
            RewriteEngine on
            RewriteRule ^/([a-zA-Z0-9]+)[/]?$ /index.html?pathtyped=$1 [QSA,L]
 </IfModule>

The problem that I’m facing is that the Application works just fine with short url fragments but they do not work with large fragments. Meaning the follwoing urls work:

http:server_name/#view1 -> http:server_name/view1

http:server_name/#view2 -> http:server_name/view2

http:server_name/#view3 -> http:server_name/view3

But the url with long fragments do not work. (The following don’t work):

http:server_name/#view1/option1 -> http:server_name/view1/option1

http:server_name/#view2/option1/option2 -> http:server_name/view2/option1/option2

Any suggestions to solve the problem are highly appreciated. Thanks you!

3

Answers


  1. Hm…maybe this is helpful (based on phantomJS) : http://traviswimer.com/blog/backbone-seo

    Login or Signup to reply.
  2. I did a bit of Googling and eventually stumbled onto this gist. I tested it out with a minimal Backbone.js app and it seems to support infinitely long stateless entrance into the app:

    <ifModule mod_rewrite.c>
        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteCond %{REQUEST_URI} !index
        RewriteRule .* index.html [L,QSA]
    </ifModule>
    

    I had to do a bit of reading to fully understand the RewriteCond statements, specifically what !-f, -d, and !index are doing. Everything but the !index makes sense.

    Give it a shot and let me know if it works for you.

    Edit: I actually found the above to only work on my Homebrew-installed version of Apache2 on my Mac and not on Debian. Some more Googling yielded this alternative form:

    <IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteBase /
        RewriteRule ^index.html$ - [L]
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule . /index.html [L]
    </IfModule>
    

    I also put together a fully-worked example of a modern Backbone.js app with pushState and stateless entry here.

    Login or Signup to reply.
  3. I’m guessing that the application works in a single URL component scenario like http://server_name/viewX because the Backbone.Router you have defined matches the URL and successfully and makes the callback defined to render viewX (without the # for modern browsers and web crawlers, and with it for older browsers). So, when it doesn’t work, I would imagine that the defined route patterns are not matching the nested URL components as expected, and the rendering callback is not fired.

    You might have a look at your Backbone.Router.routes hash and be sure your patterns are matching the expected URL components at each depth. Have a look at the Backbone documentation on routes, particularly the example on optional and nested URL components, and check that your matching pattern is correct. For instance, a routes hash containing:

    routes: {
        view(/:option1)(/:option2) : renderView
    }
    

    should match URLs starting with view (or #view) and optionally containing one or two sub parameters, like:

    • view/1
    • view/1/a
    • #view/1
    • #view/1/a

    and in the above examples, option1 would be assigned 1, and option2 would be assigned a, and would be passed to the callback.

    With a route hash like:

    routes: {
        view/*option1 : renderView
    }
    

    everything beyond the first / would match and be assigned to option1, like:

    • view/1/a
    • #view/1/a

    In both cases above, option1 would be assigned the string 1/a, and would be passed to the callback.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search