Bug report template for websites: what to ask a visitor, and what to capture instead
A bug report template you can copy, what each line is for, why visitors on a live site leave most of it out, and which parts a feedback widget can fill in by itself.
By Sylvain
A visitor writes "the checkout doesn't work". To fix it you need to know which page, which browser, what they clicked, what they expected, and what the page did instead. The visitor has given you one of those, and probably not the one you needed most.
Most bug report templates are written for testers. A tester on a staging site has time, the habit, and developer tools already open. This article is about the other case: a stranger on your live site, who noticed something broken and was kind enough to say so. I'm a UX designer and I make a feedback widget, Two Cents, so I have a side in this. I say below what it fills in and what it can't.
A bug report template you can copy
If you collect bug reports through a form, or write them up for a developer yourself, this is enough:
Page:
Browser:
Steps: 1.
2.
3.
Expected:
Actual:
Screenshot:
Console:
Failed:
Filled in, it looks like this:
Page: https://example.com/checkout
Browser: Safari 18 on iOS 18, iPhone
Steps: 1. Added a jacket in size M to the cart
2. Went to checkout, filled in the address
3. Pressed "Pay"
Expected: The payment page
Actual: The button spun, then nothing. No message.
Screenshot: attached
Console: TypeError: undefined is not an object (evaluating 'cart.total')
Failed: POST /api/checkout 500 (412 ms)
Keep the lines in this order. Whoever reads the report checks the page and the browser first, to know whether they can reproduce it at all.
What each line is for
- The page address. A bug is almost always on one page, and often with one filter or one product selected. The full address carries both.
- The browser and the operating system, with versions. "It's broken in Safari on an iPhone" rules out half of the possible causes before you open anything.
- The steps that led there. What they clicked, typed or chose, in order, from a page that worked.
- What they expected to happen.
- What happened instead. An error message copied word for word is worth more than "it crashed".
- A screenshot of the page at that moment.
- The console errors and the requests that failed. This is the part the visitor never sees, and often the part that names the cause: a
TypeErrorin a script, or aPOST /api/checkoutthat answered 500.
Screen size, language and how long the page took to load help too. They don't get a line, because nobody would think to type them.
Why visitors leave most of it out
Now hand that template to someone who came to buy a jacket.
They don't know their browser's version, and there is no reason they should. They don't remember the exact steps, because they weren't planning to report anything until the moment it broke. They can't see the console: it is in developer tools most people have never opened, and asking them to open it is asking a favour on top of a favour. Failed requests are even further out of reach. So they write the one thing they know, what went wrong, in one sentence.
This gap has been measured. Thomas Zimmermann and five colleagues surveyed developers and reporters from Apache, Eclipse and Mozilla, 466 responses in all, for a paper in IEEE Transactions on Software Engineering, published in 2010. What developers found most useful in a report, such as the steps to reproduce, was what reporters found hardest to give. Those reporters were people who chose to file bugs in an open-source tracker. Your visitors are less technical than that, and less invested.
Asking for more doesn't fix it. Every extra field on a form loses some of the people who would have finished it, and I went through the evidence for that in Contact form or feedback widget. A seven-field bug form on a shop gets you fewer reports, not better ones. And the visitor who gives up on it doesn't come back to try a shorter one.
What a widget can fill in by itself
Most of the template isn't the visitor's to give. The page knows it already. A script running on the page can read the address, the browser, the screen, and the errors the page has thrown since it loaded, and attach them to the message. That leaves the visitor the part only they know.
Here is how the template splits in Two Cents.
| Line of the template | What arrives |
|---|---|
| Page ✓ | The full address the message was sent from, and the page the visitor came from before |
| Browser and system ✓ | Shown in the inbox in plain words ("Safari on iOS"), with the screen size, the language and the time zone |
| Steps, expected, actual | The visitor's own words. The bug form asks "What went wrong?", with "I clicked X and then…" as its example |
| Screenshot | One click for the visitor on a computer. On a phone the browser can't capture the page, so the visitor attaches a screenshot taken with the phone |
| Console ✓ | The last 25 errors and warnings, uncaught exceptions included |
| Failed requests ✓ | The last 15 that failed, with method, status, path and duration, and any picture, script or stylesheet that didn't load |
| Page load ✓ | How long the page took to load, and in Chrome and Edge the connection's speed |
✓Filled in by Two Cents, with nothing for the visitor to do.
The visitor writes one sentence and maybe takes a screenshot. The rest arrives with it. "The checkout doesn't work" comes in with the iPhone, the page, and the POST /api/checkout 500 behind it, so my first reply can be the fix rather than "which page do you mean?".
The console and request lists are sent with bug reports only. An idea or a question still carries the page and the browser, but not the logs.
What it doesn't capture
None of this is a recording of the visit. Here is what it misses.
- There is no session replay. The widget doesn't record clicks, scrolling or typing, so the steps are whatever the visitor wrote.
- It watches from the moment its own script runs. An error thrown before that is missed.
- For a failed request it keeps the address without the query string, and it never reads bodies or headers. You see that
POST /api/checkoutfailed, not what was posted. - A broken picture or script has no status code, because the browser doesn't give one. Fonts and videos that fail to load aren't covered.
- In a single-page app the load time is the first load only, and a report sent before the page finished loading has none.
- Safari and Firefox give no connection speed.
- Nothing goes to an issue tracker, and a visitor can't draw on the screenshot. A report lands in the inbox, and you move it on yourself.
It is also not a QA tool. If your testers review a staging site and pin comments to elements, a tool built for that will serve them better. I compared Two Cents with Feedback Fish and Ybug, and listed more in Best website feedback tools in 2026. What I built is for the people who use your live site and will never file a ticket.
If you write the report yourself
Sometimes you are the one who finds the bug, or you pass a visitor's message to a developer. Then use the template above, all of it, and add one line about how often it happens: every time, or once in ten tries. A bug that reproduces every time is half fixed.
When the report comes from a visitor, ask for nothing beyond what went wrong. If you need the steps, write back and ask while they still remember. One sentence from them and the context from the page beat seven fields nobody fills in.
Two Cents is free for one site and 25 messages a month, with no card. Console errors and failed requests come with every bug report on the free plan too.