Website2026-08-21
AutoSet Niš — site for auto glass with four pages for local search
A driver whose windshield has been chipped by a stone does not search for long. He types "windshield replacement Niš", opens a website that loads quickly and calls the number he sees on the screen. This site was created exactly around that scene — from how the pages are divided to how long it takes to load on a mobile connection.
- Client
- AutoSet Niš
- Activity
- Auto glass - sales, installation, replacement, repair
- Area
- Niche i environment
- Type
- A multi-site with four pages for local search
- Technologies
- HTML · PHP 8.3 · nginx · PHPMailer · Consent Mode v2
- Site
- autoset.rs
- Status
- Live site, under active maintenance
- Technology
- HTML + PHP 8.3 · nginx · PHPMailer preko SMTP-a · Consent Mode v2 · MariaDB (panel)
Problem and context
What does AutoSet do
AutoSet is a service and auto glass shop in Niš. It has operated since 2005, as it says on the company page. The sales premises and workshop are at Matejevački put 24 in Niš, the headquarters is in Svrljig, and the business has a clear focus: windshields, side and rear windows, glass for vans, trucks and buses — sales, installation, replacement and repair.
It is an activity in which the customer almost never plans a visit. A stone from the highway hits the windshield on Wednesday morning, or the vehicle is found with a broken window and without a side window. The customer reaches for a phone, types in the service and the city, opens the first website that loads, and dials the number. The gap between discovering the problem and making a call can be under a minute, so the entire site is optimized for that brief decision window.
The full overview of the offer is on the page with services, and the four most requested services each got their own page. On the website, the company mentions a guarantee of up to two years on glass and installation, cooperation with insurance companies regarding documentation and free delivery in Niš and the surrounding area. These are their terms and that's how they were conveyed — the site presents them, not inventing them.
What needed to be solved
The task was not "make a company brochure". The site had to do four specific things:
- Visibility in local search. Not by the word autostakla, where the whole of Serbia competes, but by queries that include both the service and the city.
- Keep the phone within reach. Someone with a crack across the windshield doesn't fill out the form — he calls. The number must be visible on every screen and clickable on the phone.
- Reliable contact-form delivery. A message that disappears in transit is lost business, and an unprotected form turns into a trash can in a month.
- That it opens quickly on a mobile connection. Most of the visits come on a phone, often from the road and with whatever connection is available.
The fifth item arrived much later and has nothing to do with the public site: the glass inventory of over seven hundred items lived in the accounting program, and in the store it was supposed to be on the screen, searchable, while the customer is standing at the counter. There is a separate section about that at the bottom.
Structure — ten pages and clean URLs
The site has ten pages. There are four wearables: cover, services, about us and contact. There are four separate pages for individual services. Two are legal — privacy policy and terms of use. There is no blog, no news, no section that is filled out once and forever stands with last year's date.
Instead of a title, the cover has one sentence: Every day thousands of people look at the world through the glasses installed by AutoSet. Below it are four short labels — years of experience, certificate, guarantee, arrival — then a block Why AutoSet? with six cards, then recommendations, then a contact form and all four phone lines. The menu has four items and stays there; a ten-page site doesn't need drop-down menus.
Technically, pages are regular HTML files, but the URLs do not have file extensions — /services, not /usluge.html. That's what the server does: try_files searches for the correct file, then the same one with the extension .html, then the PHP version. The homepage is the only PHP page, as it pulls in the common cookie banner. The benefit is practical — if one page is rewritten in PHP tomorrow, its address remains the same and no link in the search is broken.
Four pages for four searches
This is the part that was most valuable to AutoSet. One general Services page cannot be the most relevant result for every distinct local query. That's why four separate pages were created, each for one query that a person actually types:
- Windshield replacement in Niš — a four-step process from quote to final seal inspection, a list of brands that are kept in stock, and the most frequently asked questions: how long does it take, how long to wait before driving, how an insurance claim works.
- Windshield repair in Niš — two lists next to each other, what can be repaired and what requires replacement, then five steps of the procedure with resin and a UV lamp, then a table that compares repair and replacement by price, by time and by when the vehicle is ready to drive.
- Car glass Niš — offer by type of glass and by vehicle brand, with the difference between original and replacement glass explained in words the customer understands.
- Side windows Niš — front and rear side windows, fixed quarter glass, pillar glass and glass for sliding van doors.
Each of them carries its own questions and answers in a structured form, so part of the content can be seen in the search results even before the click. Two pages attract the most traffic: windshield replacement, because it is the most expensive and most urgent operation, and windshield repair, because most people don't even know that a small chip can be repaired instead of replacing the entire windshield. The comparison table on that page isn't marketing — it actually answers the question that brought the visitor to the page.
All four service pages and the legal pages are in the sitemap, and the domain is verified in Google Search Console as a domain property via DNS records, which is the strongest verification there is. The site was also added to Bing Webmaster Tools with the same sitemap.
Phone, form and that the message actually arrives
AutoSet has four phone lines — one fixed and three mobile. All four appear in the footer of each page and in the contact block, and are all clickable on the phone. The number inside the link must not have spaces, otherwise the call will not start on some phones — a little thing that is only discovered when someone tries to call from the site.
The form is on the homepage and on the contact page: name, email, phone, drop-down selection of service and message, with consent to the terms and privacy policy. Behind it is a single PHP file that does three separate checks before it even tries to send mail.
- Honeypot. There are three hidden fields in the form that the real visitor never sees or fills out. If any is filled, the message is discarded.
- Link filter. If the message body contains a URL, HTML link tag or punycode domain, the message is silently rejected and the sender receives a normal acknowledgment. A bot that doesn't get an error doesn't try again differently.
- IP rate limit. Maximum three messages per hour from the same IP address.
The rate limit also counts incorrect attempts, not only sent messages. This is intentional — a form-targeting bot rarely hits the correct form, so counting only successful submissions would be pointless. The price is that the person who gets the email wrong three times has to wait, and that is a known concession, not an oversight.
The mail does not go through the built-in mail() function, but through PHPMailer and uses an authenticated sending account, through an encrypted connection on port 587. The reason is simple: the message that the web server sends out has neither a signature nor a history and ends up in junk mail, while a message sent through an account on the domain goes through SPF, DKIM and DMARC checks and arrives in the mailbox. The last check with the right message confirmed delivery, confirmed by the SMTP server’s 250 response.
Cookies, consent and measurement
The cookie banner is not pasted across the site but comes from a common inclusion shared by several of our sites, so it is kept in one place. For AutoSet it was recolored in their palette — light blue with highlights, same font as the rest of the site.
The order is important, not the appearance. Google Consent Mode v2 sets the default response to denied before anything Google is loaded. The measurement starts only after clicking on acceptance. The banner also offers Only necessary, the choice is remembered and respected on the next visit, and the analytics are not triggered until there is consent.
One detail from there shows how the little things go into the code. Banner recoloring was originally a style written directly into the body of the page — it works in the browser, but the W3C check throws an error on it. Extracted to a separate file that doesn't block the first view: same color, one less bug.
Performance - meter that doesn't block the first view
On this site, the speed ceiling was neither image nor code, but Google Tag Manager. About sixty kilobytes of script that the visitor does not use, which is executed while he waits to see the text. In the test over a slow mobile connection, it was seen as a time in which the page does not respond to touch.
The solution is delayed loading: the measurement script is pulled only after the visitor's first action — a scroll, click, tap, or key press — or after five seconds, whichever comes first. The implied refusal of consent remains before all of that, so the sequence of consent is not disturbed.
Measured before and after, on mobile Lighthouse profile: performance score from 74 to 86, total block time from 368 to 54 milliseconds, maximum content view from 4.7 to 4.1 seconds, layout displacement remained at zero. A check via Google's live tool gave mobile 76 and desktop 95, with 100s for accessibility, good practice and SEO on both.
It is fair to say what is not visible in those numbers: the site does not have data about real visitors in Google's measurement database, because it does not have enough traffic volume. Ratings are simulated — a valid indicator, but not a measurement of thousands of real visits. Images and fonts are in the annual cache, styles and scripts in the weekly.
Code, security and mail
A full pass through the W3C check on all ten pages started with twenty-seven errors and ended with zero. They weren't dramatic—spaces inside a phone line, a fourth-level title without a third above it, a wrongly nested block, the mentioned style in the body of the page. None of them crash the site. All together are the difference between code that someone reviewed and code that someone just let run.
At the server level: switching to an encrypted connection is a permanent redirect, the address from www leads to the primary domain while preserving the path, the certificate renews itself. The headers that tell the browser what it can and can't do — HSTS, no nesting, no content-type guessing, window isolation — are set and checked. Hidden files and backups are not served. The external verification rating of the encrypted connection is the highest.
There is a restriction in the domain's DNS records that explicitly tells certificate issuers who can issue a certificate for this domain, with the address to report the attempt. Mail has SPF, DKIM and DMARC, and the policy has been moved from quarantine to bounce — a message impersonating AutoSet doesn't end up in spam, it's bounced inbound. That's not a small thing for a company that sends offers and receives inquiries from its domain.
A fault that can only be seen on the phone
In July 2026, the site passed a real-browser review — all eleven URLs, on the desktop width and on the phone. The console was clean, no dead links, no broken images, redirects and the certificate worked correctly. But on the four pages with services, in the width range between 384 and 797 pixels, the page had a horizontal overflow and the sidebar with price list, benefits and emergency was completely beyond the viewport. On the most important pages of the site, on the widths used by many visitors.
The cause was an inherited flexbox rule. The sidebar moved from row to column on narrower screens, but the wrapping rule from a wider range was not overridden. In a columnar layout, the flex-basis applies along the column axis, not width, so each block got its own horizontal column and went outside the edge of the screen.
The fix was two lines: no wrapping and a content-based flex size. Additionally, on the side-window page, desktop grid stretching was also resolved, where a five-step sequence pushed the column beyond its width.
The lesson is more general than this site: moving from row to column within a media query doesn't override the desktop wrapping rule — it must be explicitly turned off. And more importantly, this kind of failure does not exist in any automatic assessment. Lighthouse on that page still gives a high rating. It's only visible when someone actually opens the site on their phone and tries to find the price list.
Inventory panel — a separate system
A public site is one thing. In August 2026, a separate internal system was added: an internal panel on its own subdomain, behind the login, which contains a glass inventory of 713 items and an appointment calendar. It's a tool for the shop counter, not for the visitor.
The company's bookkeeping works in a business program that is used through a browser, and the supplier controls the database. Before anything was written, it was checked whether the two systems could be connected directly. The answer was no: there is no publicly documented integration API, and the seventy pages of their help do not mention anything like that. What there is is an inventory export as a spreadsheet. So, the bridge is a file.
That's why the panel received a table import in three steps: uploading, column mapping with automatic recognition, then a preview that tells you how many items are new and how many changed before entering. Before each import, a copy of the entire inventory is made, so that an incorrect import can be undone.
The difficulty was in the data, not the code:
- The code is not unique. The same part code can occur twice, under two different manufacturers. The merge key must be the part code together with the manufacturer — otherwise the import merges two different items into one.
- When the file does not have a manufacturer column, the manufacturer must be dropped from the key — and on both sides, both in both the import file and the database. If it drops from just one, every item looks new and one import duplicates the entire inventory.
- 94 out of 713 items do not have a name, only a code. The first version of the import silently skipped them, because the name was set as mandatory. Now a row is usable if it has a name or code.
On the way, the control also found a bug that silently swallowed the data: the table displayed the prices rounded, and the editing window read those rounded numbers back from the table cells and wrote them into the database. Editing any field of a single item permanently lost decimals — 1234.75 became 1235. The fix was to have the original values line up, and the edit read them, not what was drawn on the screen.
The panel was then tested with a real mouse click, not a programmatic one, and that difference revealed two things. First: the footer of the import window could not be scrolled down — on a maximized 1366px laptop, the Next button was 189px below the edge of the screen, and nothing was scrolling. The import was unusable on the most common laptop in the shop, and the click program call passes anyway because it bypasses the visibility check; an automatic test would say everything is working. Second: The Synchronized hover tag was clicking clicks — visually innocuous, but it was receiving clicks, so the Edit and Delete buttons below it were unresponsive, no error, no trace. The space below the table doesn't help here; the only remedy is for the tag to stop receiving clicks.
The safety inspection found the most serious finding in the import itself. XLSX is actually an archive, so a file of 1.84 megabytes can be unpacked into 529 megabytes in the memory of the server worker, and with it other sites from the same set. The limit on the number of rows does not help, because it is only applied to the already built tree — the check is now done before the read, on the reported size and on the compression ratio. All of this is covered by the tests that the panel carries with it: 129 checks on data and 43 over the network, including a deliberately created archive-bomb.
Outcome
What the client received
A ten-page site, four of which focus exactly on the search that a man with a windshield crack is doing. Pages indexed, with breadcrumbs and with questions that appear in the results. A form that arrives in a box, with three layers of protection in front of it. Four phone lines that can be dialed with one touch.
The public site deliberately does not have a content editing system and that is a decision, not an omission. Ten static pages have nothing to break, no plugins to break on the next update, and no login for outsiders to guess. Content changes rarely — phone, hours, service description — and those changes go through us, backed up before each one and with the code sitting in a private repository.
The tool that the client uses every day is another: a panel with stock and a calendar, with import from bookkeeping when a new export arrives. Seven hundred items are searched in a second, the price is shown accurate to the dinar, and a new delivery is entered through one table instead of through seven hundred manual entries.
The division is intentional. The site works for someone who doesn't know AutoSet yet. The panel works for someone who works at AutoSet. These are two different jobs and should not have been the same app.
FAQ
Frequently asked questions
Why does the public site not have a content editing system?
Because it would cost more than it would be worth. The content of such a site changes several times a year — telephone number, working hours, service description. Static pages don't have plugins that break when updated or a login that someone can guess. Changes go through us, with a backup before each one.
Why four separate pages instead of one services page?
Because the page that talks about everything doesn't rank for anything. A person does not just type the word autoglass in the search, but the service and the city together. Each of the four pages responds to one such query and has its own procedure, its own questions and its own title. The services summary page still exists, but as a preview, not as a search input.
Does the message from the contact form really arrive?
It does, and it is verified by a real message with confirmation of receipt from the server. The mail does not go through the built-in <code>mail()</code> function, but through an account on the domain, with authentication and an encrypted connection, so it passes all three checks of the identity of the sender. Without it, the message most often ends up in spam.
Why a banner for cookies when nothing is sold through the site?
Because the site measures visits, and the measurement relies on cookies. The banner also offers an opt-out, and measurement is turned off by default until the visitor agrees. The order is as important as the banner itself: the bounce is set before anything Google is loaded.
How does the inventory from the ledger get to the panel?
Through export to a table. The accounting program does not have a publicly documented way of machine exchange, so the bridge is a file: export from it, import into a panel with column mapping and review before entry. A copy of the entire inventory is made before each entry, so an incorrect import can be undone.
Can a site like this be expanded later?
It can, and already is — the four service pages were added after the first version, and the inventory panel years later. The URLs omit file extensions precisely because: tomorrow the page can be rewritten in PHP and its address and its place in the search will remain the same.
Next step
Have a project that needs to work better?
Send me your goal and current situation. I will reply with a concrete scope proposal.
Start a project