In brief: Twenty years in business, two studios, 24 disciplines, 18 instructors and more than four thousand clients. And a website search engines never saw: the old Bitrix build was infected and, on top of that, returned a noindex directive in every response. However much content sat there, for Google and Yandex the centre simply did not exist. We rebuilt the project from scratch on Django, added our own booking system and opened 116 pages for indexing.
Client: Praktika yoga centre, Cheboksary · Category: website + SEO
What matters most in this project?
- From 0 indexed pages to 116. The whole site sat under noindex; for search it did not exist.
- The infected Bitrix was replaced with clean Django, without a single line of inherited code.
- Our own booking system with an admin calendar and hourly schedule sync from Yandex Calendar.
- 1132 FAQ questions marked up across 110 pages: that much living text the centre had gathered over twenty years.
- 26 KB of HTML, a 0.4-second response, gzip and HTTPS.
- Every old URL preserved, including the .php ones, so links from old posts still land.
What is the project built with?
- Backend:
- Python, Django 5.2 with server-side rendering, Phusion Passenger.
- Database:
- MySQL 8, migrated from an inherited 5.7. On shared hosting the standard driver will not build, so pure PyMySQL is used.
- Infrastructure:
- nginx, Apache, Passenger, HTTPS via Let's Encrypt, gzip, forced HTTPS redirect.
- Booking:
- a custom Django application, with the admin panel serving as the staff interface.
- Integrations:
- schedule import from Yandex Calendar over iCal, Yandex Metrica, cron.
- SEO and GEO:
- JSON-LD @graph, sitemap.xml, robots.txt, llms.txt and llms-full.txt.
Server-side rendering was not a fashion choice. The HTML arrives fully assembled, so both search crawlers and AI crawlers, which barely execute JavaScript, see all the content and markup straight away.
How did it start?
The centre came with a complaint you hear often: “we have a website and it brings no clients”. Usually that means weak copy or slow loading. Here the cause was different and considerably worse.
First, the engine was infected. The old Bitrix build had gone years without updates and was hosting malicious code. Second, the entire site returned a noindex, nofollow directive in its header. It worked perfectly well, showed the schedule and the prices, and yet for search engines it did not exist.
Treating that makes no sense: the same holes bring back the same problem. The project was rebuilt entirely, the content moved into a single registry, and the old site was retired without any downtime for the live domain.
What was built?
🌐 What is on the site?
- 116 pages: home, a catalogue of 24 disciplines, 18 personal instructor pages, services, prices, contacts, blog
- A three-week schedule with day-by-day scrolling and a mobile layout
- A blog of 50 yoga articles targeting search and informational queries
- A “Coming up” block on the home page, live, from the same source as the schedule
- A single content registry: pages, disciplines, instructors, copy, keywords and meta are edited in one place, and duplicates are impossible by design
🗓 How does the booking system work?
- The class calendar as the main admin screen, a class created with one click
- Studios with capacity, instructors, discipline cards, seat limits enforced without race conditions
- Schedule import from Yandex Calendar for both locations, automatically every hour
- A clear split between “the calendar failed to load” and “there are no classes”, so people see a correct status rather than an alarming error
🔍 What was done for technical SEO and markup?
- Unique titles of 30 to 60 characters and descriptions of 120 to 160 across all 116 pages, zero duplicates
- Canonical tags, correct indexing, Open Graph and Twitter Card on every page
- JSON-LD @graph: Organization, both locations as LocalBusiness with addresses and hours, WebSite, WebPage, Service for disciplines, Person for instructors, FAQPage, BreadcrumbList, Article
- Semantic breadcrumbs on 115 pages, tied into the schema
- FAQ blocks: 1132 questions across 110 pages, with markup built from the same text people read
- Sitemap, robots.txt, llms.txt and llms-full.txt as a content map for AI search
🖼 How were social and accessibility handled?
- 116 social cards at 1200×630: a photo plus the brand strip with the logo, generated by script
- Labels on form fields, accessible names for icon links and buttons, correct markup for decorative images
🚚 How did the migration and launch go?
- The infected Bitrix and its databases were removed and the old site retired with no domain downtime
- Every previous URL preserved, including the .php ones
- The site was taken through seven stages: indexing, meta, markup, breadcrumbs, page schema, accessibility, content
- The live site was checked across all 116 pages until two clean passes in a row
- Noindex was lifted, the site opened to search engines and the sitemap prepared for Webmaster and Search Console
What is the result?
| Metric | Before | After |
|---|---|---|
| Pages open to indexing | 0, whole site under noindex | 116 |
| Engine | infected Bitrix | Django 5.2, clean code |
| Unique titles and descriptions | none | 116 of 116, 0 duplicates |
| Schema.org markup | absent | 8+ entity types, a connected @graph |
| FAQ with markup | none | 1132 questions across 110 pages |
| Social cards | placeholder logo | 116 cards at 1200×630 |
| Booking system | none | admin calendar and hourly sync |
| Technical audit | — | 0 critical findings across 116 pages |

