Case study · A holiday rental website and its maintenance

The website took ten days, and then the work began

A holiday rental website in Balatonfüred, Hungary, live since June 2025. Most of the time since then went into something other than building.

The situation
You run a holiday rental, and guests find you through booking portals. You have no website of your own, or you have one that never comes up when someone searches for a place to stay in the area.
The approach
A website of your own with photos of the rooms and the terrace, an email address on your own domain, and a search campaign to get started. Then steady care — without it, a website quietly goes stale in a few years.
The result
The site has been running since June 2025, with domain and hosting renewed yearly. In May 2026 the scrolling stuttered — I measured what caused it, and it has run smoothly since.

The work in numbers

Key facts about building and maintaining the website
First email to publication10 days (June 2025)
Photo galleries7, one per location
Maintenanceongoing since June 2025
Content updateseach season, with the events list
Performance fixMay 2026 — after measurement, in 9 modules
Search campaignran from summer to October 2025
Project
a holiday rental in Balatonfüred, Hungary
Role
Website, ads, hosting and domain, ongoing maintenance
Arrangement
Hourly work, yearly hosting and domain subscription
The four phases of the first year The first phase is the build, ten days in June 2025. The second is advertising, from summer to October 2025. The third is content updates, over winter and spring. The fourth is the performance fix in May 2026. The build is the shortest phase; the rest together span far longer. Build website, email built in 10 days Ads search campaign Jun–Oct 2025 Content events, prices winter–spring Measure and fix May 2026 the collaboration has run since June 2025 What stayed the same throughout hosting and domain, email forwarding, someone to ask Booking enquiry via the site's form or by phone
The build was the shortest phase. The site's value comes from what happens to it afterwards — and from the fact that it keeps working meanwhile.
  1. 01 Build website, email built in 10 days
  2. 02 Ads search campaign Jun–Oct 2025
  3. 03 Content events, prices winter–spring
  4. 04 Measure and fix May 2026
  5. 05 What stayed the same throughout hosting and domain, email forwarding, someone to ask
  6. 06 Booking enquiry via the site's form or by phone

the collaboration has run since June 2025

What comes after handover

The build was the predictable part: the material was there, the structure was there, and after a few emails the final layout was too. It took ten days, from the first email to the site being published for search engines.

After that, something kept coming up. The domain and hosting expire, the seasonal events list changes, browsers update, and now and then an email arrives that someone has to judge — serious or not. Since the launch this kind of work has added up to far more than the original build.

It does not mean daily attention. The owner writes when something is needed, and I look at it then — up to July 2026 that was a handful of occasions a year, spread fairly evenly.

A search campaign also ran for the launch, from the summer of 2025 to October. The first weeks were not about bookings but about filtering out the enquiries that had nothing to do with the property — there is more on that below, in the technical part.

When a bug refuses to show itself

In the spring of 2026 the page stuttered while scrolling, and here and there a light patch flashed where a photo should have been. The photos here show the rooms and the terrace — the very thing a visitor came for.

The problem never appeared in an automated browser, only during real use. After three attempts that did not solve it, I changed direction: I wrote a small measuring script that records, in the real browser, what happens during scrolling. Instead of guessing there was now data, and the data pointed to four separate causes — no single one of them would have explained the problem alone.

What this means if your situation is similar

If you run a holiday rental and bookings come through portals, a website of your own is not about replacing them. It is about having one place that you own, where there is no commission, and where there are as many photos and as much description as you see fit.

And you do not have to write the content from scratch. Most of the text already exists, just elsewhere: for this property I collected the amenities, the nearby sights and the services from the existing portal listings, and we started from there. What the owner actually had to produce was the photography.

If you have a holiday rental or a small business and no website of your own — or you have one but no idea who would touch it if something broke — write a few sentences about where things stand. One exchange of emails usually shows how much work it would be.

kristof@kristofkarner.com
Technical details

From here on come the details: how the site was built, what the scroll measurement showed, and the traps I ran into along the way. If you only wanted to know what maintaining a website involves, everything is above — this part shows the reasoning behind the decisions.

Size of the implementation
Performance fixMay 2026 — 9 standalone modules
Measuring script298 lines, active only with ?debug=1

The website

One page, seven galleries

The property sits on a single scrolling page, with galleries by location: bedrooms, living room, two bathrooms, kitchen, terrace and cellar. The owner asked for this structure — she wanted the simple layout of another rental site she knew, with few menu items.

The price of the gallery structure is that the front page carries many images at once. Most of the later performance problems trace back to this.

An email address on the domain

Alongside the website, an email address was set up on the domain, forwarded to the owner's existing mailbox. A guest writes to the property's address, and the message lands where the owner already reads mail — no new account to manage. On the evening before handover, two test messages arrived through the form: “próba” — Hungarian for “test” — and then “Mobilpróba”. Both landed within seconds.

The five text fields of every image

For every uploaded photo I filled in all five of WordPress's text fields: the file name, the alt text, the title, the caption and the description. This is partly an accessibility matter, and partly how search engines learn what a picture shows — and for a holiday rental, image search is a real source of traffic.

The search campaign that ran for the launch

The campaign ran from the summer of 2025 to October, and brought 10,228 impressions and 514 visits at an average of HUF 157 per click. In the first weeks, though, phone calls also came in that had nothing to do with the property: people looking for long-term housing, calls in the middle of the night, callers who had not checked which town the place was in. The system starts out with a broad idea of when to show the ad.

The narrowing was done with negative keywords, based on the actual searches. It turned out, for instance, that many people added “forest”-type words to their query, and those searches were not worth appearing for. This work cannot be done in advance, because there is no knowing beforehand what people will type. The campaign stopped in October; the site has run without ads since.

What the scroll measurement showed

Three guesses first, then measurement

My first three fixes were based on assumptions, and each was reasonable. I turned off lazy loading for the images, because that looked like the most obvious explanation. I turned off a page-transition effect that sometimes got stuck above the content. I capped the image sizes. The stutter shrank, but it did not go away.

At that point measuring beat more guessing. The measuring script only switches on when I add a flag to the page address, so visitors are not affected. While scrolling, it records frame timing, tasks blocking the main thread for more than fifty milliseconds, layout shifts, image loading events and GPU details. When a frame is noticeably slow, it also stores what is at the centre of the screen — so there is no guessing afterwards about which element caused it.

  1. Measurement starts with a ?debug=1 flag in the page address — visitors never meet it.
  2. From the first scroll it records frame timing, tasks blocking longer than 50 ms, layout shifts and image events.
  3. On a noticeably slow frame it also stores what sits at the centre of the screen.
  4. After ten seconds of scroll silence it saves the recording to the server on its own.
Overriding the sizes attribute — the core of the fixPHP
// WP core hook — runs whenever image attributes are generated.
add_filter(
    'wp_calculate_image_sizes',
    static fn() => '(max-width: 768px) 50vw, 25vw',
    PHP_INT_MAX
);

// Elementor and attachment image attrs may carry sizes too — override there as well.
add_filter(
    'wp_get_attachment_image_attributes',
    static function ( $attr ) {
        if ( isset( $attr['sizes'] ) ) {
            $attr['sizes'] = '(max-width: 768px) 50vw, 25vw';
        }
        return $attr;
    },
    PHP_INT_MAX
);

Four separate causes

The measurement showed this was not one bug but four, and they reinforced each other. That is why none of the earlier fixes brought a breakthrough on its own — each one solved a quarter of the problem.

The first was lazy loading: the twenty-six images on the front page only started downloading when the visitor got near them, and until then their slots showed the body background — on another site this same switch went the other way. The second was the sizes attribute, which made the browser download the largest image variant even for the smallest slot. The third was Chrome's deferred texture upload to the GPU, which caused a short freeze the first time each image appeared. The fourth was a translucent blur effect that returned corrupted pixels in that particular graphics stack.

Measured on the local copy — the gallery image variants (uploads/)
2048 px wide variant:   9 files · average 546 KB
 768 px wide variant:  30 files · average  86 KB

Nine separate modules, not one big fix

The fixes went into nine standalone modules, each one switchable on its own. Partly this was convenience during the work, because it made each fix measurable separately. Mostly, though, it is because these fixes track browser behaviour, and browsers change. If a bug disappears in a later Chrome release, the matching fix can be removed by deleting a single file, without touching the rest.

From guessing to measuring The first three fixes were based on assumptions: disabling lazy loading, removing the page transition, capping image sizes. They reduced the stutter but did not end it. Then came measurement in the real browser, which identified four causes: lazy loading, the sizes attribute, deferred GPU texture upload, and the translucent blur. Targeted fixes followed. Measurement in the real browser frames, tasks, images Lazy loading off better, not gone Transition off better, not gone Image size cap better, not gone Lazy loading The sizes attribute Deferred GPU upload Translucent blur Nine standalone modules each switchable on its own browsers change, a fix can be removed assumption · four identified causes · two of the three early fixes stayed in after the measurement
The three early attempts were not wrong, just not enough. The measurement did not refute them — it showed what was missing next to them.
  • Lazy loading off better, not gone
  • Transition off better, not gone
  • Image size cap better, not gone
  • Measurement in the real browser frames, tasks, images
  • Lazy loading
  • The sizes attribute
  • Deferred GPU upload
  • Translucent blur
  • Nine standalone modules each switchable on its own browsers change, a fix can be removed

assumption · four identified causes · two of the three early fixes stayed in after the measurement

The parts that are not obvious

The next five things are not in the documentation. They come up in use, and each one is fairly easy to walk into.

The browser downloaded the largest image for the smallest slot

WordPress generates several sizes for every uploaded image and lets the browser choose. To choose, though, the browser has to know how much space the image will occupy, and an attribute called sizes tells it. The default value is calculated from the image's own dimensions, not from where it goes — so the browser believed every gallery tile was screen-wide.

Accordingly it downloaded the variants around two thousand pixels wide, at roughly half a megabyte each, into slots where the 768-pixel variant would have done at a tenth of the weight. The fix was overriding one attribute in three places: in WordPress's own calculation, on the gallery elements, and finally in the rendered HTML as well, so that nothing could slip through.

The translucent blur triggered a graphics bug

Seven places on the page had a layer that blurred whatever sat behind it, with a transparent background. In Chrome's graphics stack on Apple-silicon machines this misbehaved: while image textures were mid-upload, the blur sampled undefined pixels at the edges, and the body background bled through.

This had been present for months in every Chrome-based browser, but not in Safari — that one takes a different graphics path. The fix was switching the blur off in favour of a similar-looking solid translucent background colour. Visually close, and it does not depend on what the browser is doing with its textures.

The test email you send yourself never arrives

When I set up the forwarding from the domain address to the owner's mailbox, the obvious check would have been for the owner to write to her own new address. That does not work: forwarding systems typically do not deliver a message that would arrive back where it was sent from.

So the test shows a failure while the setup is fine. The check has to come from a different mailbox — I sent one from mine as well, to have something to compare against.

The font did not refresh because the cache key never changed

On the local copy, the original typeface refused to appear for a long time, even though the configuration was right. The reason: the version number attached to the font stylesheet's address was baked in at generation time. When the file's contents changed, the address stayed the same, and the browser kept serving the old, broken stylesheet from cache.

The fix is to derive the version number from the file's last-modified time. A content change then always means a new address, and the browser has to fetch it again.

The verification code that actually asked for access

In the summer of 2026 a company presenting itself as a booking platform contacted the owner. The reasoning sounded plausible: they only list real, working properties, so they verify ownership, and for that a code needs to be placed into the website source.

The code was not an identifier, though, but an executable program that would have given access to sensitive parts of the site. The genuine verifications I know work differently: they ask for a file upload, or a meta tag in the page head — something that does nothing, just sits there, checkable from outside. I find these approaches have grown more carefully crafted lately.

Questions about this project

Why does a website flicker or stutter while scrolling?

There is rarely a single cause, and that is what makes it hard to find. On this site four separate things added up: lazy-loaded images, which left the body background showing where a photo had not arrived yet; a wrong sizes attribute, which made the browser download the largest image variant for the smallest slot; Chrome's deferred texture upload to the GPU, which froze the page for a few tenths of a second the first time each image appeared; and a translucent blur effect that misbehaved in Chrome's graphics layer. Each cause needed its own fix, and no single fix made the problem go away on its own.

What does a web developer do after the website is finished?

At this holiday rental the build took ten days, in June 2025, and the maintenance has run ever since. Up to July 2026 the domain and hosting were renewed, the seasonal events content was updated, a search campaign ran and ended, the site's scroll performance was fixed, and one enquiry from a supposed booking platform had to be checked out. That last one turned out to be a scam. These were small jobs one by one, not showcase features.

Why is testing a bug in an automated browser not enough?

Because a browser driven by developer tooling runs differently from the one a visitor uses. The scroll stutter here was exactly that kind of bug: it never appeared in automated runs, only in a real Chrome. So a small measuring script went onto the site, recording frame timing, main-thread blocking tasks, layout shifts and image loading events in the real browser. That data showed what was causing the stutter — guessing had gone through three attempts without solving it.

Why does the browser download huge images for tiny thumbnails?

Because of the sizes attribute, which WordPress calculates by default from the image's own dimensions, not from the space it will occupy. If the browser believes the image will span the full screen width, it picks the largest variant on offer — even when the image is actually a small tile in a four-column gallery. On this site the variants around two thousand pixels wide weighed roughly half a megabyte each, while the 768-pixel variants that would have been enough weighed a tenth of that. The fix was overriding a single attribute.

How do you check that an email address set up on your own domain actually works?

With a message sent from a different mailbox, not from the one the forwarding points to. Forwarding systems typically do not deliver a message that would arrive back where it was sent from — so the test you send yourself never shows up, and it is easy to conclude the setup is broken. In that case the setup is fine; the test just cannot prove it.

Does Google search advertising bring bookings for a holiday rental?

It brings traffic, but the first weeks are about filtering, not bookings. At this holiday rental the campaign started in the summer of 2025 and brought 10,228 impressions and 514 visits by October, at an average cost of HUF 157 per click. In the first weeks, though, many phone calls had nothing to do with the property: people looking for long-term housing, calls in the middle of the night, callers who had not checked which town the place was in. Negative keywords narrowed this down — and that work cannot be done up front, only once you can see what people actually search for.

What should you do when an unknown platform asks you to put code into your website?

Nothing, until someone who understands the code has looked at it. In the summer of 2026 an enquiry arrived at this holiday rental from a company presenting itself as a booking platform, asking for a code snippet to be placed into the website source as owner verification. The code would in fact have been an executable program giving access to sensitive parts of the site. The genuine verifications I know work differently: they ask for a file upload or a meta tag in the page head, something that does nothing and just sits there, checkable from outside.

Summary

A holiday rental website in Balatonfüred, Hungary, was built in ten days in June 2025: a one-page layout with seven galleries by location, an email address on the property's own domain, and text content collected from the existing portal listings. A search campaign also ran for the launch, from summer to October 2025, where the work of the first weeks was filtering out irrelevant enquiries with negative keywords. In May 2026 a scroll stutter was diagnosed with custom telemetry, because the bug never appeared in an automated browser; the measurement found four causes — lazy loading, a wrong sizes attribute, deferred GPU texture upload, a translucent blur — and the fixes went into nine standalone, individually switchable modules. Maintenance continues on hourly work with a yearly hosting and domain subscription.

Karner Kristóf

Independent developer in Budapest. I built this site and I maintain it — questions are welcome at the address in the contact section.

← All case studies