Search code examples
javascripthtmlbackbone.jspushstate

History PushState API does not support long fragments in the url


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!


Solution

  • 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.