• Websites
  • 2026

Arhont Detailing: WordPress to Next.js website rebuild

Every old link still works and new photos go live on their own.

The Arhont Detailing website

At a glance

Client
Auto detailing center in Palilula, Belgrade: wash, interior, polish and ceramic coating.
Role
Nem, for his own business: the whole site, the accounts, the dashboards and testing on production.
Services
  • Website rebuild
  • WordPress migration
  • Local SEO
  • Lead forms and email
  • Self-updating gallery
  • Accessibility
Timeline
2026

Results

  • Old links work

    all of them, the two closed shops and the neighborhood pages included

    Source: The redirect map, 356 rules; 332 of 332 explicit rules checked live, 2026-07-21

  • Speed 90/100

    on Google's mobile test, with 100 for accessibility and search

    Source: PageSpeed Insights on the live site, mobile, 2026-07-30; field Core Web Vitals passed

  • Accessibility

    audit passed on every page, desktop and phone, before anything ships

    Source: Playwright and axe-core, the site's own check over 18 pages, 0 issues, 2026-11-06

  • Photos go live

    on their own: an uploaded photo appears in the gallery, no developer

    Source: How the gallery is built: the site lists its own image bucket

The problem

Arhont Detailing is my own detailing center in Palilula, Belgrade: wash, interior work, polishing and ceramic coating, cars only. Its WordPress site had grown the way local-service sites grow. Rank Math and FluentForms, a WooCommerce shop on the main domain and a second shop on a Shopify subdomain, both closed about a year and a half earlier but still present in the site's addresses. To appear in each part of the city it carried 235 service-by-neighborhood pages, near copies of one another, and around 600 addresses carried review markup. Dropping those addresses would have cut whatever standing they had in search; keeping the doorway pages would have kept the problem. What the old site cost the business in speed or missed inquiries was never measured, and I do not claim it here.

What I did

I replaced the WordPress site with a hand-built one and moved the domain to it in July 2026. There is no CMS. The text lives in plain files, a push publishes it and gallery photos are discovered from the image bucket, so a new set appears without anyone touching the code.

The new structure is 58 pages in Serbian: 16 static pages, 5 category hubs, 25 services, 5 packages, one fleet page and 6 blog posts. The 235 neighborhood pages redirect into the services they described, and one locations page covers the area. In all, 356 redirect rules send every old WordPress, doorway and shop address to a page that exists. A scripted sweep on launch day confirmed 332 of 332 explicit rules on the live site. Four indexed pages the map had missed still answered 404 that morning and were fixed the same day.

Two forms share one path: a contact form and a reservation request with a service cart, upsell swaps and photo upload straight to the bucket. There is no self-service scheduling; the shop confirms each request. A submission is validated in Serbian, screened by a honeypot, a bot check, a rate limit and an origin check, then sent through Postmark to the shop with a confirmation to the customer. If the shop's email fails, the visitor sees an error rather than a false thank-you. Every inquiry is also appended to a Google Sheet after the email; a sheet failure is logged and never blocks the inquiry.

Structured data is emitted per page type. GA4, Google Ads, the Meta Pixel and Microsoft Clarity load through Tag Manager only after cookie consent. The browser security policy allows seven outside hosts and nothing else; it was derived by driving a browser through every page with consent accepted and run in report-only mode before it was enforced. An accessibility check over 18 pages at desktop and phone widths, with two extra checks on the forms, runs before every change ships.

The hard part

A form that failed only on its second visit. The bot check rendered the first time the form appeared and never again, so a visitor who reached the reservation page by clicking through the site sent an empty token and was refused on every try, while every build, lint and accessibility check stayed green. I found it in the hosting logs: a run of responses 75 bytes long, answered in about 10 milliseconds, which is what a rejection looks like from outside. Three attempts came from outside the shop between October 22 and November 5, and one of them was probably a lost customer. The fix was one explicit render each time the form is shown and one automatic retry. The lesson stuck. A green check measures what it measures; the logs are where the customers are.

What changed

The shop publishes by pushing text or uploading photos. There is no admin panel to patch and no plugin to update. Every old address, the two dead shops and the 235 doorway pages included, lands on a page that exists. Reservations arrive with the car's photos attached and a row in the inquiry sheet, and the customer gets a confirmation. On July 30, 2026, Google's mobile speed test read 90 for performance and 100 for accessibility, best practices and search, and a full crawl of the live site found zero errors. Search traffic and bookings after the move are the business's own figures and are not published here.

← All work

Systems built to last. Start with a conversation.

No shortcuts, no fluff. Tell us what you're running and where it hurts.

Start a project