Большинство web-приложений и web-фреймворков используют архитектуру, не позволяющую разделить ui и backend разработку. Тем самым нет возможности разделить команду на узкоспециализированных frontend и backend разработчиков. Вне зависимости от предпочтений разработчика ему приходится иметь понимание как о слое представления, так и о слое логики. Если ui-разработчик знает только о том, как запустить сервер, и о модели данных — это огромная удача. В плохих случаях ui-разработчику необходимо провести полную сборку проекта чтобы увидеть изменения строчки в javascript файле, или знать о языке jsp файлов чтобы поменять стиль элемента. Формирование и передача на сервер обработанных html файлов так-же пагубно влияет на производительность сервера и сети. 

В наше время современных браузеров с поддержкой HTML5, WebSocket и Full Ajax приложений больше нет необходимости забивать backend-сервера чем-то отличным от бизнес логики. Вся ui-разработка может вестись на nginx сервере с заглушками api сервисов. А фреймворки для авто-генерации документации помогут и ui, и backend разработчикам снизить затраты на коммуникацию. Передача одних лишь json данных также существенно снизит нагрузку на сервера. Ведь сжатый javascript код ui-клиента можно держать в кеше приложения.
Но если современные бразуеры с легкостью справятся с возросшей ответственностью, то поисковым системам нужна помощь.
Для правильной индексации приложения на angularjs нам понадобятся следующие вещи:

Html5 mode превращает angularjs routes из вида
я оставляю
Теперь необходимо чтобы наш nginx сервер отправлял запросы с
Строка
Это решение уже является рабочим (а также совместимым с старым hashbang форматом ссылок) и если ваше приложение не должно индексироваться поисковыми системами то можно закончить.
Теперь поможем поисковику обработать наше приложение правильно. Для этого мы подготовим подсказки для бота и сгенерируем snapshot страниц. Для начала расскажем ему о том, какие страницы нужно индексировать с помощью
Отлично, поисковик будет запрашивать ссылки нашего сайта и получать контент
Это даст боту возможность сделать следующий шаг. Увидев
Вот и все, теперь все запросы от бота будут отправлены к api backend серверу.
Для формирования снапшота я использую thymeleaf. Т.к. и thymeleaf, и angularjs используют html5 атрибуты можно использовать единый файл шаблона, однако я предпочитаю не смешивать их.
Строчка из html view выглядела бы примерно так:

В наше время современных браузеров с поддержкой HTML5, WebSocket и Full Ajax приложений больше нет необходимости забивать backend-сервера чем-то отличным от бизнес логики. Вся ui-разработка может вестись на nginx сервере с заглушками api сервисов. А фреймворки для авто-генерации документации помогут и ui, и backend разработчикам снизить затраты на коммуникацию. Передача одних лишь json данных также существенно снизит нагрузку на сервера. Ведь сжатый javascript код ui-клиента можно держать в кеше приложения.
Но если современные бразуеры с легкостью справятся с возросшей ответственностью, то поисковым системам нужна помощь.
Для правильной индексации приложения на angularjs нам понадобятся следующие вещи:
- sitemap.xml
- html5Mode
- nginx
- old fashion backend server

HTML5 mode
Html5 mode превращает angularjs routes из вида
example.com/#!/home в вид example.com/home (все href ссылки должны также указывать на url без hashbang). Чтобы включить html5mode нужно выполнить:$locationProvider.html5Mode(true);
$locationProvider.hashPrefix('!');
я оставляю
! для совместимости с браузерами, не поддерживающими javascriptТеперь необходимо чтобы наш nginx сервер отправлял запросы с
example.com/home на главный index.html файл приложения. Для этого укажем в конфиге следующую директиву:location / {
        expires -1;
        add_header Pragma "no-cache";
        add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
        root /var/web;
        try_files $uri $uri/ /index.html =404;
    }
Строка
try_files $uri $uri/ /index.html =404; означает что теперь все несуществующие url будут переадресованы на index.html файл, сохранив при этом url в адресной строке браузера. Это решение уже является рабочим (а также совместимым с старым hashbang форматом ссылок) и если ваше приложение не должно индексироваться поисковыми системами то можно закончить.
SEO
Теперь поможем поисковику обработать наше приложение правильно. Для этого мы подготовим подсказки для бота и сгенерируем snapshot страниц. Для начала расскажем ему о том, какие страницы нужно индексировать с помощью
sitemap.xml файла. Простейший вариант файла состоит из ссылок на страницы и даты их последнего обновления (более подробный формат есть на сайте www.sitemaps.org):<urlset xmlns='http://www.sitemaps.org/schemas/sitemap/0.9'>
  <url>
    <loc>http://senior-java-developer.com/java/basics</loc>
    <lastmod>2013-07-12</lastmod>
  </url>
  <url>
    <loc>http://senior-java-developer.com/</loc>
    <lastmod>2013-07-12</lastmod>
  </url>
</urlset>
Отлично, поисковик будет запрашивать ссылки нашего сайта и получать контент
index.html т.к. никакой обработки javascript в поисковые боты не встроено. Расскажем боту что за технической страницей `index.html` спрятан реальный контент. Для этого в заголовок страницы добавим:<meta name="fragment" content="!" />
Это даст боту возможность сделать следующий шаг. Увидев
fragmet=! бот запросит страницу еще раз, но добавит в конец url параметр ?_escaped_fragment_=. Подскажем nginx что запросы с данным параметром нужно отправлять в другое место:if ($args ~ "_escaped_fragment_=(.*)") {
	rewrite ^ /snapshot${uri};
}		
location /snapshot {
    proxy_pass http://api;
    proxy_connect_timeout  60s;
}
Вот и все, теперь все запросы от бота будут отправлены к api backend серверу.
| Real url | Bot url | Backend url | 
| example.com | example.com/?_escaped_fragment_= | localhost:8080/snapshot/ | 
| example.com/home | example.com/home?_escaped_fragment_= | localhost:8080/snapshot/home | 
Для формирования снапшота я использую thymeleaf. Т.к. и thymeleaf, и angularjs используют html5 атрибуты можно использовать единый файл шаблона, однако я предпочитаю не смешивать их.
Строчка из html view выглядела бы примерно так:
<div ng-bind="text" th:text="${text}"></div>

