ScreenshotRender
← Back to blog
Tutorials

How to Screenshot a Website in Go (Golang): 3 Ways

Robert Belt·9 min read
Updated On :
Orange minimalist illustration of Go code turning a URL into a screenshot

Go will happily run a web server, parse HTML, and fan out a thousand goroutines, but it cannot open a browser, and a screenshot needs a browser. By the end of this you'll know the three ways to screenshot a website in Go, which one survives production, and how to do it in a single HTTP call when you don't want Chromium anywhere near your server.

The three are chromedp (the most popular way to drive headless Chrome from Go), go-rod (a higher level driver that can fetch its own browser), and a single HTTP call to a screenshot API with no browser on your server at all. They run from most to least infrastructure to own, and each one fits a different job.

How do you take a screenshot of a website in Go?

The shortest path to a screenshot of a website in Go is one HTTP request to a screenshot API: send a URL, get back a hosted image, with no browser to install or drive yourself. With ScreenshotRender the whole capture is one line: https://screenshotrender.com/api/v1/screenshot?apiKey=YOUR_API_KEY&url=https://go.dev&fullPage=true. Fire that from Go's standard library and the JSON response carries a hosted image URL at data.screenshot.

If you'd rather keep the rendering on your own machine, the chromedp and go-rod routes below run a real Chrome locally, at the cost of installing and patching that browser yourself. The rest of this post shows all three, so you can pick by how much infrastructure you want to own.

Why does Go need a headless browser to screenshot a website?

Go needs a headless browser to screenshot a website because its standard library has no rendering engine and no browser binding, so nothing in Go can lay out a page and paint pixels on its own. Go can fetch the HTML over net/http, but turning that HTML into an image means running a real browser engine, and Go does not ship one.

That leaves two shapes of local solution and one remote one. You can pull in chromedp or go-rod, both of which drive a real Chrome over the Chrome DevTools Protocol, so the browser does the rendering and your Go code just sends commands. Or you can hand the whole job to a remote service over HTTP and read back an image URL. The DevTools Protocol is the common thread: chromedp and go-rod are both Go clients for it, and a screenshot API runs the same kind of browser on its own hardware.

How do you screenshot a website in Go with chromedp?

You screenshot a website in Go with chromedp by opening a browser context, navigating to the URL, and capturing the result into a byte buffer you write to disk. chromedp is the most widely used Go package for driving headless Chrome, and a full capture is a single chromedp.Run with two actions: navigate, then screenshot.

You open a context with ctx, cancel := chromedp.NewContext(context.Background()), then run chromedp.Run(ctx, chromedp.Navigate("https://go.dev"), chromedp.FullScreenshot(&buf, 90)) and write the bytes out with os.WriteFile("go.png", buf, 0644). For just the visible viewport instead of the whole page, swap chromedp.FullScreenshot for chromedp.CaptureScreenshot(&buf).

What just happened is that chromedp spoke straight to a Chrome you already have installed and asked it to render the page and hand back PNG bytes. It does not download a browser; it expects to find Chrome or Chromium on the box. That is the catch in production: every server and CI runner that runs this needs a Chrome binary, the right shared libraries, and the fonts the page expects, and you keep all of that patched as Chrome ships a new headless stable every few weeks.

How do you take a full page screenshot in Go with go-rod?

You take a full page screenshot in Go with go-rod by connecting to a browser, opening the page, and calling its full page screenshot helper in one chain. go-rod is a higher level driver than chromedp, and the capture reads almost like prose: page := rod.New().MustConnect().MustPage("https://go.dev") then page.MustWaitStable().MustScreenshotFullPage("go.png").

The reason teams reach for go-rod is the first run. chromedp expects a Chrome already on the machine; go-rod's launcher can download a matching Chromium the first time it runs, so a fresh server or container gets a working browser without a separate install step. For the visible viewport instead of the whole page, swap in page.MustScreenshot("go.png"), and MustWaitStable holds the capture until the page stops changing, which matters for JavaScript-heavy sites.

The cost is that go-rod still runs a real Chromium. Auto-download is convenient, but the browser is now a heavy dependency on disk, it wants memory for every concurrent page, and a downloaded Chromium drifts out of date the same way an installed one does. You have traded the install step for a download step, not removed the browser.

Stop shipping Chrome with your Go binary.

chromedp and go-rod both run a real Chromium on your server, with the RAM, the patching, and the bot fights that come with it. ScreenshotRender renders the page for you and returns a hosted image, with cookie banners and ads already stripped. 100 free screenshots a month, no credit card.

Try a render

How do you screenshot a website in Go with a screenshot API?

You screenshot a website in Go with a screenshot API by sending the URL to one HTTP endpoint and reading the image URL out of the JSON response, with no browser on your server. The full request is one copy-pasteable line: https://screenshotrender.com/api/v1/screenshot?apiKey=YOUR_API_KEY&url=https://go.dev&fullPage=true.

From Go you fire that with the standard library, no SDK to add: resp, err := http.Get(endpoint), decode the JSON, and read the hosted image URL from data.screenshot. net/http is all you need, which keeps the dependency list empty and the binary small.

The parameters are the whole surface. url is the page to capture, fullPage=true grabs the entire scrollable document instead of the default 1280 by 720 viewport, wait takes a millisecond delay for pages that finish rendering after the initial load, and timeout caps how long a slow page can take. The response also carries the page title, description, and favicon, which is handy if you are building a link preview card. There is more than one screenshot API and they differ on exactly these defaults, so I broke down how to choose in our guide to the best screenshot API.

The other thing you stop doing is cleanup. Cookie consent banners, ad overlays, and chat widgets are removed automatically before every capture, on every plan including the free one, so the image is the page rather than the page plus three popups. Repeat captures of the same URL and options are served from an edge cache, and you pay only for successful requests, so a failed render does not cost a credit.

When does Go website screenshot capture fail?

Go website screenshot capture fails in a few predictable ways, and the cause is rarely the Go code itself.

  • Bot protection. A vanilla headless Chrome, whether you drive it with chromedp or go-rod, gets served a challenge page on bot-protected sites, so the capture is the challenge rather than the site. ScreenshotRender ships Stealth Mode on the Hobby plan and above; our guide to screenshotting Cloudflare-protected sites covers what changes.
  • Lazy loading and JS timing. Content that loads after the first paint, like images that appear on scroll, is missing if you capture too early. go-rod's MustWaitStable, a chromedp wait action, or the API's wait parameter all let the page settle first.
  • Login walls. A URL-only API takes a URL, not a session cookie, so it cannot reach a page behind a sign-in. That is the one case where driving the browser yourself wins, because chromedp and go-rod can script the login before the shot.
  • Server and CI memory. Chromium is memory-hungry, and a small container or a CI runner will kill the process when several captures run at once. Moving the render off your host removes the whole class of problem, since nothing heavy runs on your server.

Most failures are timing or access, not the capture itself. Match the fix to the cause and the image comes back clean.

Common questions about taking screenshots in Go

How do I take a full page screenshot in Go?

Capture the whole scrollable document instead of the visible viewport. With chromedp you call chromedp.FullScreenshot instead of chromedp.CaptureScreenshot; with go-rod you call MustScreenshotFullPage instead of MustScreenshot; with a screenshot API you add fullPage=true to the request. All three stitch the entire page from top to bottom, while the default capture is just the 1280 by 720 viewport. The scroll-and-stitch mechanics are in our guide to screenshotting an entire webpage.

Is chromedp or go-rod better for screenshots?

Both drive a real Chrome over the Chrome DevTools Protocol, so the image quality is the same. chromedp is lower level and expects a Chrome already installed on the machine, which keeps it thin but pushes browser setup onto you. go-rod is higher level and its launcher can download a matching Chromium on first run, which is friendlier on a fresh box. Pick go-rod for fewer setup steps, chromedp for a client that sits close to the protocol.

Do I need to install Chrome to screenshot a website in Go?

For the local routes, yes. chromedp drives a Chrome or Chromium that is already on the machine, and go-rod downloads one on first run if it cannot find it. A screenshot API needs no browser on your host at all, since the rendering happens on the service, which is exactly why it works on tiny containers and shared infrastructure where you cannot install system packages.

How do I screenshot a JavaScript-heavy page in Go?

Use a real browser and wait for it to settle. chromedp and go-rod both run Chrome, so JavaScript executes and modern React, Vue, and Svelte pages render correctly; add a wait for a selector, go-rod's MustWaitStable, or a short delay so lazy-loaded images and late content land before the capture rather than after it. A screenshot API runs Chromium for you and takes a wait parameter for the same reason.

Can I take a screenshot in Go without a headless browser?

Not on your own machine. Go's standard library has no rendering engine, so something has to run a browser to paint the page. Either drive one locally with chromedp or go-rod, or call a screenshot API that renders remotely, in which case no browser runs on your server and your Go code only makes an HTTP request with net/http.

The honest takeaway: match the method to the machine. On a server you fully control with Chrome already installed, chromedp is the thinnest path to a screenshot. If you want one library to fetch its own browser, go-rod trades the install for a download. And to turn any public URL into a clean image without putting a browser on your Go host at all, the single HTTP call is the least to maintain.

Keep reading