AutoSet Niš
an auto glass website with eight pages for local search
Someone whose windshield was just cracked by a stone doesn't search for long. They type “windshield replacement Niš”, open the first site that loads quickly and call the number they see on the screen. This site was built around exactly that moment, from how the pages are divided to how long it takes to load on mobile data.
- Multi-page website
- Niš
- August 2026
- 13 min read
- HTML + PHP 8.5
- nginx
- PHPMailer over SMTP
- Consent Mode v2
- MariaDB (panel)
- Customer
- AutoSet Niš
- Industry
- Auto glass: sales, installation, replacement, repair
- Area
- Niš and the surrounding area
- Type
- Multi-page site with eight pages for local search
- Technologies
- HTML · PHP 8.5 · nginx · PHPMailer · Consent Mode v2
- Website
- autoset.rs
- Status
- Live site, with ongoing maintenance

01 / 11
What AutoSet does
AutoSet is an auto glass service and shop in Niš. It has been in business since 2005, according to its About page. The showroom and workshop are at Matejevački put 24 in Niš, the registered office is in Svrljig, and the business is narrow and clear: windshields, side and rear windows, glass for vans, trucks and buses, covering sales, installation, replacement and repair.
It is a trade where customers almost never plan their visit. A stone from the highway hits the windshield on a Wednesday morning, or a car is found broken into with a side window gone. The person picks up the phone, types the service and the city, opens the first site that loads and calls the number. Less than a minute stands between the problem and the call, and the whole site is built for that minute.
A full overview of the offer is on the services page, and each of the eight services has its own page. On the site, the company lists a five-year warranty on glass and labor, handling of insurance claims with the insurer, and on-site service by arrangement in Niš and the surrounding area. Those are their terms and they are passed on as such: the site states them, it doesn't invent them.

02 / 11
What needed solving
The task wasn't “make a company presentation”. The site had to do four specific things:
- Get found in local search. Not for the word auto glass, where all of Serbia competes, but for queries that contain both the service and the city.
- Keep the phone a tap away. Someone with a crack across half the windshield doesn't fill in a form. They call. The number has to be visible on every screen and tappable on a phone.
- Make sure form messages actually arrive. A message lost along the way is lost business, and an unprotected form turns into a trash can within a month.
- Load fast on mobile data. Most visits come from phones, often from the road, on whatever connection happens to be there.
A fifth item came much later and has nothing to do with the public site: an inventory of more than seven hundred glass items lived in the accounting software, and the shop needed it on screen, searchable, while the customer stands at the counter. There is a separate section about that near the bottom.

03 / 11
Structure: eighteen pages and clean URLs
The site has eighteen pages. Four are the core: home, services, about and contact. Eight are separate pages for individual services, plus prices, FAQ, booking and one tip for drivers. Two are legal: the privacy policy and the terms of use. No blog, no news, no section that gets filled in once and then sits forever with last year's date.
Below the heading, the home page carries a single sentence: Every day, thousands of people see the world through glass installed by AutoSet. Below it come four short badges (years of experience, warranty, on-site service, Google rating), then the Why AutoSet? block with six cards, then reviews from the Google profile, FAQ, and contact with a map and two phone lines. The menu has six items, and the services sit in a dropdown, because there are eight of them.
Technically, the pages are PHP files with a shared header and footer, but the URLs have no extension: /usluge, not /usluge.php. The server takes care of that: try_files looks for the exact file, then the PHP version, and permanently redirects old URLs ending in .html to the URL without the extension. This paid off in September 2026, when all the pages were rewritten from HTML to PHP: the URLs stayed the same, and old links still lead to the right pages.
04 / 11
Four pages for four searches
This is the part that was worth the most to AutoSet. A single services page that mentions everything ranks for nothing, because there is nothing it can be the best at. So four separate pages were built, each for one query people actually type:
- Windshield replacement Niš — a three-step process from quote to seal check, a list of makes for which windshields are sourced, and the most common questions: how long it takes, how long to wait before driving, how it works through insurance.
- Windshield repair Niš — two lists side by side, what can be repaired and what needs replacing, then a table comparing repair and replacement by price, by time and by when the car is ready to drive, then the three steps of the process with resin and a UV lamp.
- Auto glass Niš — the range by glass type and by vehicle make, with the difference between original and aftermarket glass explained in words customers understand.
- Side windows Niš — front and rear side windows, fixed quarter glass, pillar glass and glass for van sliding doors.
Each of them has its own questions and answers, and its structured data carries a service description and breadcrumbs. Two of them bring the most traffic: windshield replacement, because it is the most expensive and most urgent job, and windshield repair, because most people don't even know that a small chip can be repaired instead of replacing the whole glass. The comparison table on that page isn't marketing: it actually answers the question the visitor arrives with.
Today there are eight service pages: besides these four, rear windows, glass for trucks and vans, on-site service and replacement through insurance. All of them are in the sitemap; the legal pages are not, because they are marked noindex. In Google Search Console the domain is verified as a Domain property through a DNS record, the strongest verification there is. The site is also set up in Bing Webmaster Tools with the same sitemap.
05 / 11
The phone, the form and making sure messages actually arrive
AutoSet has four lines: one landline and three mobile. The site shows two, the landline and a mobile, in the footer of every page and in the contact block, and both are tappable on a phone. The number inside the link must not contain spaces, otherwise the call won't start on some phones, a small detail you only discover when someone tries to call from the site.
There are forms on the contact page and on the booking page: name, phone, email and message, and on the booking form also the vehicle, glass type, insurance and a photo of the damage, with a privacy consent. Behind both is a single PHP file that, after the shared protection with an invisible check, runs three separate checks before it even tries to send mail.
- Honeypot. Every form has a hidden field that a real visitor never sees or fills in. If it is filled in, the message is discarded.
- Link filter. If the message body contains a web address, link markup or a punycode domain, the message is silently discarded and the sender gets the usual confirmation. A bot that doesn't get an error doesn't try again a different way.
- Rate limit per IP address. At most three messages per hour from the same address.
The limit counts every attempt that passes field validation, including ones where sending fails, so someone who mistypes their email gets a description of the error and doesn't use up an attempt.
Mail doesn't go through the language's built-in function but through PHPMailer and a real sending account with authentication, over an encrypted connection on port 587. The reason is down to earth: a message the web server fires off by itself has no signature and no history and lands in spam, while a message sent through an account on the domain passes SPF, DKIM and DMARC checks and reaches the inbox. The last test with a real message confirmed delivery, with a server response of 250.
06 / 11
Cookies, consent and analytics
The cookie banner isn't pasted on top of the site; it comes from a shared include used by several of our sites, so it is maintained in one place. For AutoSet it was recolored to match their palette: light blue with accents and the same font as the rest of the site.
What matters is the order, not the look. Google Consent Mode v2 sets the default to denied before anything from Google loads. Measurement turns on only after a click on accept. The banner also offers Essential only, the choice is remembered and honored on the next visit, and analytics don't start until there is consent.
One detail from this shows how small things make their way into the code. The banner recoloring originally sat as a style written directly into the page body. It works in the browser, but the W3C validator reports it as an error. It was moved to a separate file that doesn't block the first render: same color, one error fewer.
07 / 11
Speed: a tracker that doesn't block the first render
On this site, the speed ceiling was neither images nor code, but Google Tag Manager: some sixty kilobytes of script the visitor doesn't use, running while they wait to see the text. In a test over a throttled mobile connection, it showed up as time during which the page didn't respond to touch.
The fix is deferred loading: the analytics script is fetched only after the visitor's first action (a scroll, click, tap or key press) or after five seconds, whichever comes first. The default consent refusal is still set before all of that, so the consent order stays intact.
Measured before and after on the mobile Lighthouse profile: performance score from 74 to 86, total blocking time from 368 to 54 milliseconds, largest contentful paint from 4.7 to 4.1 seconds, and layout shift stayed at zero. A check with Google's live tool gave a mobile score of 76 and desktop 95, with 100 for accessibility, best practices and SEO on both.
It is only fair to mention what these numbers don't show: the site has no real-user data in Google's measurement database, because it doesn't get enough traffic. The scores are simulated, a valid indicator but not a measurement across thousands of real visits. Images and fonts are cached for a year, styles and scripts for a week.
08 / 11
Code, security and mail
A full W3C validation pass over all ten pages started at twenty-seven errors and ended at zero. None of them were dramatic: spaces inside a phone link, a fourth-level heading without a third-level one above it, an incorrectly nested block, the style in the page body mentioned above. None of them breaks the site. Together they are the difference between code someone has reviewed and code someone simply left running.
At the server level: the switch to an encrypted connection is a permanent redirect, the address with www leads to the bare domain with the path preserved, and the certificate renews itself. The headers that tell the browser what is and isn't allowed (HSTS, no framing by other sites, no content-type sniffing, window isolation) are set and verified. Hidden files and backups are not served. An external check of the encrypted connection gives the top grade.
The domain's DNS records include a restriction that tells certificate authorities explicitly who may issue a certificate for this domain, with an address for reporting attempts. Mail has SPF, DKIM and DMARC, and the policy was moved from quarantine to reject: a message pretending to be from AutoSet doesn't end up in spam, it is turned away at the door. That is no small thing for a company that sends quotes and receives inquiries from its own domain.
09 / 11
A bug that only shows on a phone
In July 2026 the site went through a check in a real browser: all eleven URLs, at desktop width and on a phone. Clean console, no dead links, no broken images, redirects and certificate in order. But on four service pages, at widths between 384 and 797 pixels, the page overflowed horizontally, and the sidebar with the price list, benefits and emergency help was entirely off screen. Invisible. On the most important pages of the site, at the width most visitors use.
The cause was a single inherited line in the styles. On narrower screens the sidebar switched from a row to a column, but the wrap rule from the wider breakpoint was never reset. In a column layout the flex basis applies to height, not width, so each block wrapped into its own column, side by side, and ran off the edge of the screen.
The fix was two lines: no wrapping, and a flex basis based on content instead of full width. On top of that, a grid stretching problem on desktop was fixed on the side windows page, where a row of five process steps pushed the column beyond its width.
The lesson goes beyond this site: switching from row to column inside a media query does not reset the wrap rule from a wider breakpoint; it has to be turned off explicitly. Even more important, a bug like this doesn't show up in any automated score. Lighthouse still gives that page a high score. You only see it when someone actually opens the site on a phone and tries to find the price list.
10 / 11
The inventory panel: a separate job
The public site is one thing. In August 2026 a second part was added that has nothing to do with it: an internal panel on its own subdomain, behind a login, holding a glass inventory of 713 items and a booking calendar. It is a tool for the shop counter, not for visitors.
The company's bookkeeping runs in business software used through the browser, with the database held by the vendor. Before anything was written, we checked whether the two systems could be connected directly. The answer was no: there is no publicly documented service for automated data exchange, and some seventy pages of their help docs mention nothing of the kind. What does exist is an export of the stock to a spreadsheet. So the bridge is a file.
So the panel got a three-step spreadsheet import: upload, column mapping with automatic detection, then a preview that shows, before anything is written, how many items are new and how many have changed. Before every write, the whole inventory is backed up, so a bad import can be undone.
The hard part was the data, not the code:
- The product code isn't unique. The same code exists twice, under two different manufacturers. The matching key has to be the code together with the manufacturer, otherwise the import merges two different items into one.
- When the file has no manufacturer column, the manufacturer has to drop out of the key — on both sides, in the file and in the database. If it drops out on only one side, every item looks new and a single import doubles the entire inventory.
- 94 of the 713 items have no name, only a code. The first version of the import silently skipped them, because the name was set as required. Now a row is usable if it has either a name or a code.
Along the way, the review also found an existing bug that was quietly eating data: the table displayed prices rounded, and the edit dialog read those rounded numbers back from the table cells and wrote them to the database. Editing any field of an item permanently lost the decimals: 1234.75 became 1235. The fix was to keep the original values with each row and have the edit read those, not what is drawn on the screen.
The panel was then tested with real mouse clicks, not programmatic ones, and that difference uncovered two things. First: the bottom of the import dialog couldn't be scrolled into view — on a maximized 1366-pixel laptop screen, the Next button was 189 pixels below the edge of the screen, and nothing scrolled. The import was unusable on the most common laptop in the shop, yet a programmatic click still goes through because it bypasses the visibility check; an automated test would have said everything works. Second: the floating “Synced” badge was swallowing clicks — visually harmless, but it was catching clicks, so the Edit and Delete buttons beneath it didn't respond, with no error and no trace. Adding space below the table doesn't help here; the only cure is for the badge to stop receiving clicks.
The security review found its most serious issue in the import itself. An XLSX file is actually an archive, so a 1.84-megabyte file can unpack into 529 megabytes in memory and take down a server worker, and with it the other sites in the same pool. A row limit doesn't help, because it only applies to a tree that has already been built. The check now runs before reading, on the declared size and on the compression ratio. All of this is covered by the tests the panel ships with: 129 checks on the data and 43 over the network, including a deliberately crafted zip bomb.
11 / 11
What the client ends up with
An eighteen-page site, eight pages of which target exactly the search someone runs with a crack in their windshield. Pages indexed, with breadcrumbs visible in the results. Forms that reach the inbox, with several layers of protection in front of them. Two phone lines that dial with a single tap.
The public site deliberately has no content management system, and that is a decision, not an oversight. Eighteen pages without a database have nothing to break, no plugins that fall apart at the next update and no login for an outsider to guess. The content changes rarely (a phone number, opening hours, a service description), and those changes go through us, with a backup before each one and the code kept in a private repository.
The tool the client uses every day is a different one: the panel with the inventory and the calendar, with an import from the accounting software whenever a new export comes in. Seven hundred items can be searched in a second, prices show exact to the dinar, and a new delivery comes in through one spreadsheet instead of seven hundred manual entries.
The split is deliberate. The site works for someone who doesn't know AutoSet yet. The panel works for someone who works at AutoSet. Those are two different jobs, and they were never meant to be the same application.
On a phone
The same site, in your hand.
Most visitors come from phones, so the layout, buttons and images are arranged for the small screen first.

Questions
What clients ask.
Why doesn't the public site have a content management system?
Because it would cost more than it is worth. On a site like this, the content changes a few times a year: a phone number, opening hours, a service description. Static pages have no plugins that fall apart during updates and no login for anyone to guess. Changes go through us, with a backup before each one.
Why separate service pages instead of one page with all the services?
Because a page that talks about everything ranks for nothing. People don't type just “auto glass” into search; they type the service and the city together. Each of the eight pages answers one such query and has its own process, its own questions and its own title. The services overview page still exists, but as an overview, not as an entry point from search.
Do messages from the contact form actually arrive?
They do, and this was checked with a real message and a delivery confirmation from the server. Mail isn't sent through the language's built-in function but through an account on the domain, with authentication and an encrypted connection, so it passes all three sender identity checks. Without that, a message usually ends up in spam.
Why a cookie banner when nothing is sold through the site?
Because the site measures visits, and measurement relies on cookies. The banner also offers a way to decline, and measurement is off by default until the visitor consents. The order matters as much as the banner itself: the refusal is set before anything from Google loads.
How does the stock get from the accounting software into the panel?
Through a spreadsheet export. The accounting software has no publicly documented way to exchange data automatically, so the bridge is a file: an export from the software, then an import into the panel with column mapping and a preview before anything is written. Before every write, the whole inventory is backed up, so a bad import can be undone.
Can a site like this be expanded later?
Yes, and it already has been: four service pages were added after the first version, and the inventory panel years later. That is exactly why the URLs have no extension: when all the pages were rewritten in PHP in September 2026, the URLs stayed the same.
Related projects
From the same field.

Glass Service Bošnjace
Tehnički Pregled Stanković 964
Auto Delovi RM